From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- sc/source/ui/Accessibility/AccessibilityHints.cxx | 62 + sc/source/ui/Accessibility/AccessibleCell.cxx | 614 ++ sc/source/ui/Accessibility/AccessibleCellBase.cxx | 590 ++ .../ui/Accessibility/AccessibleContextBase.cxx | 507 ++ .../ui/Accessibility/AccessibleCsvControl.cxx | 1460 +++ sc/source/ui/Accessibility/AccessibleDocument.cxx | 2224 +++++ .../ui/Accessibility/AccessibleDocumentBase.cxx | 36 + .../AccessibleDocumentPagePreview.cxx | 1567 ++++ .../ui/Accessibility/AccessibleEditObject.cxx | 604 ++ .../ui/Accessibility/AccessiblePageHeader.cxx | 371 + .../ui/Accessibility/AccessiblePageHeaderArea.cxx | 279 + .../ui/Accessibility/AccessiblePreviewCell.cxx | 277 + .../Accessibility/AccessiblePreviewHeaderCell.cxx | 404 + .../ui/Accessibility/AccessiblePreviewTable.cxx | 641 ++ .../ui/Accessibility/AccessibleSpreadsheet.cxx | 1667 ++++ sc/source/ui/Accessibility/AccessibleTableBase.cxx | 470 + sc/source/ui/Accessibility/AccessibleText.cxx | 1385 +++ .../ui/Accessibility/DrawModelBroadcaster.cxx | 107 + .../StatisticsDialogs/AnalysisOfVarianceDialog.cxx | 559 ++ .../ui/StatisticsDialogs/ChiSquareTestDialog.cxx | 91 + .../ui/StatisticsDialogs/CorrelationDialog.cxx | 39 + .../ui/StatisticsDialogs/CovarianceDialog.cxx | 44 + .../DescriptiveStatisticsDialog.cxx | 141 + .../ExponentialSmoothingDialog.cxx | 120 + sc/source/ui/StatisticsDialogs/FTestDialog.cxx | 171 + .../ui/StatisticsDialogs/FourierAnalysisDialog.cxx | 231 + .../MatrixComparisonGenerator.cxx | 113 + .../ui/StatisticsDialogs/MovingAverageDialog.cxx | 116 + .../RandomNumberGeneratorDialog.cxx | 482 + .../ui/StatisticsDialogs/RegressionDialog.cxx | 696 ++ sc/source/ui/StatisticsDialogs/SamplingDialog.cxx | 563 ++ .../StatisticsInputOutputDialog.cxx | 305 + .../StatisticsTwoVariableDialog.cxx | 347 + sc/source/ui/StatisticsDialogs/TTestDialog.cxx | 183 + .../TableFillingAndNavigationTools.cxx | 386 + sc/source/ui/StatisticsDialogs/ZTestDialog.cxx | 167 + sc/source/ui/app/client.cxx | 232 + sc/source/ui/app/drwtrans.cxx | 738 ++ sc/source/ui/app/inputhdl.cxx | 4635 ++++++++++ sc/source/ui/app/inputwin.cxx | 2703 ++++++ sc/source/ui/app/lnktrans.cxx | 76 + sc/source/ui/app/msgpool.cxx | 93 + sc/source/ui/app/rfindlst.cxx | 90 + sc/source/ui/app/scdll.cxx | 257 + sc/source/ui/app/scmod.cxx | 2325 +++++ sc/source/ui/app/seltrans.cxx | 429 + sc/source/ui/app/transobj.cxx | 921 ++ sc/source/ui/app/typemap.cxx | 140 + sc/source/ui/app/uiitems.cxx | 438 + sc/source/ui/attrdlg/attrdlg.cxx | 88 + sc/source/ui/attrdlg/scabstdlg.cxx | 55 + sc/source/ui/attrdlg/scdlgfact.cxx | 1370 +++ sc/source/ui/attrdlg/scdlgfact.hxx | 806 ++ sc/source/ui/attrdlg/scuiexp.cxx | 33 + sc/source/ui/attrdlg/tabpages.cxx | 220 + sc/source/ui/cctrl/cbnumberformat.cxx | 88 + sc/source/ui/cctrl/cbuttonw.cxx | 139 + sc/source/ui/cctrl/checklistmenu.cxx | 1737 ++++ sc/source/ui/cctrl/dpcontrol.cxx | 220 + sc/source/ui/cctrl/editfield.cxx | 63 + sc/source/ui/cctrl/tbzoomsliderctrl.cxx | 457 + sc/source/ui/condformat/colorformat.cxx | 303 + sc/source/ui/condformat/condformatdlg.cxx | 709 ++ sc/source/ui/condformat/condformatdlgentry.cxx | 1530 ++++ sc/source/ui/condformat/condformatdlgitem.cxx | 66 + sc/source/ui/condformat/condformathelper.cxx | 229 + sc/source/ui/condformat/condformatmgr.cxx | 180 + sc/source/ui/dataprovider/csvdataprovider.cxx | 176 + sc/source/ui/dataprovider/dataprovider.cxx | 313 + sc/source/ui/dataprovider/datatransformation.cxx | 1275 +++ sc/source/ui/dataprovider/htmldataprovider.cxx | 280 + sc/source/ui/dataprovider/htmldataprovider.hxx | 38 + sc/source/ui/dataprovider/sqldataprovider.cxx | 169 + sc/source/ui/dataprovider/sqldataprovider.hxx | 38 + sc/source/ui/dataprovider/xmldataprovider.cxx | 126 + sc/source/ui/dataprovider/xmldataprovider.hxx | 37 + sc/source/ui/dbgui/PivotLayoutDialog.cxx | 719 ++ sc/source/ui/dbgui/PivotLayoutTreeList.cxx | 132 + sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx | 122 + sc/source/ui/dbgui/PivotLayoutTreeListData.cxx | 287 + sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx | 85 + sc/source/ui/dbgui/asciiopt.cxx | 296 + sc/source/ui/dbgui/consdlg.cxx | 535 ++ sc/source/ui/dbgui/csvcontrol.cxx | 284 + sc/source/ui/dbgui/csvgrid.cxx | 1418 +++ sc/source/ui/dbgui/csvruler.cxx | 666 ++ sc/source/ui/dbgui/csvsplits.cxx | 102 + sc/source/ui/dbgui/csvtablebox.cxx | 369 + sc/source/ui/dbgui/dapidata.cxx | 169 + sc/source/ui/dbgui/dapitype.cxx | 153 + sc/source/ui/dbgui/dbnamdlg.cxx | 645 ++ sc/source/ui/dbgui/dpgroupdlg.cxx | 353 + sc/source/ui/dbgui/filtdlg.cxx | 1571 ++++ sc/source/ui/dbgui/foptmgr.cxx | 260 + sc/source/ui/dbgui/imoptdlg.cxx | 135 + sc/source/ui/dbgui/pfiltdlg.cxx | 507 ++ sc/source/ui/dbgui/pvfundlg.cxx | 973 ++ sc/source/ui/dbgui/scendlg.cxx | 163 + sc/source/ui/dbgui/scuiasciiopt.cxx | 943 ++ sc/source/ui/dbgui/scuiimoptdlg.cxx | 340 + sc/source/ui/dbgui/sfiltdlg.cxx | 435 + sc/source/ui/dbgui/sortdlg.cxx | 65 + sc/source/ui/dbgui/sortkeydlg.cxx | 77 + sc/source/ui/dbgui/subtdlg.cxx | 46 + sc/source/ui/dbgui/textimportoptions.cxx | 96 + sc/source/ui/dbgui/tpsort.cxx | 855 ++ sc/source/ui/dbgui/tpsubt.cxx | 621 ++ sc/source/ui/dbgui/validate.cxx | 927 ++ sc/source/ui/dialogs/SparklineDataRangeDialog.cxx | 202 + sc/source/ui/dialogs/SparklineDialog.cxx | 552 ++ sc/source/ui/dialogs/searchresults.cxx | 278 + sc/source/ui/docshell/arealink.cxx | 498 ++ sc/source/ui/docshell/autostyl.cxx | 191 + sc/source/ui/docshell/datastream.cxx | 549 ++ sc/source/ui/docshell/dbdocfun.cxx | 1790 ++++ sc/source/ui/docshell/dbdocimp.cxx | 628 ++ sc/source/ui/docshell/docfunc.cxx | 5922 +++++++++++++ sc/source/ui/docshell/docfuncutil.cxx | 116 + sc/source/ui/docshell/docsh.cxx | 3482 ++++++++ sc/source/ui/docshell/docsh2.cxx | 187 + sc/source/ui/docshell/docsh3.cxx | 1331 +++ sc/source/ui/docshell/docsh4.cxx | 2788 ++++++ sc/source/ui/docshell/docsh5.cxx | 1037 +++ sc/source/ui/docshell/docsh6.cxx | 517 ++ sc/source/ui/docshell/docsh8.cxx | 1082 +++ sc/source/ui/docshell/docshimp.hxx | 38 + sc/source/ui/docshell/documentlinkmgr.cxx | 274 + sc/source/ui/docshell/editable.cxx | 162 + sc/source/ui/docshell/externalrefmgr.cxx | 3319 +++++++ sc/source/ui/docshell/impex.cxx | 2887 ++++++ sc/source/ui/docshell/macromgr.cxx | 201 + sc/source/ui/docshell/olinefun.cxx | 792 ++ sc/source/ui/docshell/pagedata.cxx | 100 + sc/source/ui/docshell/pntlock.cxx | 43 + sc/source/ui/docshell/servobj.cxx | 258 + sc/source/ui/docshell/sizedev.cxx | 63 + sc/source/ui/docshell/tablink.cxx | 594 ++ sc/source/ui/docshell/tpstat.cxx | 70 + sc/source/ui/drawfunc/chartsh.cxx | 155 + sc/source/ui/drawfunc/drawsh.cxx | 626 ++ sc/source/ui/drawfunc/drawsh2.cxx | 564 ++ sc/source/ui/drawfunc/drawsh4.cxx | 65 + sc/source/ui/drawfunc/drawsh5.cxx | 721 ++ sc/source/ui/drawfunc/drformsh.cxx | 55 + sc/source/ui/drawfunc/drtxtob.cxx | 1238 +++ sc/source/ui/drawfunc/drtxtob1.cxx | 124 + sc/source/ui/drawfunc/drtxtob2.cxx | 229 + sc/source/ui/drawfunc/fuconarc.cxx | 154 + sc/source/ui/drawfunc/fuconcustomshape.cxx | 199 + sc/source/ui/drawfunc/fuconpol.cxx | 288 + sc/source/ui/drawfunc/fuconrec.cxx | 449 + sc/source/ui/drawfunc/fuconstr.cxx | 252 + sc/source/ui/drawfunc/fuconuno.cxx | 122 + sc/source/ui/drawfunc/fudraw.cxx | 766 ++ sc/source/ui/drawfunc/fuins1.cxx | 450 + sc/source/ui/drawfunc/fuins2.cxx | 689 ++ sc/source/ui/drawfunc/fupoor.cxx | 280 + sc/source/ui/drawfunc/fusel.cxx | 546 ++ sc/source/ui/drawfunc/fusel2.cxx | 159 + sc/source/ui/drawfunc/futext.cxx | 687 ++ sc/source/ui/drawfunc/futext2.cxx | 46 + sc/source/ui/drawfunc/futext3.cxx | 182 + sc/source/ui/drawfunc/graphsh.cxx | 380 + sc/source/ui/drawfunc/mediash.cxx | 65 + sc/source/ui/drawfunc/oleobjsh.cxx | 51 + sc/source/ui/formdlg/dwfunctr.cxx | 410 + sc/source/ui/formdlg/formdata.cxx | 33 + sc/source/ui/formdlg/formula.cxx | 691 ++ sc/source/ui/inc/AccessibilityHints.hxx | 57 + sc/source/ui/inc/AccessibleCell.hxx | 167 + sc/source/ui/inc/AccessibleCellBase.hxx | 137 + sc/source/ui/inc/AccessibleContextBase.hxx | 286 + sc/source/ui/inc/AccessibleCsvControl.hxx | 511 ++ sc/source/ui/inc/AccessibleDocument.hxx | 263 + sc/source/ui/inc/AccessibleDocumentBase.hxx | 34 + sc/source/ui/inc/AccessibleDocumentPagePreview.hxx | 132 + sc/source/ui/inc/AccessibleEditObject.hxx | 231 + sc/source/ui/inc/AccessiblePageHeader.hxx | 90 + sc/source/ui/inc/AccessiblePageHeaderArea.hxx | 113 + sc/source/ui/inc/AccessiblePreviewCell.hxx | 99 + sc/source/ui/inc/AccessiblePreviewHeaderCell.hxx | 130 + sc/source/ui/inc/AccessiblePreviewTable.hxx | 134 + sc/source/ui/inc/AccessibleSpreadsheet.hxx | 273 + sc/source/ui/inc/AccessibleTableBase.hxx | 234 + sc/source/ui/inc/AccessibleText.hxx | 290 + sc/source/ui/inc/AnalysisOfVarianceDialog.hxx | 61 + sc/source/ui/inc/ChartRangeSelectionListener.hxx | 64 + sc/source/ui/inc/ChiSquareTestDialog.hxx | 31 + sc/source/ui/inc/ChildWindowWrapper.hxx | 81 + sc/source/ui/inc/CorrelationDialog.hxx | 29 + sc/source/ui/inc/CovarianceDialog.hxx | 30 + sc/source/ui/inc/DescriptiveStatisticsDialog.hxx | 31 + sc/source/ui/inc/DrawModelBroadcaster.hxx | 55 + sc/source/ui/inc/ExponentialSmoothingDialog.hxx | 37 + sc/source/ui/inc/FTestDialog.hxx | 31 + sc/source/ui/inc/FilterListBox.hxx | 81 + sc/source/ui/inc/FourierAnalysisDialog.hxx | 56 + sc/source/ui/inc/IAnyRefDialog.hxx | 48 + sc/source/ui/inc/MatrixComparisonGenerator.hxx | 36 + sc/source/ui/inc/MovingAverageDialog.hxx | 38 + sc/source/ui/inc/PivotLayoutDialog.hxx | 140 + sc/source/ui/inc/PivotLayoutTreeList.hxx | 41 + sc/source/ui/inc/PivotLayoutTreeListBase.hxx | 70 + sc/source/ui/inc/PivotLayoutTreeListData.hxx | 42 + sc/source/ui/inc/PivotLayoutTreeListLabel.hxx | 34 + sc/source/ui/inc/RandomNumberGeneratorDialog.hxx | 85 + sc/source/ui/inc/RegressionDialog.hxx | 82 + sc/source/ui/inc/SamplingDialog.hxx | 91 + sc/source/ui/inc/SparklineDataRangeDialog.hxx | 67 + sc/source/ui/inc/SparklineDialog.hxx | 113 + sc/source/ui/inc/SparklineRenderer.hxx | 576 ++ sc/source/ui/inc/SparklineShell.hxx | 41 + sc/source/ui/inc/StatisticsInputOutputDialog.hxx | 90 + sc/source/ui/inc/StatisticsTwoVariableDialog.hxx | 91 + sc/source/ui/inc/TTestDialog.hxx | 31 + .../ui/inc/TableFillingAndNavigationTools.hxx | 159 + sc/source/ui/inc/ZTestDialog.hxx | 31 + sc/source/ui/inc/acredlin.hxx | 163 + sc/source/ui/inc/anyrefdg.hxx | 165 + sc/source/ui/inc/areasave.hxx | 70 + sc/source/ui/inc/areasdlg.hxx | 88 + sc/source/ui/inc/asciiopt.hxx | 108 + sc/source/ui/inc/attrdlg.hxx | 40 + sc/source/ui/inc/auditsh.hxx | 49 + sc/source/ui/inc/autofmt.hxx | 91 + sc/source/ui/inc/autostyl.hxx | 81 + sc/source/ui/inc/cbnumberformat.hxx | 42 + sc/source/ui/inc/cbutton.hxx | 56 + sc/source/ui/inc/cellmergeoption.hxx | 34 + sc/source/ui/inc/cellsh.hxx | 109 + sc/source/ui/inc/chartsh.hxx | 48 + sc/source/ui/inc/checklistmenu.hxx | 381 + sc/source/ui/inc/client.hxx | 44 + sc/source/ui/inc/cliputil.hxx | 31 + sc/source/ui/inc/colorformat.hxx | 64 + sc/source/ui/inc/colrowba.hxx | 90 + sc/source/ui/inc/condformatdlg.hxx | 129 + sc/source/ui/inc/condformatdlgentry.hxx | 331 + sc/source/ui/inc/condformatdlgitem.hxx | 62 + sc/source/ui/inc/condformathelper.hxx | 36 + sc/source/ui/inc/condformatmgr.hxx | 66 + sc/source/ui/inc/condformatuno.hxx | 366 + sc/source/ui/inc/conflictsdlg.hxx | 148 + sc/source/ui/inc/consdlg.hxx | 99 + sc/source/ui/inc/content.hxx | 161 + sc/source/ui/inc/corodlg.hxx | 45 + sc/source/ui/inc/crdlg.hxx | 39 + sc/source/ui/inc/crnrdlg.hxx | 94 + sc/source/ui/inc/csvcontrol.hxx | 373 + sc/source/ui/inc/csvgrid.hxx | 315 + sc/source/ui/inc/csvruler.hxx | 181 + sc/source/ui/inc/csvsplits.hxx | 81 + sc/source/ui/inc/csvtablebox.hxx | 132 + sc/source/ui/inc/dapidata.hxx | 44 + sc/source/ui/inc/dapitype.hxx | 69 + sc/source/ui/inc/datafdlg.hxx | 70 + sc/source/ui/inc/dataprovider.hxx | 149 + sc/source/ui/inc/dataproviderdlg.hxx | 100 + sc/source/ui/inc/datastream.hxx | 127 + sc/source/ui/inc/datastreamdlg.hxx | 62 + sc/source/ui/inc/datatableview.hxx | 109 + sc/source/ui/inc/datatransformation.hxx | 229 + sc/source/ui/inc/dbdocfun.hxx | 101 + sc/source/ui/inc/dbfunc.hxx | 118 + sc/source/ui/inc/dbnamdlg.hxx | 101 + sc/source/ui/inc/delcldlg.hxx | 41 + sc/source/ui/inc/delcodlg.hxx | 54 + sc/source/ui/inc/docfunc.hxx | 264 + sc/source/ui/inc/docfuncutil.hxx | 42 + sc/source/ui/inc/docsh.hxx | 504 ++ sc/source/ui/inc/dpcontrol.hxx | 71 + sc/source/ui/inc/dpgroupdlg.hxx | 138 + sc/source/ui/inc/drawsh.hxx | 96 + sc/source/ui/inc/drawutil.hxx | 38 + sc/source/ui/inc/drawview.hxx | 179 + sc/source/ui/inc/drformsh.hxx | 43 + sc/source/ui/inc/drtxtob.hxx | 80 + sc/source/ui/inc/drwtrans.hxx | 99 + sc/source/ui/inc/dwfunctr.hxx | 76 + sc/source/ui/inc/editable.hxx | 89 + sc/source/ui/inc/editfield.hxx | 43 + sc/source/ui/inc/editsh.hxx | 90 + sc/source/ui/inc/filldlg.hxx | 101 + sc/source/ui/inc/filtdlg.hxx | 244 + sc/source/ui/inc/foptmgr.hxx | 83 + sc/source/ui/inc/formatsh.hxx | 75 + sc/source/ui/inc/formdata.hxx | 48 + sc/source/ui/inc/formula.hxx | 104 + sc/source/ui/inc/fuconarc.hxx | 43 + sc/source/ui/inc/fuconcustomshape.hxx | 49 + sc/source/ui/inc/fuconpol.hxx | 44 + sc/source/ui/inc/fuconrec.hxx | 44 + sc/source/ui/inc/fuconstr.hxx | 43 + sc/source/ui/inc/fuconuno.hxx | 50 + sc/source/ui/inc/fudraw.hxx | 54 + sc/source/ui/inc/fuinsert.hxx | 59 + sc/source/ui/inc/fupoor.hxx | 107 + sc/source/ui/inc/fusel.hxx | 48 + sc/source/ui/inc/futext.hxx | 56 + sc/source/ui/inc/gototabdlg.hxx | 43 + sc/source/ui/inc/graphsh.hxx | 70 + sc/source/ui/inc/gridmerg.hxx | 52 + sc/source/ui/inc/gridwin.hxx | 527 ++ sc/source/ui/inc/groupdlg.hxx | 36 + sc/source/ui/inc/hdrcont.hxx | 133 + sc/source/ui/inc/hfedtdlg.hxx | 152 + sc/source/ui/inc/highred.hxx | 73 + sc/source/ui/inc/hiranges.hxx | 34 + sc/source/ui/inc/imoptdlg.hxx | 60 + sc/source/ui/inc/impex.hxx | 241 + sc/source/ui/inc/inputhdl.hxx | 334 + sc/source/ui/inc/inputwin.hxx | 366 + sc/source/ui/inc/inscldlg.hxx | 42 + sc/source/ui/inc/inscodlg.hxx | 102 + sc/source/ui/inc/instbdlg.hxx | 94 + sc/source/ui/inc/invmerge.hxx | 44 + sc/source/ui/inc/lbseldlg.hxx | 38 + sc/source/ui/inc/linkarea.hxx | 72 + sc/source/ui/inc/lnktrans.hxx | 42 + sc/source/ui/inc/mediash.hxx | 45 + sc/source/ui/inc/mergecellsdialog.hxx | 35 + sc/source/ui/inc/msgpool.hxx | 57 + sc/source/ui/inc/mtrindlg.hxx | 49 + sc/source/ui/inc/mvtabdlg.hxx | 84 + sc/source/ui/inc/namecrea.hxx | 38 + sc/source/ui/inc/namedefdlg.hxx | 89 + sc/source/ui/inc/namedlg.hxx | 124 + sc/source/ui/inc/namemgrtable.hxx | 91 + sc/source/ui/inc/namepast.hxx | 53 + sc/source/ui/inc/navcitem.hxx | 40 + sc/source/ui/inc/navipi.hxx | 194 + sc/source/ui/inc/navsett.hxx | 49 + sc/source/ui/inc/notemark.hxx | 69 + sc/source/ui/inc/oleobjsh.hxx | 43 + sc/source/ui/inc/olinefun.hxx | 52 + sc/source/ui/inc/olinewin.hxx | 225 + sc/source/ui/inc/opredlin.hxx | 41 + sc/source/ui/inc/optsolver.hxx | 246 + sc/source/ui/inc/output.hxx | 388 + sc/source/ui/inc/overlayobject.hxx | 65 + sc/source/ui/inc/pagedata.hxx | 81 + sc/source/ui/inc/pfiltdlg.hxx | 93 + sc/source/ui/inc/pfuncache.hxx | 111 + sc/source/ui/inc/pgbrksh.hxx | 43 + sc/source/ui/inc/pivotsh.hxx | 52 + sc/source/ui/inc/pntlock.hxx | 56 + sc/source/ui/inc/preview.hxx | 163 + sc/source/ui/inc/prevloc.hxx | 152 + sc/source/ui/inc/prevwsh.hxx | 118 + sc/source/ui/inc/printfun.hxx | 399 + sc/source/ui/inc/protectiondlg.hxx | 70 + sc/source/ui/inc/pvfundlg.hxx | 216 + sc/source/ui/inc/redcom.hxx | 57 + sc/source/ui/inc/reffact.hxx | 217 + sc/source/ui/inc/refundo.hxx | 55 + sc/source/ui/inc/retypepassdlg.hxx | 128 + sc/source/ui/inc/rfindlst.hxx | 64 + sc/source/ui/inc/scendlg.hxx | 58 + sc/source/ui/inc/scui_def.hxx | 69 + sc/source/ui/inc/scuiasciiopt.hxx | 136 + sc/source/ui/inc/scuiautofmt.hxx | 78 + sc/source/ui/inc/scuiimoptdlg.hxx | 75 + sc/source/ui/inc/scuitphfedit.hxx | 158 + sc/source/ui/inc/searchresults.hxx | 57 + sc/source/ui/inc/select.hxx | 108 + sc/source/ui/inc/selectionstate.hxx | 55 + sc/source/ui/inc/seltrans.hxx | 72 + sc/source/ui/inc/servobj.hxx | 64 + sc/source/ui/inc/sharedocdlg.hxx | 52 + sc/source/ui/inc/shtabdlg.hxx | 47 + sc/source/ui/inc/simpref.hxx | 78 + sc/source/ui/inc/sizedev.hxx | 46 + sc/source/ui/inc/solveroptions.hxx | 125 + sc/source/ui/inc/solverutil.hxx | 39 + sc/source/ui/inc/solvrdlg.hxx | 90 + sc/source/ui/inc/sortdlg.hxx | 43 + sc/source/ui/inc/sortkeydlg.hxx | 52 + sc/source/ui/inc/spelldialog.hxx | 90 + sc/source/ui/inc/spelleng.hxx | 151 + sc/source/ui/inc/spellparam.hxx | 71 + sc/source/ui/inc/strindlg.hxx | 43 + sc/source/ui/inc/styledlg.hxx | 41 + sc/source/ui/inc/subtdlg.hxx | 35 + sc/source/ui/inc/tabbgcolordlg.hxx | 68 + sc/source/ui/inc/tabcont.hxx | 78 + sc/source/ui/inc/tabopdlg.hxx | 93 + sc/source/ui/inc/tabpages.hxx | 68 + sc/source/ui/inc/tabsplit.hxx | 44 + sc/source/ui/inc/tabview.hxx | 612 ++ sc/source/ui/inc/tabvwsh.hxx | 425 + sc/source/ui/inc/target.hxx | 39 + sc/source/ui/inc/tbzoomsliderctrl.hxx | 86 + sc/source/ui/inc/textdlgs.hxx | 47 + sc/source/ui/inc/textimportoptions.hxx | 52 + sc/source/ui/inc/tpcalc.hxx | 73 + sc/source/ui/inc/tpcompatibility.hxx | 29 + sc/source/ui/inc/tpdefaults.hxx | 43 + sc/source/ui/inc/tpformula.hxx | 85 + sc/source/ui/inc/tphf.hxx | 70 + sc/source/ui/inc/tphfedit.hxx | 86 + sc/source/ui/inc/tpprint.hxx | 40 + sc/source/ui/inc/tpsort.hxx | 152 + sc/source/ui/inc/tpstat.hxx | 43 + sc/source/ui/inc/tpsubt.hxx | 147 + sc/source/ui/inc/tptable.hxx | 80 + sc/source/ui/inc/tpusrlst.hxx | 88 + sc/source/ui/inc/tpview.hxx | 115 + sc/source/ui/inc/transobj.hxx | 112 + sc/source/ui/inc/uiitems.hxx | 277 + sc/source/ui/inc/uiobject.hxx | 47 + sc/source/ui/inc/undo/UndoDeleteSparkline.hxx | 43 + sc/source/ui/inc/undo/UndoDeleteSparklineGroup.hxx | 45 + sc/source/ui/inc/undo/UndoEditSparkline.hxx | 47 + sc/source/ui/inc/undo/UndoEditSparklineGroup.hxx | 44 + sc/source/ui/inc/undo/UndoGroupSparklines.hxx | 56 + sc/source/ui/inc/undo/UndoInsertSparkline.hxx | 45 + sc/source/ui/inc/undo/UndoUngroupSparklines.hxx | 54 + sc/source/ui/inc/undobase.hxx | 178 + sc/source/ui/inc/undoblk.hxx | 983 +++ sc/source/ui/inc/undocell.hxx | 369 + sc/source/ui/inc/undoconvert.hxx | 33 + sc/source/ui/inc/undodat.hxx | 446 + sc/source/ui/inc/undodraw.hxx | 52 + sc/source/ui/inc/undoolk.hxx | 32 + sc/source/ui/inc/undosort.hxx | 34 + sc/source/ui/inc/undostyl.hxx | 102 + sc/source/ui/inc/undotab.hxx | 471 + sc/source/ui/inc/undoutil.hxx | 50 + sc/source/ui/inc/validate.hxx | 275 + sc/source/ui/inc/viewdata.hxx | 734 ++ sc/source/ui/inc/viewfunc.hxx | 384 + sc/source/ui/inc/viewutil.hxx | 88 + sc/source/ui/inc/warnbox.hxx | 39 + sc/source/ui/inc/xmlsourcedlg.hxx | 106 + sc/source/ui/miscdlgs/acredlin.cxx | 1792 ++++ sc/source/ui/miscdlgs/anyrefdg.cxx | 784 ++ sc/source/ui/miscdlgs/autofmt.cxx | 531 ++ sc/source/ui/miscdlgs/conflictsdlg.cxx | 643 ++ sc/source/ui/miscdlgs/crdlg.cxx | 45 + sc/source/ui/miscdlgs/crnrdlg.cxx | 800 ++ sc/source/ui/miscdlgs/datafdlg.cxx | 355 + sc/source/ui/miscdlgs/dataproviderdlg.cxx | 1125 +++ sc/source/ui/miscdlgs/datastreamdlg.cxx | 178 + sc/source/ui/miscdlgs/datatableview.cxx | 320 + sc/source/ui/miscdlgs/delcldlg.cxx | 101 + sc/source/ui/miscdlgs/delcodlg.cxx | 125 + sc/source/ui/miscdlgs/filldlg.cxx | 290 + sc/source/ui/miscdlgs/gototabdlg.cxx | 81 + sc/source/ui/miscdlgs/groupdlg.cxx | 52 + sc/source/ui/miscdlgs/highred.cxx | 221 + sc/source/ui/miscdlgs/inscldlg.cxx | 110 + sc/source/ui/miscdlgs/inscodlg.cxx | 367 + sc/source/ui/miscdlgs/instbdlg.cxx | 356 + sc/source/ui/miscdlgs/lbseldlg.cxx | 54 + sc/source/ui/miscdlgs/linkarea.cxx | 336 + sc/source/ui/miscdlgs/mergecellsdialog.cxx | 36 + sc/source/ui/miscdlgs/mtrindlg.cxx | 104 + sc/source/ui/miscdlgs/mvtabdlg.cxx | 319 + sc/source/ui/miscdlgs/namecrea.cxx | 55 + sc/source/ui/miscdlgs/optsolver.cxx | 1075 +++ sc/source/ui/miscdlgs/protectiondlg.cxx | 154 + sc/source/ui/miscdlgs/redcom.cxx | 167 + sc/source/ui/miscdlgs/retypepassdlg.cxx | 366 + sc/source/ui/miscdlgs/scuiautofmt.cxx | 385 + sc/source/ui/miscdlgs/sharedocdlg.cxx | 213 + sc/source/ui/miscdlgs/shtabdlg.cxx | 66 + sc/source/ui/miscdlgs/simpref.cxx | 190 + sc/source/ui/miscdlgs/solveroptions.cxx | 415 + sc/source/ui/miscdlgs/solverutil.cxx | 175 + sc/source/ui/miscdlgs/solvrdlg.cxx | 282 + sc/source/ui/miscdlgs/strindlg.cxx | 42 + sc/source/ui/miscdlgs/tabbgcolordlg.cxx | 141 + sc/source/ui/miscdlgs/tabopdlg.cxx | 345 + sc/source/ui/miscdlgs/textdlgs.cxx | 94 + sc/source/ui/miscdlgs/warnbox.cxx | 52 + sc/source/ui/namedlg/namedefdlg.cxx | 323 + sc/source/ui/namedlg/namedlg.cxx | 505 ++ sc/source/ui/namedlg/namemgrtable.cxx | 177 + sc/source/ui/namedlg/namepast.cxx | 99 + sc/source/ui/navipi/content.cxx | 1637 ++++ sc/source/ui/navipi/navcitem.cxx | 99 + sc/source/ui/navipi/navipi.cxx | 965 ++ sc/source/ui/navipi/scenwnd.cxx | 242 + sc/source/ui/optdlg/calcoptionsdlg.cxx | 135 + sc/source/ui/optdlg/calcoptionsdlg.hxx | 43 + sc/source/ui/optdlg/opredlin.cxx | 105 + sc/source/ui/optdlg/tpcalc.cxx | 277 + sc/source/ui/optdlg/tpcompatibility.cxx | 74 + sc/source/ui/optdlg/tpdefaults.cxx | 134 + sc/source/ui/optdlg/tpformula.cxx | 411 + sc/source/ui/optdlg/tpprint.cxx | 111 + sc/source/ui/optdlg/tpusrlst.cxx | 738 ++ sc/source/ui/optdlg/tpview.cxx | 619 ++ sc/source/ui/pagedlg/areasdlg.cxx | 786 ++ sc/source/ui/pagedlg/hfedtdlg.cxx | 263 + sc/source/ui/pagedlg/scuitphfedit.cxx | 855 ++ sc/source/ui/pagedlg/tphf.cxx | 262 + sc/source/ui/pagedlg/tphfedit.cxx | 258 + sc/source/ui/pagedlg/tptable.cxx | 522 ++ sc/source/ui/sidebar/AlignmentPropertyPanel.cxx | 339 + sc/source/ui/sidebar/AlignmentPropertyPanel.hxx | 112 + .../ui/sidebar/CellAppearancePropertyPanel.cxx | 506 ++ .../ui/sidebar/CellAppearancePropertyPanel.hxx | 147 + sc/source/ui/sidebar/CellBorderStyleControl.cxx | 281 + sc/source/ui/sidebar/CellBorderStyleControl.hxx | 52 + sc/source/ui/sidebar/CellLineStyleControl.cxx | 243 + sc/source/ui/sidebar/CellLineStyleControl.hxx | 54 + sc/source/ui/sidebar/CellLineStyleValueSet.cxx | 187 + sc/source/ui/sidebar/CellLineStyleValueSet.hxx | 48 + sc/source/ui/sidebar/NumberFormatControl.cxx | 77 + sc/source/ui/sidebar/NumberFormatPropertyPanel.cxx | 290 + sc/source/ui/sidebar/NumberFormatPropertyPanel.hxx | 94 + sc/source/ui/sidebar/ScPanelFactory.cxx | 143 + sc/source/ui/sidebar/ScPanelFactory.hxx | 55 + sc/source/ui/sparklines/SparklineAttributes.cxx | 277 + sc/source/ui/sparklines/SparklineData.cxx | 30 + sc/source/ui/sparklines/SparklineGroup.cxx | 34 + sc/source/ui/sparklines/SparklineList.cxx | 103 + sc/source/ui/styleui/styledlg.cxx | 139 + sc/source/ui/styleui/template.cur | Bin 0 -> 326 bytes sc/source/ui/uitest/uiobject.cxx | 410 + sc/source/ui/undo/UndoDeleteSparkline.cxx | 76 + sc/source/ui/undo/UndoDeleteSparklineGroup.cxx | 82 + sc/source/ui/undo/UndoEditSparkline.cxx | 63 + sc/source/ui/undo/UndoEditSparklineGroup.cxx | 65 + sc/source/ui/undo/UndoGroupSparklines.cxx | 91 + sc/source/ui/undo/UndoInsertSparkline.cxx | 78 + sc/source/ui/undo/UndoUngroupSparklines.cxx | 89 + sc/source/ui/undo/areasave.cxx | 192 + sc/source/ui/undo/refundo.cxx | 176 + sc/source/ui/undo/target.cxx | 24 + sc/source/ui/undo/undobase.cxx | 616 ++ sc/source/ui/undo/undoblk.cxx | 2437 +++++ sc/source/ui/undo/undoblk2.cxx | 186 + sc/source/ui/undo/undoblk3.cxx | 1743 ++++ sc/source/ui/undo/undocell.cxx | 1048 +++ sc/source/ui/undo/undocell2.cxx | 71 + sc/source/ui/undo/undoconvert.cxx | 52 + sc/source/ui/undo/undodat.cxx | 1925 ++++ sc/source/ui/undo/undodraw.cxx | 107 + sc/source/ui/undo/undoolk.cxx | 75 + sc/source/ui/undo/undorangename.cxx | 136 + sc/source/ui/undo/undosort.cxx | 87 + sc/source/ui/undo/undostyl.cxx | 280 + sc/source/ui/undo/undotab.cxx | 1570 ++++ sc/source/ui/undo/undoutil.cxx | 114 + .../ui/unoobj/ChartRangeSelectionListener.cxx | 71 + sc/source/ui/unoobj/ChartTools.cxx | 174 + sc/source/ui/unoobj/PivotTableDataProvider.cxx | 904 ++ sc/source/ui/unoobj/PivotTableDataSequence.cxx | 280 + sc/source/ui/unoobj/PivotTableDataSource.cxx | 49 + sc/source/ui/unoobj/TablePivotChart.cxx | 103 + sc/source/ui/unoobj/TablePivotCharts.cxx | 279 + sc/source/ui/unoobj/addruno.cxx | 300 + sc/source/ui/unoobj/afmtuno.cxx | 724 ++ sc/source/ui/unoobj/appluno.cxx | 621 ++ sc/source/ui/unoobj/celllistsource.cxx | 433 + sc/source/ui/unoobj/celllistsource.hxx | 155 + sc/source/ui/unoobj/cellsuno.cxx | 9302 ++++++++++++++++++++ sc/source/ui/unoobj/cellvaluebinding.cxx | 576 ++ sc/source/ui/unoobj/cellvaluebinding.hxx | 146 + sc/source/ui/unoobj/chart2uno.cxx | 3490 ++++++++ sc/source/ui/unoobj/chartuno.cxx | 738 ++ sc/source/ui/unoobj/condformatuno.cxx | 1904 ++++ sc/source/ui/unoobj/confuno.cxx | 657 ++ sc/source/ui/unoobj/convuno.cxx | 46 + sc/source/ui/unoobj/cursuno.cxx | 450 + sc/source/ui/unoobj/dapiuno.cxx | 3356 +++++++ sc/source/ui/unoobj/datauno.cxx | 2390 +++++ sc/source/ui/unoobj/defltuno.cxx | 335 + sc/source/ui/unoobj/dispuno.cxx | 369 + sc/source/ui/unoobj/docuno.cxx | 4866 ++++++++++ sc/source/ui/unoobj/drdefuno.cxx | 79 + sc/source/ui/unoobj/editsrc.cxx | 272 + sc/source/ui/unoobj/eventuno.cxx | 174 + sc/source/ui/unoobj/exceldetect.cxx | 198 + sc/source/ui/unoobj/exceldetect.hxx | 34 + sc/source/ui/unoobj/fielduno.cxx | 1302 +++ sc/source/ui/unoobj/filtuno.cxx | 363 + sc/source/ui/unoobj/fmtuno.cxx | 921 ++ sc/source/ui/unoobj/forbiuno.cxx | 81 + sc/source/ui/unoobj/funcuno.cxx | 653 ++ sc/source/ui/unoobj/linkuno.cxx | 1691 ++++ sc/source/ui/unoobj/listenercalls.cxx | 69 + sc/source/ui/unoobj/miscuno.cxx | 284 + sc/source/ui/unoobj/nameuno.cxx | 1158 +++ sc/source/ui/unoobj/notesuno.cxx | 232 + sc/source/ui/unoobj/optuno.cxx | 222 + sc/source/ui/unoobj/pageuno.cxx | 60 + sc/source/ui/unoobj/scdetect.cxx | 353 + sc/source/ui/unoobj/scdetect.hxx | 49 + sc/source/ui/unoobj/servuno.cxx | 620 ++ sc/source/ui/unoobj/shapeuno.cxx | 1443 +++ sc/source/ui/unoobj/srchuno.cxx | 197 + sc/source/ui/unoobj/styleuno.cxx | 1936 ++++ sc/source/ui/unoobj/targuno.cxx | 293 + sc/source/ui/unoobj/textuno.cxx | 886 ++ sc/source/ui/unoobj/tokenuno.cxx | 521 ++ sc/source/ui/unoobj/unodoc.cxx | 45 + sc/source/ui/unoobj/unoreflist.cxx | 54 + sc/source/ui/unoobj/viewuno.cxx | 2204 +++++ sc/source/ui/unoobj/warnpassword.cxx | 62 + sc/source/ui/vba/excelvbahelper.cxx | 399 + sc/source/ui/vba/excelvbahelper.hxx | 97 + sc/source/ui/vba/helperdecl.hxx | 47 + sc/source/ui/vba/vbaapplication.cxx | 1566 ++++ sc/source/ui/vba/vbaapplication.hxx | 168 + sc/source/ui/vba/vbaassistant.cxx | 116 + sc/source/ui/vba/vbaassistant.hxx | 57 + sc/source/ui/vba/vbaaxes.cxx | 211 + sc/source/ui/vba/vbaaxes.hxx | 47 + sc/source/ui/vba/vbaaxis.cxx | 656 ++ sc/source/ui/vba/vbaaxis.hxx | 90 + sc/source/ui/vba/vbaaxistitle.cxx | 44 + sc/source/ui/vba/vbaaxistitle.hxx | 36 + sc/source/ui/vba/vbaborders.cxx | 591 ++ sc/source/ui/vba/vbaborders.hxx | 67 + sc/source/ui/vba/vbacharacters.cxx | 134 + sc/source/ui/vba/vbacharacters.hxx | 61 + sc/source/ui/vba/vbachart.cxx | 1056 +++ sc/source/ui/vba/vbachart.hxx | 103 + sc/source/ui/vba/vbachartobject.cxx | 147 + sc/source/ui/vba/vbachartobject.hxx | 61 + sc/source/ui/vba/vbachartobjects.cxx | 206 + sc/source/ui/vba/vbachartobjects.hxx | 60 + sc/source/ui/vba/vbacharttitle.cxx | 44 + sc/source/ui/vba/vbacharttitle.hxx | 35 + sc/source/ui/vba/vbacomment.cxx | 234 + sc/source/ui/vba/vbacomment.hxx | 72 + sc/source/ui/vba/vbacomments.cxx | 113 + sc/source/ui/vba/vbacomments.hxx | 49 + sc/source/ui/vba/vbacondition.cxx | 143 + sc/source/ui/vba/vbacondition.hxx | 48 + sc/source/ui/vba/vbadialog.cxx | 85 + sc/source/ui/vba/vbadialog.hxx | 40 + sc/source/ui/vba/vbadialogs.cxx | 51 + sc/source/ui/vba/vbadialogs.hxx | 43 + sc/source/ui/vba/vbaeventshelper.cxx | 898 ++ sc/source/ui/vba/vbaeventshelper.hxx | 84 + sc/source/ui/vba/vbafiledialog.cxx | 174 + sc/source/ui/vba/vbafiledialog.hxx | 58 + sc/source/ui/vba/vbafiledialogitems.cxx | 123 + sc/source/ui/vba/vbafiledialogitems.hxx | 43 + sc/source/ui/vba/vbafont.cxx | 329 + sc/source/ui/vba/vbafont.hxx | 76 + sc/source/ui/vba/vbaformat.cxx | 814 ++ sc/source/ui/vba/vbaformat.hxx | 154 + sc/source/ui/vba/vbaformatcondition.cxx | 157 + sc/source/ui/vba/vbaformatcondition.hxx | 67 + sc/source/ui/vba/vbaformatconditions.cxx | 289 + sc/source/ui/vba/vbaformatconditions.hxx | 66 + sc/source/ui/vba/vbaglobals.cxx | 265 + sc/source/ui/vba/vbaglobals.hxx | 82 + sc/source/ui/vba/vbahyperlink.cxx | 236 + sc/source/ui/vba/vbahyperlink.hxx | 86 + sc/source/ui/vba/vbahyperlinks.cxx | 277 + sc/source/ui/vba/vbahyperlinks.hxx | 136 + sc/source/ui/vba/vbainterior.cxx | 413 + sc/source/ui/vba/vbainterior.hxx | 83 + sc/source/ui/vba/vbalineshape.cxx | 34 + sc/source/ui/vba/vbalineshape.hxx | 34 + sc/source/ui/vba/vbamenu.cxx | 67 + sc/source/ui/vba/vbamenu.hxx | 37 + sc/source/ui/vba/vbamenubar.cxx | 48 + sc/source/ui/vba/vbamenubar.hxx | 33 + sc/source/ui/vba/vbamenubars.cxx | 118 + sc/source/ui/vba/vbamenubars.hxx | 40 + sc/source/ui/vba/vbamenuitem.cxx | 64 + sc/source/ui/vba/vbamenuitem.hxx | 38 + sc/source/ui/vba/vbamenuitems.cxx | 138 + sc/source/ui/vba/vbamenuitems.hxx | 42 + sc/source/ui/vba/vbamenus.cxx | 124 + sc/source/ui/vba/vbamenus.hxx | 42 + sc/source/ui/vba/vbaname.cxx | 216 + sc/source/ui/vba/vbaname.hxx | 69 + sc/source/ui/vba/vbanames.cxx | 260 + sc/source/ui/vba/vbanames.hxx | 69 + sc/source/ui/vba/vbaoleobject.cxx | 150 + sc/source/ui/vba/vbaoleobject.hxx | 57 + sc/source/ui/vba/vbaoleobjects.cxx | 184 + sc/source/ui/vba/vbaoleobjects.hxx | 47 + sc/source/ui/vba/vbaoutline.cxx | 58 + sc/source/ui/vba/vbaoutline.hxx | 43 + sc/source/ui/vba/vbaovalshape.cxx | 34 + sc/source/ui/vba/vbaovalshape.hxx | 34 + sc/source/ui/vba/vbapagebreak.cxx | 142 + sc/source/ui/vba/vbapagebreak.hxx | 87 + sc/source/ui/vba/vbapagebreaks.cxx | 322 + sc/source/ui/vba/vbapagebreaks.hxx | 83 + sc/source/ui/vba/vbapagesetup.cxx | 631 ++ sc/source/ui/vba/vbapagesetup.hxx | 90 + sc/source/ui/vba/vbapalette.cxx | 115 + sc/source/ui/vba/vbapalette.hxx | 44 + sc/source/ui/vba/vbapane.cxx | 196 + sc/source/ui/vba/vbapane.hxx | 55 + sc/source/ui/vba/vbapivotcache.cxx | 50 + sc/source/ui/vba/vbapivotcache.hxx | 40 + sc/source/ui/vba/vbapivottable.cxx | 53 + sc/source/ui/vba/vbapivottable.hxx | 40 + sc/source/ui/vba/vbapivottables.cxx | 89 + sc/source/ui/vba/vbapivottables.hxx | 49 + sc/source/ui/vba/vbarange.cxx | 5720 ++++++++++++ sc/source/ui/vba/vbarange.hxx | 327 + sc/source/ui/vba/vbasheetobject.cxx | 540 ++ sc/source/ui/vba/vbasheetobject.hxx | 209 + sc/source/ui/vba/vbasheetobjects.cxx | 555 ++ sc/source/ui/vba/vbasheetobjects.hxx | 102 + sc/source/ui/vba/vbastyle.cxx | 183 + sc/source/ui/vba/vbastyle.hxx | 62 + sc/source/ui/vba/vbastyles.cxx | 199 + sc/source/ui/vba/vbastyles.hxx | 50 + sc/source/ui/vba/vbatextboxshape.cxx | 60 + sc/source/ui/vba/vbatextboxshape.hxx | 39 + sc/source/ui/vba/vbatextframe.cxx | 67 + sc/source/ui/vba/vbatextframe.hxx | 40 + sc/source/ui/vba/vbatitle.hxx | 141 + sc/source/ui/vba/vbavalidation.cxx | 382 + sc/source/ui/vba/vbavalidation.hxx | 64 + sc/source/ui/vba/vbawindow.cxx | 868 ++ sc/source/ui/vba/vbawindow.hxx | 126 + sc/source/ui/vba/vbawindows.cxx | 249 + sc/source/ui/vba/vbawindows.hxx | 48 + sc/source/ui/vba/vbaworkbook.cxx | 420 + sc/source/ui/vba/vbaworkbook.hxx | 73 + sc/source/ui/vba/vbaworkbooks.cxx | 291 + sc/source/ui/vba/vbaworkbooks.hxx | 56 + sc/source/ui/vba/vbaworksheet.cxx | 1052 +++ sc/source/ui/vba/vbaworksheet.hxx | 167 + sc/source/ui/vba/vbaworksheets.cxx | 535 ++ sc/source/ui/vba/vbaworksheets.hxx | 68 + sc/source/ui/vba/vbawsfunction.cxx | 298 + sc/source/ui/vba/vbawsfunction.hxx | 45 + sc/source/ui/view/SparklineShell.cxx | 54 + sc/source/ui/view/auditsh.cxx | 123 + sc/source/ui/view/cellmergeoption.cxx | 49 + sc/source/ui/view/cellsh.cxx | 1322 +++ sc/source/ui/view/cellsh1.cxx | 3454 ++++++++ sc/source/ui/view/cellsh2.cxx | 1263 +++ sc/source/ui/view/cellsh3.cxx | 1067 +++ sc/source/ui/view/cellsh4.cxx | 518 ++ sc/source/ui/view/cliputil.cxx | 169 + sc/source/ui/view/colrowba.cxx | 383 + sc/source/ui/view/dbfunc.cxx | 447 + sc/source/ui/view/dbfunc2.cxx | 41 + sc/source/ui/view/dbfunc3.cxx | 2314 +++++ sc/source/ui/view/dbfunc4.cxx | 73 + sc/source/ui/view/drawutil.cxx | 89 + sc/source/ui/view/drawvie3.cxx | 259 + sc/source/ui/view/drawvie4.cxx | 579 ++ sc/source/ui/view/drawview.cxx | 1264 +++ sc/source/ui/view/editsh.cxx | 1367 +++ sc/source/ui/view/formatsh.cxx | 2868 ++++++ sc/source/ui/view/gridmerg.cxx | 227 + sc/source/ui/view/gridwin.cxx | 7094 +++++++++++++++ sc/source/ui/view/gridwin2.cxx | 1090 +++ sc/source/ui/view/gridwin3.cxx | 399 + sc/source/ui/view/gridwin4.cxx | 2608 ++++++ sc/source/ui/view/gridwin5.cxx | 392 + sc/source/ui/view/gridwin_dbgutil.cxx | 171 + sc/source/ui/view/hdrcont.cxx | 1123 +++ sc/source/ui/view/hintwin.cxx | 181 + sc/source/ui/view/imapwrap.cxx | 48 + sc/source/ui/view/imapwrap.hxx | 40 + sc/source/ui/view/invmerge.cxx | 162 + sc/source/ui/view/notemark.cxx | 203 + sc/source/ui/view/olinewin.cxx | 1040 +++ sc/source/ui/view/output.cxx | 2698 ++++++ sc/source/ui/view/output2.cxx | 5123 +++++++++++ sc/source/ui/view/output3.cxx | 215 + sc/source/ui/view/overlayobject.cxx | 85 + sc/source/ui/view/pfuncache.cxx | 190 + sc/source/ui/view/pgbrksh.cxx | 53 + sc/source/ui/view/pivotsh.cxx | 167 + sc/source/ui/view/preview.cxx | 1575 ++++ sc/source/ui/view/prevloc.cxx | 718 ++ sc/source/ui/view/prevwsh.cxx | 1189 +++ sc/source/ui/view/prevwsh2.cxx | 68 + sc/source/ui/view/printfun.cxx | 3229 +++++++ sc/source/ui/view/reffact.cxx | 298 + sc/source/ui/view/scextopt.cxx | 218 + sc/source/ui/view/select.cxx | 983 +++ sc/source/ui/view/selectionstate.cxx | 54 + sc/source/ui/view/spellcheckcontext.cxx | 387 + sc/source/ui/view/spelldialog.cxx | 281 + sc/source/ui/view/spelleng.cxx | 447 + sc/source/ui/view/tabcont.cxx | 669 ++ sc/source/ui/view/tabsplit.cxx | 127 + sc/source/ui/view/tabview.cxx | 3016 +++++++ sc/source/ui/view/tabview2.cxx | 1510 ++++ sc/source/ui/view/tabview3.cxx | 3112 +++++++ sc/source/ui/view/tabview4.cxx | 532 ++ sc/source/ui/view/tabview5.cxx | 708 ++ sc/source/ui/view/tabvwsh.cxx | 120 + sc/source/ui/view/tabvwsh2.cxx | 472 + sc/source/ui/view/tabvwsh3.cxx | 1352 +++ sc/source/ui/view/tabvwsh4.cxx | 1942 ++++ sc/source/ui/view/tabvwsh5.cxx | 390 + sc/source/ui/view/tabvwsh8.cxx | 76 + sc/source/ui/view/tabvwsh9.cxx | 203 + sc/source/ui/view/tabvwsha.cxx | 896 ++ sc/source/ui/view/tabvwshb.cxx | 831 ++ sc/source/ui/view/tabvwshc.cxx | 745 ++ sc/source/ui/view/tabvwshd.cxx | 67 + sc/source/ui/view/tabvwshe.cxx | 342 + sc/source/ui/view/tabvwshf.cxx | 1055 +++ sc/source/ui/view/tabvwshg.cxx | 120 + sc/source/ui/view/tabvwshh.cxx | 261 + sc/source/ui/view/viewdata.cxx | 4366 +++++++++ sc/source/ui/view/viewfun2.cxx | 3464 ++++++++ sc/source/ui/view/viewfun3.cxx | 2027 +++++ sc/source/ui/view/viewfun4.cxx | 789 ++ sc/source/ui/view/viewfun5.cxx | 818 ++ sc/source/ui/view/viewfun6.cxx | 546 ++ sc/source/ui/view/viewfun7.cxx | 456 + sc/source/ui/view/viewfunc.cxx | 3073 +++++++ sc/source/ui/view/viewutil.cxx | 426 + sc/source/ui/view/waitoff.cxx | 51 + sc/source/ui/xmlsource/xmlsourcedlg.cxx | 622 ++ 818 files changed, 329722 insertions(+) create mode 100644 sc/source/ui/Accessibility/AccessibilityHints.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleCell.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleCellBase.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleContextBase.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleCsvControl.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleDocument.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleDocumentBase.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleEditObject.cxx create mode 100644 sc/source/ui/Accessibility/AccessiblePageHeader.cxx create mode 100644 sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx create mode 100644 sc/source/ui/Accessibility/AccessiblePreviewCell.cxx create mode 100644 sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx create mode 100644 sc/source/ui/Accessibility/AccessiblePreviewTable.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleTableBase.cxx create mode 100644 sc/source/ui/Accessibility/AccessibleText.cxx create mode 100644 sc/source/ui/Accessibility/DrawModelBroadcaster.cxx create mode 100644 sc/source/ui/StatisticsDialogs/AnalysisOfVarianceDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/ChiSquareTestDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/CorrelationDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/CovarianceDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/DescriptiveStatisticsDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/ExponentialSmoothingDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/FTestDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/FourierAnalysisDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/MatrixComparisonGenerator.cxx create mode 100644 sc/source/ui/StatisticsDialogs/MovingAverageDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/RegressionDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/SamplingDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/StatisticsInputOutputDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/StatisticsTwoVariableDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/TTestDialog.cxx create mode 100644 sc/source/ui/StatisticsDialogs/TableFillingAndNavigationTools.cxx create mode 100644 sc/source/ui/StatisticsDialogs/ZTestDialog.cxx create mode 100644 sc/source/ui/app/client.cxx create mode 100644 sc/source/ui/app/drwtrans.cxx create mode 100644 sc/source/ui/app/inputhdl.cxx create mode 100644 sc/source/ui/app/inputwin.cxx create mode 100644 sc/source/ui/app/lnktrans.cxx create mode 100644 sc/source/ui/app/msgpool.cxx create mode 100644 sc/source/ui/app/rfindlst.cxx create mode 100644 sc/source/ui/app/scdll.cxx create mode 100644 sc/source/ui/app/scmod.cxx create mode 100644 sc/source/ui/app/seltrans.cxx create mode 100644 sc/source/ui/app/transobj.cxx create mode 100644 sc/source/ui/app/typemap.cxx create mode 100644 sc/source/ui/app/uiitems.cxx create mode 100644 sc/source/ui/attrdlg/attrdlg.cxx create mode 100644 sc/source/ui/attrdlg/scabstdlg.cxx create mode 100644 sc/source/ui/attrdlg/scdlgfact.cxx create mode 100644 sc/source/ui/attrdlg/scdlgfact.hxx create mode 100644 sc/source/ui/attrdlg/scuiexp.cxx create mode 100644 sc/source/ui/attrdlg/tabpages.cxx create mode 100644 sc/source/ui/cctrl/cbnumberformat.cxx create mode 100644 sc/source/ui/cctrl/cbuttonw.cxx create mode 100644 sc/source/ui/cctrl/checklistmenu.cxx create mode 100644 sc/source/ui/cctrl/dpcontrol.cxx create mode 100644 sc/source/ui/cctrl/editfield.cxx create mode 100644 sc/source/ui/cctrl/tbzoomsliderctrl.cxx create mode 100644 sc/source/ui/condformat/colorformat.cxx create mode 100644 sc/source/ui/condformat/condformatdlg.cxx create mode 100644 sc/source/ui/condformat/condformatdlgentry.cxx create mode 100644 sc/source/ui/condformat/condformatdlgitem.cxx create mode 100644 sc/source/ui/condformat/condformathelper.cxx create mode 100644 sc/source/ui/condformat/condformatmgr.cxx create mode 100644 sc/source/ui/dataprovider/csvdataprovider.cxx create mode 100644 sc/source/ui/dataprovider/dataprovider.cxx create mode 100644 sc/source/ui/dataprovider/datatransformation.cxx create mode 100644 sc/source/ui/dataprovider/htmldataprovider.cxx create mode 100644 sc/source/ui/dataprovider/htmldataprovider.hxx create mode 100644 sc/source/ui/dataprovider/sqldataprovider.cxx create mode 100644 sc/source/ui/dataprovider/sqldataprovider.hxx create mode 100644 sc/source/ui/dataprovider/xmldataprovider.cxx create mode 100644 sc/source/ui/dataprovider/xmldataprovider.hxx create mode 100644 sc/source/ui/dbgui/PivotLayoutDialog.cxx create mode 100644 sc/source/ui/dbgui/PivotLayoutTreeList.cxx create mode 100644 sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx create mode 100644 sc/source/ui/dbgui/PivotLayoutTreeListData.cxx create mode 100644 sc/source/ui/dbgui/PivotLayoutTreeListLabel.cxx create mode 100644 sc/source/ui/dbgui/asciiopt.cxx create mode 100644 sc/source/ui/dbgui/consdlg.cxx create mode 100644 sc/source/ui/dbgui/csvcontrol.cxx create mode 100644 sc/source/ui/dbgui/csvgrid.cxx create mode 100644 sc/source/ui/dbgui/csvruler.cxx create mode 100644 sc/source/ui/dbgui/csvsplits.cxx create mode 100644 sc/source/ui/dbgui/csvtablebox.cxx create mode 100644 sc/source/ui/dbgui/dapidata.cxx create mode 100644 sc/source/ui/dbgui/dapitype.cxx create mode 100644 sc/source/ui/dbgui/dbnamdlg.cxx create mode 100644 sc/source/ui/dbgui/dpgroupdlg.cxx create mode 100644 sc/source/ui/dbgui/filtdlg.cxx create mode 100644 sc/source/ui/dbgui/foptmgr.cxx create mode 100644 sc/source/ui/dbgui/imoptdlg.cxx create mode 100644 sc/source/ui/dbgui/pfiltdlg.cxx create mode 100644 sc/source/ui/dbgui/pvfundlg.cxx create mode 100644 sc/source/ui/dbgui/scendlg.cxx create mode 100644 sc/source/ui/dbgui/scuiasciiopt.cxx create mode 100644 sc/source/ui/dbgui/scuiimoptdlg.cxx create mode 100644 sc/source/ui/dbgui/sfiltdlg.cxx create mode 100644 sc/source/ui/dbgui/sortdlg.cxx create mode 100644 sc/source/ui/dbgui/sortkeydlg.cxx create mode 100644 sc/source/ui/dbgui/subtdlg.cxx create mode 100644 sc/source/ui/dbgui/textimportoptions.cxx create mode 100644 sc/source/ui/dbgui/tpsort.cxx create mode 100644 sc/source/ui/dbgui/tpsubt.cxx create mode 100644 sc/source/ui/dbgui/validate.cxx create mode 100644 sc/source/ui/dialogs/SparklineDataRangeDialog.cxx create mode 100644 sc/source/ui/dialogs/SparklineDialog.cxx create mode 100644 sc/source/ui/dialogs/searchresults.cxx create mode 100644 sc/source/ui/docshell/arealink.cxx create mode 100644 sc/source/ui/docshell/autostyl.cxx create mode 100644 sc/source/ui/docshell/datastream.cxx create mode 100644 sc/source/ui/docshell/dbdocfun.cxx create mode 100644 sc/source/ui/docshell/dbdocimp.cxx create mode 100644 sc/source/ui/docshell/docfunc.cxx create mode 100644 sc/source/ui/docshell/docfuncutil.cxx create mode 100644 sc/source/ui/docshell/docsh.cxx create mode 100644 sc/source/ui/docshell/docsh2.cxx create mode 100644 sc/source/ui/docshell/docsh3.cxx create mode 100644 sc/source/ui/docshell/docsh4.cxx create mode 100644 sc/source/ui/docshell/docsh5.cxx create mode 100644 sc/source/ui/docshell/docsh6.cxx create mode 100644 sc/source/ui/docshell/docsh8.cxx create mode 100644 sc/source/ui/docshell/docshimp.hxx create mode 100644 sc/source/ui/docshell/documentlinkmgr.cxx create mode 100644 sc/source/ui/docshell/editable.cxx create mode 100644 sc/source/ui/docshell/externalrefmgr.cxx create mode 100644 sc/source/ui/docshell/impex.cxx create mode 100644 sc/source/ui/docshell/macromgr.cxx create mode 100644 sc/source/ui/docshell/olinefun.cxx create mode 100644 sc/source/ui/docshell/pagedata.cxx create mode 100644 sc/source/ui/docshell/pntlock.cxx create mode 100644 sc/source/ui/docshell/servobj.cxx create mode 100644 sc/source/ui/docshell/sizedev.cxx create mode 100644 sc/source/ui/docshell/tablink.cxx create mode 100644 sc/source/ui/docshell/tpstat.cxx create mode 100644 sc/source/ui/drawfunc/chartsh.cxx create mode 100644 sc/source/ui/drawfunc/drawsh.cxx create mode 100644 sc/source/ui/drawfunc/drawsh2.cxx create mode 100644 sc/source/ui/drawfunc/drawsh4.cxx create mode 100644 sc/source/ui/drawfunc/drawsh5.cxx create mode 100644 sc/source/ui/drawfunc/drformsh.cxx create mode 100644 sc/source/ui/drawfunc/drtxtob.cxx create mode 100644 sc/source/ui/drawfunc/drtxtob1.cxx create mode 100644 sc/source/ui/drawfunc/drtxtob2.cxx create mode 100644 sc/source/ui/drawfunc/fuconarc.cxx create mode 100644 sc/source/ui/drawfunc/fuconcustomshape.cxx create mode 100644 sc/source/ui/drawfunc/fuconpol.cxx create mode 100644 sc/source/ui/drawfunc/fuconrec.cxx create mode 100644 sc/source/ui/drawfunc/fuconstr.cxx create mode 100644 sc/source/ui/drawfunc/fuconuno.cxx create mode 100644 sc/source/ui/drawfunc/fudraw.cxx create mode 100644 sc/source/ui/drawfunc/fuins1.cxx create mode 100644 sc/source/ui/drawfunc/fuins2.cxx create mode 100644 sc/source/ui/drawfunc/fupoor.cxx create mode 100644 sc/source/ui/drawfunc/fusel.cxx create mode 100644 sc/source/ui/drawfunc/fusel2.cxx create mode 100644 sc/source/ui/drawfunc/futext.cxx create mode 100644 sc/source/ui/drawfunc/futext2.cxx create mode 100644 sc/source/ui/drawfunc/futext3.cxx create mode 100644 sc/source/ui/drawfunc/graphsh.cxx create mode 100644 sc/source/ui/drawfunc/mediash.cxx create mode 100644 sc/source/ui/drawfunc/oleobjsh.cxx create mode 100644 sc/source/ui/formdlg/dwfunctr.cxx create mode 100644 sc/source/ui/formdlg/formdata.cxx create mode 100644 sc/source/ui/formdlg/formula.cxx create mode 100644 sc/source/ui/inc/AccessibilityHints.hxx create mode 100644 sc/source/ui/inc/AccessibleCell.hxx create mode 100644 sc/source/ui/inc/AccessibleCellBase.hxx create mode 100644 sc/source/ui/inc/AccessibleContextBase.hxx create mode 100644 sc/source/ui/inc/AccessibleCsvControl.hxx create mode 100644 sc/source/ui/inc/AccessibleDocument.hxx create mode 100644 sc/source/ui/inc/AccessibleDocumentBase.hxx create mode 100644 sc/source/ui/inc/AccessibleDocumentPagePreview.hxx create mode 100644 sc/source/ui/inc/AccessibleEditObject.hxx create mode 100644 sc/source/ui/inc/AccessiblePageHeader.hxx create mode 100644 sc/source/ui/inc/AccessiblePageHeaderArea.hxx create mode 100644 sc/source/ui/inc/AccessiblePreviewCell.hxx create mode 100644 sc/source/ui/inc/AccessiblePreviewHeaderCell.hxx create mode 100644 sc/source/ui/inc/AccessiblePreviewTable.hxx create mode 100644 sc/source/ui/inc/AccessibleSpreadsheet.hxx create mode 100644 sc/source/ui/inc/AccessibleTableBase.hxx create mode 100644 sc/source/ui/inc/AccessibleText.hxx create mode 100644 sc/source/ui/inc/AnalysisOfVarianceDialog.hxx create mode 100644 sc/source/ui/inc/ChartRangeSelectionListener.hxx create mode 100644 sc/source/ui/inc/ChiSquareTestDialog.hxx create mode 100644 sc/source/ui/inc/ChildWindowWrapper.hxx create mode 100644 sc/source/ui/inc/CorrelationDialog.hxx create mode 100644 sc/source/ui/inc/CovarianceDialog.hxx create mode 100644 sc/source/ui/inc/DescriptiveStatisticsDialog.hxx create mode 100644 sc/source/ui/inc/DrawModelBroadcaster.hxx create mode 100644 sc/source/ui/inc/ExponentialSmoothingDialog.hxx create mode 100644 sc/source/ui/inc/FTestDialog.hxx create mode 100644 sc/source/ui/inc/FilterListBox.hxx create mode 100644 sc/source/ui/inc/FourierAnalysisDialog.hxx create mode 100644 sc/source/ui/inc/IAnyRefDialog.hxx create mode 100644 sc/source/ui/inc/MatrixComparisonGenerator.hxx create mode 100644 sc/source/ui/inc/MovingAverageDialog.hxx create mode 100644 sc/source/ui/inc/PivotLayoutDialog.hxx create mode 100644 sc/source/ui/inc/PivotLayoutTreeList.hxx create mode 100644 sc/source/ui/inc/PivotLayoutTreeListBase.hxx create mode 100644 sc/source/ui/inc/PivotLayoutTreeListData.hxx create mode 100644 sc/source/ui/inc/PivotLayoutTreeListLabel.hxx create mode 100644 sc/source/ui/inc/RandomNumberGeneratorDialog.hxx create mode 100644 sc/source/ui/inc/RegressionDialog.hxx create mode 100644 sc/source/ui/inc/SamplingDialog.hxx create mode 100644 sc/source/ui/inc/SparklineDataRangeDialog.hxx create mode 100644 sc/source/ui/inc/SparklineDialog.hxx create mode 100644 sc/source/ui/inc/SparklineRenderer.hxx create mode 100644 sc/source/ui/inc/SparklineShell.hxx create mode 100644 sc/source/ui/inc/StatisticsInputOutputDialog.hxx create mode 100644 sc/source/ui/inc/StatisticsTwoVariableDialog.hxx create mode 100644 sc/source/ui/inc/TTestDialog.hxx create mode 100644 sc/source/ui/inc/TableFillingAndNavigationTools.hxx create mode 100644 sc/source/ui/inc/ZTestDialog.hxx create mode 100644 sc/source/ui/inc/acredlin.hxx create mode 100644 sc/source/ui/inc/anyrefdg.hxx create mode 100644 sc/source/ui/inc/areasave.hxx create mode 100644 sc/source/ui/inc/areasdlg.hxx create mode 100644 sc/source/ui/inc/asciiopt.hxx create mode 100644 sc/source/ui/inc/attrdlg.hxx create mode 100644 sc/source/ui/inc/auditsh.hxx create mode 100644 sc/source/ui/inc/autofmt.hxx create mode 100644 sc/source/ui/inc/autostyl.hxx create mode 100644 sc/source/ui/inc/cbnumberformat.hxx create mode 100644 sc/source/ui/inc/cbutton.hxx create mode 100644 sc/source/ui/inc/cellmergeoption.hxx create mode 100644 sc/source/ui/inc/cellsh.hxx create mode 100644 sc/source/ui/inc/chartsh.hxx create mode 100644 sc/source/ui/inc/checklistmenu.hxx create mode 100644 sc/source/ui/inc/client.hxx create mode 100644 sc/source/ui/inc/cliputil.hxx create mode 100644 sc/source/ui/inc/colorformat.hxx create mode 100644 sc/source/ui/inc/colrowba.hxx create mode 100644 sc/source/ui/inc/condformatdlg.hxx create mode 100644 sc/source/ui/inc/condformatdlgentry.hxx create mode 100644 sc/source/ui/inc/condformatdlgitem.hxx create mode 100644 sc/source/ui/inc/condformathelper.hxx create mode 100644 sc/source/ui/inc/condformatmgr.hxx create mode 100644 sc/source/ui/inc/condformatuno.hxx create mode 100644 sc/source/ui/inc/conflictsdlg.hxx create mode 100644 sc/source/ui/inc/consdlg.hxx create mode 100644 sc/source/ui/inc/content.hxx create mode 100644 sc/source/ui/inc/corodlg.hxx create mode 100644 sc/source/ui/inc/crdlg.hxx create mode 100644 sc/source/ui/inc/crnrdlg.hxx create mode 100644 sc/source/ui/inc/csvcontrol.hxx create mode 100644 sc/source/ui/inc/csvgrid.hxx create mode 100644 sc/source/ui/inc/csvruler.hxx create mode 100644 sc/source/ui/inc/csvsplits.hxx create mode 100644 sc/source/ui/inc/csvtablebox.hxx create mode 100644 sc/source/ui/inc/dapidata.hxx create mode 100644 sc/source/ui/inc/dapitype.hxx create mode 100644 sc/source/ui/inc/datafdlg.hxx create mode 100644 sc/source/ui/inc/dataprovider.hxx create mode 100644 sc/source/ui/inc/dataproviderdlg.hxx create mode 100644 sc/source/ui/inc/datastream.hxx create mode 100644 sc/source/ui/inc/datastreamdlg.hxx create mode 100644 sc/source/ui/inc/datatableview.hxx create mode 100644 sc/source/ui/inc/datatransformation.hxx create mode 100644 sc/source/ui/inc/dbdocfun.hxx create mode 100644 sc/source/ui/inc/dbfunc.hxx create mode 100644 sc/source/ui/inc/dbnamdlg.hxx create mode 100644 sc/source/ui/inc/delcldlg.hxx create mode 100644 sc/source/ui/inc/delcodlg.hxx create mode 100644 sc/source/ui/inc/docfunc.hxx create mode 100644 sc/source/ui/inc/docfuncutil.hxx create mode 100644 sc/source/ui/inc/docsh.hxx create mode 100644 sc/source/ui/inc/dpcontrol.hxx create mode 100644 sc/source/ui/inc/dpgroupdlg.hxx create mode 100644 sc/source/ui/inc/drawsh.hxx create mode 100644 sc/source/ui/inc/drawutil.hxx create mode 100644 sc/source/ui/inc/drawview.hxx create mode 100644 sc/source/ui/inc/drformsh.hxx create mode 100644 sc/source/ui/inc/drtxtob.hxx create mode 100644 sc/source/ui/inc/drwtrans.hxx create mode 100644 sc/source/ui/inc/dwfunctr.hxx create mode 100644 sc/source/ui/inc/editable.hxx create mode 100644 sc/source/ui/inc/editfield.hxx create mode 100644 sc/source/ui/inc/editsh.hxx create mode 100644 sc/source/ui/inc/filldlg.hxx create mode 100644 sc/source/ui/inc/filtdlg.hxx create mode 100644 sc/source/ui/inc/foptmgr.hxx create mode 100644 sc/source/ui/inc/formatsh.hxx create mode 100644 sc/source/ui/inc/formdata.hxx create mode 100644 sc/source/ui/inc/formula.hxx create mode 100644 sc/source/ui/inc/fuconarc.hxx create mode 100644 sc/source/ui/inc/fuconcustomshape.hxx create mode 100644 sc/source/ui/inc/fuconpol.hxx create mode 100644 sc/source/ui/inc/fuconrec.hxx create mode 100644 sc/source/ui/inc/fuconstr.hxx create mode 100644 sc/source/ui/inc/fuconuno.hxx create mode 100644 sc/source/ui/inc/fudraw.hxx create mode 100644 sc/source/ui/inc/fuinsert.hxx create mode 100644 sc/source/ui/inc/fupoor.hxx create mode 100644 sc/source/ui/inc/fusel.hxx create mode 100644 sc/source/ui/inc/futext.hxx create mode 100644 sc/source/ui/inc/gototabdlg.hxx create mode 100644 sc/source/ui/inc/graphsh.hxx create mode 100644 sc/source/ui/inc/gridmerg.hxx create mode 100644 sc/source/ui/inc/gridwin.hxx create mode 100644 sc/source/ui/inc/groupdlg.hxx create mode 100644 sc/source/ui/inc/hdrcont.hxx create mode 100644 sc/source/ui/inc/hfedtdlg.hxx create mode 100644 sc/source/ui/inc/highred.hxx create mode 100644 sc/source/ui/inc/hiranges.hxx create mode 100644 sc/source/ui/inc/imoptdlg.hxx create mode 100644 sc/source/ui/inc/impex.hxx create mode 100644 sc/source/ui/inc/inputhdl.hxx create mode 100644 sc/source/ui/inc/inputwin.hxx create mode 100644 sc/source/ui/inc/inscldlg.hxx create mode 100644 sc/source/ui/inc/inscodlg.hxx create mode 100644 sc/source/ui/inc/instbdlg.hxx create mode 100644 sc/source/ui/inc/invmerge.hxx create mode 100644 sc/source/ui/inc/lbseldlg.hxx create mode 100644 sc/source/ui/inc/linkarea.hxx create mode 100644 sc/source/ui/inc/lnktrans.hxx create mode 100644 sc/source/ui/inc/mediash.hxx create mode 100644 sc/source/ui/inc/mergecellsdialog.hxx create mode 100644 sc/source/ui/inc/msgpool.hxx create mode 100644 sc/source/ui/inc/mtrindlg.hxx create mode 100644 sc/source/ui/inc/mvtabdlg.hxx create mode 100644 sc/source/ui/inc/namecrea.hxx create mode 100644 sc/source/ui/inc/namedefdlg.hxx create mode 100644 sc/source/ui/inc/namedlg.hxx create mode 100644 sc/source/ui/inc/namemgrtable.hxx create mode 100644 sc/source/ui/inc/namepast.hxx create mode 100644 sc/source/ui/inc/navcitem.hxx create mode 100644 sc/source/ui/inc/navipi.hxx create mode 100644 sc/source/ui/inc/navsett.hxx create mode 100644 sc/source/ui/inc/notemark.hxx create mode 100644 sc/source/ui/inc/oleobjsh.hxx create mode 100644 sc/source/ui/inc/olinefun.hxx create mode 100644 sc/source/ui/inc/olinewin.hxx create mode 100644 sc/source/ui/inc/opredlin.hxx create mode 100644 sc/source/ui/inc/optsolver.hxx create mode 100644 sc/source/ui/inc/output.hxx create mode 100644 sc/source/ui/inc/overlayobject.hxx create mode 100644 sc/source/ui/inc/pagedata.hxx create mode 100644 sc/source/ui/inc/pfiltdlg.hxx create mode 100644 sc/source/ui/inc/pfuncache.hxx create mode 100644 sc/source/ui/inc/pgbrksh.hxx create mode 100644 sc/source/ui/inc/pivotsh.hxx create mode 100644 sc/source/ui/inc/pntlock.hxx create mode 100644 sc/source/ui/inc/preview.hxx create mode 100644 sc/source/ui/inc/prevloc.hxx create mode 100644 sc/source/ui/inc/prevwsh.hxx create mode 100644 sc/source/ui/inc/printfun.hxx create mode 100644 sc/source/ui/inc/protectiondlg.hxx create mode 100644 sc/source/ui/inc/pvfundlg.hxx create mode 100644 sc/source/ui/inc/redcom.hxx create mode 100644 sc/source/ui/inc/reffact.hxx create mode 100644 sc/source/ui/inc/refundo.hxx create mode 100644 sc/source/ui/inc/retypepassdlg.hxx create mode 100644 sc/source/ui/inc/rfindlst.hxx create mode 100644 sc/source/ui/inc/scendlg.hxx create mode 100644 sc/source/ui/inc/scui_def.hxx create mode 100644 sc/source/ui/inc/scuiasciiopt.hxx create mode 100644 sc/source/ui/inc/scuiautofmt.hxx create mode 100644 sc/source/ui/inc/scuiimoptdlg.hxx create mode 100644 sc/source/ui/inc/scuitphfedit.hxx create mode 100644 sc/source/ui/inc/searchresults.hxx create mode 100644 sc/source/ui/inc/select.hxx create mode 100644 sc/source/ui/inc/selectionstate.hxx create mode 100644 sc/source/ui/inc/seltrans.hxx create mode 100644 sc/source/ui/inc/servobj.hxx create mode 100644 sc/source/ui/inc/sharedocdlg.hxx create mode 100644 sc/source/ui/inc/shtabdlg.hxx create mode 100644 sc/source/ui/inc/simpref.hxx create mode 100644 sc/source/ui/inc/sizedev.hxx create mode 100644 sc/source/ui/inc/solveroptions.hxx create mode 100644 sc/source/ui/inc/solverutil.hxx create mode 100644 sc/source/ui/inc/solvrdlg.hxx create mode 100644 sc/source/ui/inc/sortdlg.hxx create mode 100644 sc/source/ui/inc/sortkeydlg.hxx create mode 100644 sc/source/ui/inc/spelldialog.hxx create mode 100644 sc/source/ui/inc/spelleng.hxx create mode 100644 sc/source/ui/inc/spellparam.hxx create mode 100644 sc/source/ui/inc/strindlg.hxx create mode 100644 sc/source/ui/inc/styledlg.hxx create mode 100644 sc/source/ui/inc/subtdlg.hxx create mode 100644 sc/source/ui/inc/tabbgcolordlg.hxx create mode 100644 sc/source/ui/inc/tabcont.hxx create mode 100644 sc/source/ui/inc/tabopdlg.hxx create mode 100644 sc/source/ui/inc/tabpages.hxx create mode 100644 sc/source/ui/inc/tabsplit.hxx create mode 100644 sc/source/ui/inc/tabview.hxx create mode 100644 sc/source/ui/inc/tabvwsh.hxx create mode 100644 sc/source/ui/inc/target.hxx create mode 100644 sc/source/ui/inc/tbzoomsliderctrl.hxx create mode 100644 sc/source/ui/inc/textdlgs.hxx create mode 100644 sc/source/ui/inc/textimportoptions.hxx create mode 100644 sc/source/ui/inc/tpcalc.hxx create mode 100644 sc/source/ui/inc/tpcompatibility.hxx create mode 100644 sc/source/ui/inc/tpdefaults.hxx create mode 100644 sc/source/ui/inc/tpformula.hxx create mode 100644 sc/source/ui/inc/tphf.hxx create mode 100644 sc/source/ui/inc/tphfedit.hxx create mode 100644 sc/source/ui/inc/tpprint.hxx create mode 100644 sc/source/ui/inc/tpsort.hxx create mode 100644 sc/source/ui/inc/tpstat.hxx create mode 100644 sc/source/ui/inc/tpsubt.hxx create mode 100644 sc/source/ui/inc/tptable.hxx create mode 100644 sc/source/ui/inc/tpusrlst.hxx create mode 100644 sc/source/ui/inc/tpview.hxx create mode 100644 sc/source/ui/inc/transobj.hxx create mode 100644 sc/source/ui/inc/uiitems.hxx create mode 100644 sc/source/ui/inc/uiobject.hxx create mode 100644 sc/source/ui/inc/undo/UndoDeleteSparkline.hxx create mode 100644 sc/source/ui/inc/undo/UndoDeleteSparklineGroup.hxx create mode 100644 sc/source/ui/inc/undo/UndoEditSparkline.hxx create mode 100644 sc/source/ui/inc/undo/UndoEditSparklineGroup.hxx create mode 100644 sc/source/ui/inc/undo/UndoGroupSparklines.hxx create mode 100644 sc/source/ui/inc/undo/UndoInsertSparkline.hxx create mode 100644 sc/source/ui/inc/undo/UndoUngroupSparklines.hxx create mode 100644 sc/source/ui/inc/undobase.hxx create mode 100644 sc/source/ui/inc/undoblk.hxx create mode 100644 sc/source/ui/inc/undocell.hxx create mode 100644 sc/source/ui/inc/undoconvert.hxx create mode 100644 sc/source/ui/inc/undodat.hxx create mode 100644 sc/source/ui/inc/undodraw.hxx create mode 100644 sc/source/ui/inc/undoolk.hxx create mode 100644 sc/source/ui/inc/undosort.hxx create mode 100644 sc/source/ui/inc/undostyl.hxx create mode 100644 sc/source/ui/inc/undotab.hxx create mode 100644 sc/source/ui/inc/undoutil.hxx create mode 100644 sc/source/ui/inc/validate.hxx create mode 100644 sc/source/ui/inc/viewdata.hxx create mode 100644 sc/source/ui/inc/viewfunc.hxx create mode 100644 sc/source/ui/inc/viewutil.hxx create mode 100644 sc/source/ui/inc/warnbox.hxx create mode 100644 sc/source/ui/inc/xmlsourcedlg.hxx create mode 100644 sc/source/ui/miscdlgs/acredlin.cxx create mode 100644 sc/source/ui/miscdlgs/anyrefdg.cxx create mode 100644 sc/source/ui/miscdlgs/autofmt.cxx create mode 100644 sc/source/ui/miscdlgs/conflictsdlg.cxx create mode 100644 sc/source/ui/miscdlgs/crdlg.cxx create mode 100644 sc/source/ui/miscdlgs/crnrdlg.cxx create mode 100644 sc/source/ui/miscdlgs/datafdlg.cxx create mode 100644 sc/source/ui/miscdlgs/dataproviderdlg.cxx create mode 100644 sc/source/ui/miscdlgs/datastreamdlg.cxx create mode 100644 sc/source/ui/miscdlgs/datatableview.cxx create mode 100644 sc/source/ui/miscdlgs/delcldlg.cxx create mode 100644 sc/source/ui/miscdlgs/delcodlg.cxx create mode 100644 sc/source/ui/miscdlgs/filldlg.cxx create mode 100644 sc/source/ui/miscdlgs/gototabdlg.cxx create mode 100644 sc/source/ui/miscdlgs/groupdlg.cxx create mode 100644 sc/source/ui/miscdlgs/highred.cxx create mode 100644 sc/source/ui/miscdlgs/inscldlg.cxx create mode 100644 sc/source/ui/miscdlgs/inscodlg.cxx create mode 100644 sc/source/ui/miscdlgs/instbdlg.cxx create mode 100644 sc/source/ui/miscdlgs/lbseldlg.cxx create mode 100644 sc/source/ui/miscdlgs/linkarea.cxx create mode 100644 sc/source/ui/miscdlgs/mergecellsdialog.cxx create mode 100644 sc/source/ui/miscdlgs/mtrindlg.cxx create mode 100644 sc/source/ui/miscdlgs/mvtabdlg.cxx create mode 100644 sc/source/ui/miscdlgs/namecrea.cxx create mode 100644 sc/source/ui/miscdlgs/optsolver.cxx create mode 100644 sc/source/ui/miscdlgs/protectiondlg.cxx create mode 100644 sc/source/ui/miscdlgs/redcom.cxx create mode 100644 sc/source/ui/miscdlgs/retypepassdlg.cxx create mode 100644 sc/source/ui/miscdlgs/scuiautofmt.cxx create mode 100644 sc/source/ui/miscdlgs/sharedocdlg.cxx create mode 100644 sc/source/ui/miscdlgs/shtabdlg.cxx create mode 100644 sc/source/ui/miscdlgs/simpref.cxx create mode 100644 sc/source/ui/miscdlgs/solveroptions.cxx create mode 100644 sc/source/ui/miscdlgs/solverutil.cxx create mode 100644 sc/source/ui/miscdlgs/solvrdlg.cxx create mode 100644 sc/source/ui/miscdlgs/strindlg.cxx create mode 100644 sc/source/ui/miscdlgs/tabbgcolordlg.cxx create mode 100644 sc/source/ui/miscdlgs/tabopdlg.cxx create mode 100644 sc/source/ui/miscdlgs/textdlgs.cxx create mode 100644 sc/source/ui/miscdlgs/warnbox.cxx create mode 100644 sc/source/ui/namedlg/namedefdlg.cxx create mode 100644 sc/source/ui/namedlg/namedlg.cxx create mode 100644 sc/source/ui/namedlg/namemgrtable.cxx create mode 100644 sc/source/ui/namedlg/namepast.cxx create mode 100644 sc/source/ui/navipi/content.cxx create mode 100644 sc/source/ui/navipi/navcitem.cxx create mode 100644 sc/source/ui/navipi/navipi.cxx create mode 100644 sc/source/ui/navipi/scenwnd.cxx create mode 100644 sc/source/ui/optdlg/calcoptionsdlg.cxx create mode 100644 sc/source/ui/optdlg/calcoptionsdlg.hxx create mode 100644 sc/source/ui/optdlg/opredlin.cxx create mode 100644 sc/source/ui/optdlg/tpcalc.cxx create mode 100644 sc/source/ui/optdlg/tpcompatibility.cxx create mode 100644 sc/source/ui/optdlg/tpdefaults.cxx create mode 100644 sc/source/ui/optdlg/tpformula.cxx create mode 100644 sc/source/ui/optdlg/tpprint.cxx create mode 100644 sc/source/ui/optdlg/tpusrlst.cxx create mode 100644 sc/source/ui/optdlg/tpview.cxx create mode 100644 sc/source/ui/pagedlg/areasdlg.cxx create mode 100644 sc/source/ui/pagedlg/hfedtdlg.cxx create mode 100644 sc/source/ui/pagedlg/scuitphfedit.cxx create mode 100644 sc/source/ui/pagedlg/tphf.cxx create mode 100644 sc/source/ui/pagedlg/tphfedit.cxx create mode 100644 sc/source/ui/pagedlg/tptable.cxx create mode 100644 sc/source/ui/sidebar/AlignmentPropertyPanel.cxx create mode 100644 sc/source/ui/sidebar/AlignmentPropertyPanel.hxx create mode 100644 sc/source/ui/sidebar/CellAppearancePropertyPanel.cxx create mode 100644 sc/source/ui/sidebar/CellAppearancePropertyPanel.hxx create mode 100644 sc/source/ui/sidebar/CellBorderStyleControl.cxx create mode 100644 sc/source/ui/sidebar/CellBorderStyleControl.hxx create mode 100644 sc/source/ui/sidebar/CellLineStyleControl.cxx create mode 100644 sc/source/ui/sidebar/CellLineStyleControl.hxx create mode 100644 sc/source/ui/sidebar/CellLineStyleValueSet.cxx create mode 100644 sc/source/ui/sidebar/CellLineStyleValueSet.hxx create mode 100644 sc/source/ui/sidebar/NumberFormatControl.cxx create mode 100644 sc/source/ui/sidebar/NumberFormatPropertyPanel.cxx create mode 100644 sc/source/ui/sidebar/NumberFormatPropertyPanel.hxx create mode 100644 sc/source/ui/sidebar/ScPanelFactory.cxx create mode 100644 sc/source/ui/sidebar/ScPanelFactory.hxx create mode 100644 sc/source/ui/sparklines/SparklineAttributes.cxx create mode 100644 sc/source/ui/sparklines/SparklineData.cxx create mode 100644 sc/source/ui/sparklines/SparklineGroup.cxx create mode 100644 sc/source/ui/sparklines/SparklineList.cxx create mode 100644 sc/source/ui/styleui/styledlg.cxx create mode 100644 sc/source/ui/styleui/template.cur create mode 100644 sc/source/ui/uitest/uiobject.cxx create mode 100644 sc/source/ui/undo/UndoDeleteSparkline.cxx create mode 100644 sc/source/ui/undo/UndoDeleteSparklineGroup.cxx create mode 100644 sc/source/ui/undo/UndoEditSparkline.cxx create mode 100644 sc/source/ui/undo/UndoEditSparklineGroup.cxx create mode 100644 sc/source/ui/undo/UndoGroupSparklines.cxx create mode 100644 sc/source/ui/undo/UndoInsertSparkline.cxx create mode 100644 sc/source/ui/undo/UndoUngroupSparklines.cxx create mode 100644 sc/source/ui/undo/areasave.cxx create mode 100644 sc/source/ui/undo/refundo.cxx create mode 100644 sc/source/ui/undo/target.cxx create mode 100644 sc/source/ui/undo/undobase.cxx create mode 100644 sc/source/ui/undo/undoblk.cxx create mode 100644 sc/source/ui/undo/undoblk2.cxx create mode 100644 sc/source/ui/undo/undoblk3.cxx create mode 100644 sc/source/ui/undo/undocell.cxx create mode 100644 sc/source/ui/undo/undocell2.cxx create mode 100644 sc/source/ui/undo/undoconvert.cxx create mode 100644 sc/source/ui/undo/undodat.cxx create mode 100644 sc/source/ui/undo/undodraw.cxx create mode 100644 sc/source/ui/undo/undoolk.cxx create mode 100644 sc/source/ui/undo/undorangename.cxx create mode 100644 sc/source/ui/undo/undosort.cxx create mode 100644 sc/source/ui/undo/undostyl.cxx create mode 100644 sc/source/ui/undo/undotab.cxx create mode 100644 sc/source/ui/undo/undoutil.cxx create mode 100644 sc/source/ui/unoobj/ChartRangeSelectionListener.cxx create mode 100644 sc/source/ui/unoobj/ChartTools.cxx create mode 100644 sc/source/ui/unoobj/PivotTableDataProvider.cxx create mode 100644 sc/source/ui/unoobj/PivotTableDataSequence.cxx create mode 100644 sc/source/ui/unoobj/PivotTableDataSource.cxx create mode 100644 sc/source/ui/unoobj/TablePivotChart.cxx create mode 100644 sc/source/ui/unoobj/TablePivotCharts.cxx create mode 100644 sc/source/ui/unoobj/addruno.cxx create mode 100644 sc/source/ui/unoobj/afmtuno.cxx create mode 100644 sc/source/ui/unoobj/appluno.cxx create mode 100644 sc/source/ui/unoobj/celllistsource.cxx create mode 100644 sc/source/ui/unoobj/celllistsource.hxx create mode 100644 sc/source/ui/unoobj/cellsuno.cxx create mode 100644 sc/source/ui/unoobj/cellvaluebinding.cxx create mode 100644 sc/source/ui/unoobj/cellvaluebinding.hxx create mode 100644 sc/source/ui/unoobj/chart2uno.cxx create mode 100644 sc/source/ui/unoobj/chartuno.cxx create mode 100644 sc/source/ui/unoobj/condformatuno.cxx create mode 100644 sc/source/ui/unoobj/confuno.cxx create mode 100644 sc/source/ui/unoobj/convuno.cxx create mode 100644 sc/source/ui/unoobj/cursuno.cxx create mode 100644 sc/source/ui/unoobj/dapiuno.cxx create mode 100644 sc/source/ui/unoobj/datauno.cxx create mode 100644 sc/source/ui/unoobj/defltuno.cxx create mode 100644 sc/source/ui/unoobj/dispuno.cxx create mode 100644 sc/source/ui/unoobj/docuno.cxx create mode 100644 sc/source/ui/unoobj/drdefuno.cxx create mode 100644 sc/source/ui/unoobj/editsrc.cxx create mode 100644 sc/source/ui/unoobj/eventuno.cxx create mode 100644 sc/source/ui/unoobj/exceldetect.cxx create mode 100644 sc/source/ui/unoobj/exceldetect.hxx create mode 100644 sc/source/ui/unoobj/fielduno.cxx create mode 100644 sc/source/ui/unoobj/filtuno.cxx create mode 100644 sc/source/ui/unoobj/fmtuno.cxx create mode 100644 sc/source/ui/unoobj/forbiuno.cxx create mode 100644 sc/source/ui/unoobj/funcuno.cxx create mode 100644 sc/source/ui/unoobj/linkuno.cxx create mode 100644 sc/source/ui/unoobj/listenercalls.cxx create mode 100644 sc/source/ui/unoobj/miscuno.cxx create mode 100644 sc/source/ui/unoobj/nameuno.cxx create mode 100644 sc/source/ui/unoobj/notesuno.cxx create mode 100644 sc/source/ui/unoobj/optuno.cxx create mode 100644 sc/source/ui/unoobj/pageuno.cxx create mode 100644 sc/source/ui/unoobj/scdetect.cxx create mode 100644 sc/source/ui/unoobj/scdetect.hxx create mode 100644 sc/source/ui/unoobj/servuno.cxx create mode 100644 sc/source/ui/unoobj/shapeuno.cxx create mode 100644 sc/source/ui/unoobj/srchuno.cxx create mode 100644 sc/source/ui/unoobj/styleuno.cxx create mode 100644 sc/source/ui/unoobj/targuno.cxx create mode 100644 sc/source/ui/unoobj/textuno.cxx create mode 100644 sc/source/ui/unoobj/tokenuno.cxx create mode 100644 sc/source/ui/unoobj/unodoc.cxx create mode 100644 sc/source/ui/unoobj/unoreflist.cxx create mode 100644 sc/source/ui/unoobj/viewuno.cxx create mode 100644 sc/source/ui/unoobj/warnpassword.cxx create mode 100644 sc/source/ui/vba/excelvbahelper.cxx create mode 100644 sc/source/ui/vba/excelvbahelper.hxx create mode 100644 sc/source/ui/vba/helperdecl.hxx create mode 100644 sc/source/ui/vba/vbaapplication.cxx create mode 100644 sc/source/ui/vba/vbaapplication.hxx create mode 100644 sc/source/ui/vba/vbaassistant.cxx create mode 100644 sc/source/ui/vba/vbaassistant.hxx create mode 100644 sc/source/ui/vba/vbaaxes.cxx create mode 100644 sc/source/ui/vba/vbaaxes.hxx create mode 100644 sc/source/ui/vba/vbaaxis.cxx create mode 100644 sc/source/ui/vba/vbaaxis.hxx create mode 100644 sc/source/ui/vba/vbaaxistitle.cxx create mode 100644 sc/source/ui/vba/vbaaxistitle.hxx create mode 100644 sc/source/ui/vba/vbaborders.cxx create mode 100644 sc/source/ui/vba/vbaborders.hxx create mode 100644 sc/source/ui/vba/vbacharacters.cxx create mode 100644 sc/source/ui/vba/vbacharacters.hxx create mode 100644 sc/source/ui/vba/vbachart.cxx create mode 100644 sc/source/ui/vba/vbachart.hxx create mode 100644 sc/source/ui/vba/vbachartobject.cxx create mode 100644 sc/source/ui/vba/vbachartobject.hxx create mode 100644 sc/source/ui/vba/vbachartobjects.cxx create mode 100644 sc/source/ui/vba/vbachartobjects.hxx create mode 100644 sc/source/ui/vba/vbacharttitle.cxx create mode 100644 sc/source/ui/vba/vbacharttitle.hxx create mode 100644 sc/source/ui/vba/vbacomment.cxx create mode 100644 sc/source/ui/vba/vbacomment.hxx create mode 100644 sc/source/ui/vba/vbacomments.cxx create mode 100644 sc/source/ui/vba/vbacomments.hxx create mode 100644 sc/source/ui/vba/vbacondition.cxx create mode 100644 sc/source/ui/vba/vbacondition.hxx create mode 100644 sc/source/ui/vba/vbadialog.cxx create mode 100644 sc/source/ui/vba/vbadialog.hxx create mode 100644 sc/source/ui/vba/vbadialogs.cxx create mode 100644 sc/source/ui/vba/vbadialogs.hxx create mode 100644 sc/source/ui/vba/vbaeventshelper.cxx create mode 100644 sc/source/ui/vba/vbaeventshelper.hxx create mode 100644 sc/source/ui/vba/vbafiledialog.cxx create mode 100644 sc/source/ui/vba/vbafiledialog.hxx create mode 100644 sc/source/ui/vba/vbafiledialogitems.cxx create mode 100644 sc/source/ui/vba/vbafiledialogitems.hxx create mode 100644 sc/source/ui/vba/vbafont.cxx create mode 100644 sc/source/ui/vba/vbafont.hxx create mode 100644 sc/source/ui/vba/vbaformat.cxx create mode 100644 sc/source/ui/vba/vbaformat.hxx create mode 100644 sc/source/ui/vba/vbaformatcondition.cxx create mode 100644 sc/source/ui/vba/vbaformatcondition.hxx create mode 100644 sc/source/ui/vba/vbaformatconditions.cxx create mode 100644 sc/source/ui/vba/vbaformatconditions.hxx create mode 100644 sc/source/ui/vba/vbaglobals.cxx create mode 100644 sc/source/ui/vba/vbaglobals.hxx create mode 100644 sc/source/ui/vba/vbahyperlink.cxx create mode 100644 sc/source/ui/vba/vbahyperlink.hxx create mode 100644 sc/source/ui/vba/vbahyperlinks.cxx create mode 100644 sc/source/ui/vba/vbahyperlinks.hxx create mode 100644 sc/source/ui/vba/vbainterior.cxx create mode 100644 sc/source/ui/vba/vbainterior.hxx create mode 100644 sc/source/ui/vba/vbalineshape.cxx create mode 100644 sc/source/ui/vba/vbalineshape.hxx create mode 100644 sc/source/ui/vba/vbamenu.cxx create mode 100644 sc/source/ui/vba/vbamenu.hxx create mode 100644 sc/source/ui/vba/vbamenubar.cxx create mode 100644 sc/source/ui/vba/vbamenubar.hxx create mode 100644 sc/source/ui/vba/vbamenubars.cxx create mode 100644 sc/source/ui/vba/vbamenubars.hxx create mode 100644 sc/source/ui/vba/vbamenuitem.cxx create mode 100644 sc/source/ui/vba/vbamenuitem.hxx create mode 100644 sc/source/ui/vba/vbamenuitems.cxx create mode 100644 sc/source/ui/vba/vbamenuitems.hxx create mode 100644 sc/source/ui/vba/vbamenus.cxx create mode 100644 sc/source/ui/vba/vbamenus.hxx create mode 100644 sc/source/ui/vba/vbaname.cxx create mode 100644 sc/source/ui/vba/vbaname.hxx create mode 100644 sc/source/ui/vba/vbanames.cxx create mode 100644 sc/source/ui/vba/vbanames.hxx create mode 100644 sc/source/ui/vba/vbaoleobject.cxx create mode 100644 sc/source/ui/vba/vbaoleobject.hxx create mode 100644 sc/source/ui/vba/vbaoleobjects.cxx create mode 100644 sc/source/ui/vba/vbaoleobjects.hxx create mode 100644 sc/source/ui/vba/vbaoutline.cxx create mode 100644 sc/source/ui/vba/vbaoutline.hxx create mode 100644 sc/source/ui/vba/vbaovalshape.cxx create mode 100644 sc/source/ui/vba/vbaovalshape.hxx create mode 100644 sc/source/ui/vba/vbapagebreak.cxx create mode 100644 sc/source/ui/vba/vbapagebreak.hxx create mode 100644 sc/source/ui/vba/vbapagebreaks.cxx create mode 100644 sc/source/ui/vba/vbapagebreaks.hxx create mode 100644 sc/source/ui/vba/vbapagesetup.cxx create mode 100644 sc/source/ui/vba/vbapagesetup.hxx create mode 100644 sc/source/ui/vba/vbapalette.cxx create mode 100644 sc/source/ui/vba/vbapalette.hxx create mode 100644 sc/source/ui/vba/vbapane.cxx create mode 100644 sc/source/ui/vba/vbapane.hxx create mode 100644 sc/source/ui/vba/vbapivotcache.cxx create mode 100644 sc/source/ui/vba/vbapivotcache.hxx create mode 100644 sc/source/ui/vba/vbapivottable.cxx create mode 100644 sc/source/ui/vba/vbapivottable.hxx create mode 100644 sc/source/ui/vba/vbapivottables.cxx create mode 100644 sc/source/ui/vba/vbapivottables.hxx create mode 100644 sc/source/ui/vba/vbarange.cxx create mode 100644 sc/source/ui/vba/vbarange.hxx create mode 100644 sc/source/ui/vba/vbasheetobject.cxx create mode 100644 sc/source/ui/vba/vbasheetobject.hxx create mode 100644 sc/source/ui/vba/vbasheetobjects.cxx create mode 100644 sc/source/ui/vba/vbasheetobjects.hxx create mode 100644 sc/source/ui/vba/vbastyle.cxx create mode 100644 sc/source/ui/vba/vbastyle.hxx create mode 100644 sc/source/ui/vba/vbastyles.cxx create mode 100644 sc/source/ui/vba/vbastyles.hxx create mode 100644 sc/source/ui/vba/vbatextboxshape.cxx create mode 100644 sc/source/ui/vba/vbatextboxshape.hxx create mode 100644 sc/source/ui/vba/vbatextframe.cxx create mode 100644 sc/source/ui/vba/vbatextframe.hxx create mode 100644 sc/source/ui/vba/vbatitle.hxx create mode 100644 sc/source/ui/vba/vbavalidation.cxx create mode 100644 sc/source/ui/vba/vbavalidation.hxx create mode 100644 sc/source/ui/vba/vbawindow.cxx create mode 100644 sc/source/ui/vba/vbawindow.hxx create mode 100644 sc/source/ui/vba/vbawindows.cxx create mode 100644 sc/source/ui/vba/vbawindows.hxx create mode 100644 sc/source/ui/vba/vbaworkbook.cxx create mode 100644 sc/source/ui/vba/vbaworkbook.hxx create mode 100644 sc/source/ui/vba/vbaworkbooks.cxx create mode 100644 sc/source/ui/vba/vbaworkbooks.hxx create mode 100644 sc/source/ui/vba/vbaworksheet.cxx create mode 100644 sc/source/ui/vba/vbaworksheet.hxx create mode 100644 sc/source/ui/vba/vbaworksheets.cxx create mode 100644 sc/source/ui/vba/vbaworksheets.hxx create mode 100644 sc/source/ui/vba/vbawsfunction.cxx create mode 100644 sc/source/ui/vba/vbawsfunction.hxx create mode 100644 sc/source/ui/view/SparklineShell.cxx create mode 100644 sc/source/ui/view/auditsh.cxx create mode 100644 sc/source/ui/view/cellmergeoption.cxx create mode 100644 sc/source/ui/view/cellsh.cxx create mode 100644 sc/source/ui/view/cellsh1.cxx create mode 100644 sc/source/ui/view/cellsh2.cxx create mode 100644 sc/source/ui/view/cellsh3.cxx create mode 100644 sc/source/ui/view/cellsh4.cxx create mode 100644 sc/source/ui/view/cliputil.cxx create mode 100644 sc/source/ui/view/colrowba.cxx create mode 100644 sc/source/ui/view/dbfunc.cxx create mode 100644 sc/source/ui/view/dbfunc2.cxx create mode 100644 sc/source/ui/view/dbfunc3.cxx create mode 100644 sc/source/ui/view/dbfunc4.cxx create mode 100644 sc/source/ui/view/drawutil.cxx create mode 100644 sc/source/ui/view/drawvie3.cxx create mode 100644 sc/source/ui/view/drawvie4.cxx create mode 100644 sc/source/ui/view/drawview.cxx create mode 100644 sc/source/ui/view/editsh.cxx create mode 100644 sc/source/ui/view/formatsh.cxx create mode 100644 sc/source/ui/view/gridmerg.cxx create mode 100644 sc/source/ui/view/gridwin.cxx create mode 100644 sc/source/ui/view/gridwin2.cxx create mode 100644 sc/source/ui/view/gridwin3.cxx create mode 100644 sc/source/ui/view/gridwin4.cxx create mode 100644 sc/source/ui/view/gridwin5.cxx create mode 100644 sc/source/ui/view/gridwin_dbgutil.cxx create mode 100644 sc/source/ui/view/hdrcont.cxx create mode 100644 sc/source/ui/view/hintwin.cxx create mode 100644 sc/source/ui/view/imapwrap.cxx create mode 100644 sc/source/ui/view/imapwrap.hxx create mode 100644 sc/source/ui/view/invmerge.cxx create mode 100644 sc/source/ui/view/notemark.cxx create mode 100644 sc/source/ui/view/olinewin.cxx create mode 100644 sc/source/ui/view/output.cxx create mode 100644 sc/source/ui/view/output2.cxx create mode 100644 sc/source/ui/view/output3.cxx create mode 100644 sc/source/ui/view/overlayobject.cxx create mode 100644 sc/source/ui/view/pfuncache.cxx create mode 100644 sc/source/ui/view/pgbrksh.cxx create mode 100644 sc/source/ui/view/pivotsh.cxx create mode 100644 sc/source/ui/view/preview.cxx create mode 100644 sc/source/ui/view/prevloc.cxx create mode 100644 sc/source/ui/view/prevwsh.cxx create mode 100644 sc/source/ui/view/prevwsh2.cxx create mode 100644 sc/source/ui/view/printfun.cxx create mode 100644 sc/source/ui/view/reffact.cxx create mode 100644 sc/source/ui/view/scextopt.cxx create mode 100644 sc/source/ui/view/select.cxx create mode 100644 sc/source/ui/view/selectionstate.cxx create mode 100644 sc/source/ui/view/spellcheckcontext.cxx create mode 100644 sc/source/ui/view/spelldialog.cxx create mode 100644 sc/source/ui/view/spelleng.cxx create mode 100644 sc/source/ui/view/tabcont.cxx create mode 100644 sc/source/ui/view/tabsplit.cxx create mode 100644 sc/source/ui/view/tabview.cxx create mode 100644 sc/source/ui/view/tabview2.cxx create mode 100644 sc/source/ui/view/tabview3.cxx create mode 100644 sc/source/ui/view/tabview4.cxx create mode 100644 sc/source/ui/view/tabview5.cxx create mode 100644 sc/source/ui/view/tabvwsh.cxx create mode 100644 sc/source/ui/view/tabvwsh2.cxx create mode 100644 sc/source/ui/view/tabvwsh3.cxx create mode 100644 sc/source/ui/view/tabvwsh4.cxx create mode 100644 sc/source/ui/view/tabvwsh5.cxx create mode 100644 sc/source/ui/view/tabvwsh8.cxx create mode 100644 sc/source/ui/view/tabvwsh9.cxx create mode 100644 sc/source/ui/view/tabvwsha.cxx create mode 100644 sc/source/ui/view/tabvwshb.cxx create mode 100644 sc/source/ui/view/tabvwshc.cxx create mode 100644 sc/source/ui/view/tabvwshd.cxx create mode 100644 sc/source/ui/view/tabvwshe.cxx create mode 100644 sc/source/ui/view/tabvwshf.cxx create mode 100644 sc/source/ui/view/tabvwshg.cxx create mode 100644 sc/source/ui/view/tabvwshh.cxx create mode 100644 sc/source/ui/view/viewdata.cxx create mode 100644 sc/source/ui/view/viewfun2.cxx create mode 100644 sc/source/ui/view/viewfun3.cxx create mode 100644 sc/source/ui/view/viewfun4.cxx create mode 100644 sc/source/ui/view/viewfun5.cxx create mode 100644 sc/source/ui/view/viewfun6.cxx create mode 100644 sc/source/ui/view/viewfun7.cxx create mode 100644 sc/source/ui/view/viewfunc.cxx create mode 100644 sc/source/ui/view/viewutil.cxx create mode 100644 sc/source/ui/view/waitoff.cxx create mode 100644 sc/source/ui/xmlsource/xmlsourcedlg.cxx (limited to 'sc/source/ui') diff --git a/sc/source/ui/Accessibility/AccessibilityHints.cxx b/sc/source/ui/Accessibility/AccessibilityHints.cxx new file mode 100644 index 000000000..733311f72 --- /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 + +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 000000000..392eb4b9a --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleCell.cxx @@ -0,0 +1,614 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +rtl::Reference ScAccessibleCell::create( + const uno::Reference& rxParent, + ScTabViewShell* pViewShell, + const ScAddress& rCellAddress, + sal_Int32 nIndex, + ScSplitPos eSplitPos, + ScAccessibleDocument* pAccDoc) +{ + rtl::Reference x(new ScAccessibleCell( + rxParent, pViewShell, rCellAddress, nIndex, eSplitPos, pAccDoc)); + x->Init(); + return x; +} + +ScAccessibleCell::ScAccessibleCell( + const uno::Reference& rxParent, + ScTabViewShell* pViewShell, + const ScAddress& rCellAddress, + sal_Int32 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(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + { + xAccessibleComponent->grabFocus(); + mpViewShell->SetCursor(maCellAddress.Col(), maCellAddress.Row()); + } + } +} + +tools::Rectangle ScAccessibleCell::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aCellRect(GetBoundingBox()); + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); + if (pWindow) + { + tools::Rectangle aRect = pWindow->GetWindowExtentsRelative(nullptr); + 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_Int32 SAL_CALL + ScAccessibleCell::getAccessibleChildCount() +{ + return AccessibleStaticTextBase::getAccessibleChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL + ScAccessibleCell::getAccessibleChild(sal_Int32 nIndex) +{ + return AccessibleStaticTextBase::getAccessibleChild(nIndex); +} + +uno::Reference SAL_CALL + ScAccessibleCell::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + if (IsFocused()) + pStateSet->AddState(AccessibleStateType::FOCUSED); + + if (IsFormulaMode()) + { + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + if (IsOpaque()) + pStateSet->AddState(AccessibleStateType::OPAQUE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + if (IsSelected()) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::TRANSIENT); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + return pStateSet; + } + if (IsEditable(xParentStates)) + { + pStateSet->AddState(AccessibleStateType::EDITABLE); + pStateSet->AddState(AccessibleStateType::RESIZABLE); + } + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + if (IsOpaque()) + pStateSet->AddState(AccessibleStateType::OPAQUE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + if (IsSelected()) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::TRANSIENT); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +uno::Reference SAL_CALL + ScAccessibleCell::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + rtl::Reference 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 vals { "com.sun.star.sheet.AccessibleCell" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + + //==== internal ========================================================= + +bool ScAccessibleCell::IsDefunc( + const uno::Reference& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC)); +} + +bool ScAccessibleCell::IsEditable( + const uno::Reference& rxParentStates) +{ + bool bEditable(true); + if (rxParentStates.is() && !rxParentStates->contains(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(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(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.meType == CELLTYPE_FORMULA) + { + ScFormulaCell* pCell = aCell.mpFormula; + 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(rRange.aEnd.Col() - + rRange.aStart.Col() + 1) * (rRange.aEnd.Row() - + rRange.aStart.Row() + 1)); + 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(rRange.aEnd.Row()); ++nRow) + { + for (sal_uInt32 nCol = rRange.aStart.Col(); nCol <= sal::static_int_cast(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 ReplaceOneChar(const OUString& oldOUString, std::u16string_view replacedChar, std::u16string_view replaceStr) +{ + int iReplace = oldOUString.lastIndexOf(replacedChar); + OUString aRet = oldOUString; + while(iReplace > -1) + { + aRet = aRet.replaceAt(iReplace, 1, replaceStr); + iReplace = aRet.lastIndexOf(replacedChar, iReplace); + } + return aRet; +} + +static OUString ReplaceFourChar(const OUString& oldOUString) +{ + OUString aRet = ReplaceOneChar(oldOUString, u"\\", u"\\\\"); + aRet = ReplaceOneChar(aRet, u";", u"\\;"); + aRet = ReplaceOneChar(aRet, u"=", u"\\="); + aRet = ReplaceOneChar(aRet, u",", u"\\,"); + aRet = ReplaceOneChar(aRet, u":", u"\\:"); + return aRet; +} + +uno::Any SAL_CALL ScAccessibleCell::getExtendedAttributes() +{ + SolarMutexGuard aGuard; + + uno::Any strRet; + 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 += ";"; + } + strRet <<= strFor ; + } + return strRet; +} + +// 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(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+1IsScenario(nTab+1) && !mpDoc->IsScenario(nTab) ) + { + SCTAB i; + ScMarkData aMarks(mpDoc->GetSheetLimits()); + for (i=nTab+1; iIsScenario(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; iExtendTotalMerge( 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 000000000..03ecf65af --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleCellBase.cxx @@ -0,0 +1,590 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +#define DEFAULT_LINE_WIDTH 2 + +//===== internal ============================================================ + +ScAccessibleCellBase::ScAccessibleCellBase( + const uno::Reference& rxParent, + ScDocument* pDoc, + const ScAddress& rCellAddress, + sal_Int32 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) + { + SfxObjectShell* pObjSh = mpDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference xSheets = xSpreadDoc->getSheets(); + uno::Reference xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab()); + uno::Reference xTable; + if (aTable>>=xTable) + { + uno::Reference xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row()); + if (xCell.is()) + { + uno::Reference 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) + { + SfxObjectShell* pObjSh = mpDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference xSheets = xSpreadDoc->getSheets(); + uno::Reference xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab()); + uno::Reference xTable; + if (aTable>>=xTable) + { + uno::Reference xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row()); + if (xCell.is()) + { + uno::Reference 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_Int32 + 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()) + { + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + if (IsEditable(xParentStates)) + { + ScDocShell* pDocShell = static_cast(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_CALL + ScAccessibleCellBase::getImplementationId() +{ + return css::uno::Sequence(); +} + +bool ScAccessibleCellBase::IsEditable( + const uno::Reference& rxParentStates) +{ + bool bEditable(false); + if (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::EDITABLE)) + bEditable = true; + return bEditable; +} + +OUString ScAccessibleCellBase::GetNote() const +{ + SolarMutexGuard aGuard; + IsObjectValid(); + OUString sNote; + if (mpDoc) + { + SfxObjectShell* pObjSh = mpDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference xSheets = xSpreadDoc->getSheets(); + uno::Reference xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab()); + uno::Reference xTable; + if (aTable>>=xTable) + { + uno::Reference xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row()); + if (xCell.is()) + { + uno::Reference xAnnotationAnchor ( xCell, uno::UNO_QUERY); + if(xAnnotationAnchor.is()) + { + uno::Reference xSheetAnnotation = xAnnotationAnchor->getAnnotation(); + if (xSheetAnnotation.is()) + { + uno::Reference 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) + { + SfxObjectShell* pObjSh = mpDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference xSheets = xSpreadDoc->getSheets(); + uno::Reference xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab()); + uno::Reference xTable; + if (aTable>>=xTable) + { + uno::Reference xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row()); + if (xCell.is()) + { + uno::Reference 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(aShadowFmt.ShadowWidth) ) + + sInnerSplit + + "IsTransparent=" + + OUString::number( static_cast(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) + { + SfxObjectShell* pObjSh = mpDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference xSheets = xSpreadDoc->getSheets(); + uno::Reference xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab()); + uno::Reference xTable; + if (aTable>>=xTable) + { + uno::Reference xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row()); + if (xCell.is()) + { + uno::Reference 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(aTopBorder.InnerLineWidth) ) + + sInnerSplit + + "OuterLineWidth=" + + OUString::number( static_cast(aTopBorder.OuterLineWidth) ) + + sInnerSplit + + "LineDistance=" + + OUString::number( static_cast(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(aBottomBorder.InnerLineWidth) ) + + sInnerSplit + + "OuterLineWidth=" + + OUString::number( static_cast(aBottomBorder.OuterLineWidth) ) + + sInnerSplit + + "LineDistance=" + + OUString::number( static_cast(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(aLeftBorder.InnerLineWidth) ) + + sInnerSplit + + "OuterLineWidth=" + + OUString::number( static_cast(aLeftBorder.OuterLineWidth) ) + + sInnerSplit + + "LineDistance=" + + OUString::number( static_cast(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(aRightBorder.InnerLineWidth) ) + + sInnerSplit + + "OuterLineWidth=" + + OUString::number( static_cast(aRightBorder.OuterLineWidth) ) + + sInnerSplit + + "LineDistance=" + + OUString::number( static_cast(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 000000000..cdbb5ea71 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleContextBase.cxx @@ -0,0 +1,507 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +ScAccessibleContextBase::ScAccessibleContextBase( + const uno::Reference& rxParent, + const sal_Int16 aRole) + : + ScAccessibleContextBaseWeakImpl(m_aMutex), + mxParent(rxParent), + 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()) + xBroadcaster->addAccessibleEventListener(this); + } + 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.is()) + xBroadcaster->removeAccessibleEventListener(this); + mxParent = nullptr; + } + + ScAccessibleContextBaseWeakImpl::disposing(); +} + +//===== XInterface ===================================================== + +uno::Any SAL_CALL ScAccessibleContextBase::queryInterface( uno::Type const & rType ) +{ + uno::Any aAny (ScAccessibleContextBaseWeakImpl::queryInterface(rType)); + return aAny.hasValue() ? aAny : ScAccessibleContextBaseImplEvent::queryInterface(rType); +} + +void SAL_CALL ScAccessibleContextBase::acquire() + noexcept +{ + ScAccessibleContextBaseWeakImpl::acquire(); +} + +void SAL_CALL ScAccessibleContextBase::release() + noexcept +{ + ScAccessibleContextBaseWeakImpl::release(); +} + +//===== 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(); +} + +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 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_Int32 SAL_CALL ScAccessibleContextBase::getAccessibleChildCount() +{ + OSL_FAIL("should be implemented in the abrevated class"); + return 0; +} + +uno::Reference SAL_CALL + ScAccessibleContextBase::getAccessibleChild(sal_Int32 /* nIndex */) +{ + OSL_FAIL("should be implemented in the abrevated class"); + return uno::Reference(); +} + +uno::Reference SAL_CALL + ScAccessibleContextBase::getAccessibleParent() +{ + return mxParent; +} + +sal_Int32 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_Int32 nIndex(-1); + + // Iterate over all the parent's children and search for this object. + if (mxParent.is()) + { + uno::Reference xParentContext ( + mxParent->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for (sal_Int32 i=0; i 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 SAL_CALL + ScAccessibleContextBase::getAccessibleRelationSet() +{ + return new utl::AccessibleRelationSetHelper(); +} + +uno::Reference SAL_CALL + ScAccessibleContextBase::getAccessibleStateSet() +{ + return uno::Reference(); +} + +lang::Locale SAL_CALL + ScAccessibleContextBase::getLocale() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (mxParent.is()) + { + uno::Reference 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& 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& 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; + } +} + + //===== XAccessibleEventListener ======================================== + +void SAL_CALL ScAccessibleContextBase::disposing( + const lang::EventObject& rSource ) +{ + SolarMutexGuard aGuard; + if (rSource.Source == mxParent) + dispose(); +} + +void SAL_CALL ScAccessibleContextBase::notifyEvent( + const AccessibleEventObject& /* aEvent */ ) +{ +} + +// 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"}; +} + +//===== XTypeProvider ======================================================= + +uno::Sequence< uno::Type > SAL_CALL ScAccessibleContextBase::getTypes() +{ + return comphelper::concatSequences(ScAccessibleContextBaseWeakImpl::getTypes(), ScAccessibleContextBaseImplEvent::getTypes()); +} + +uno::Sequence SAL_CALL + ScAccessibleContextBase::getImplementationId() +{ + return css::uno::Sequence(); +} + +//===== 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(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(this)); + aEvent.OldValue <<= AccessibleStateType::FOCUSED; + + CommitChange(aEvent); +} + +tools::Rectangle ScAccessibleContextBase::GetBoundingBoxOnScreen() const +{ + OSL_FAIL("not implemented"); + return tools::Rectangle(); +} + +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 000000000..e9a4a1dee --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleCsvControl.cxx @@ -0,0 +1,1460 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::utl::AccessibleRelationSetHelper; +using ::utl::AccessibleStateSetHelper; +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; +} + +rtl::Reference ScAccessibleCsvControl::implCreateStateSet() +{ + SolarMutexGuard aGuard; + rtl::Reference pStateSet = new AccessibleStateSetHelper(); + if (isAlive()) + { + const ScCsvControl& rCtrl = implGetControl(); + pStateSet->AddState( AccessibleStateType::OPAQUE ); + if( rCtrl.IsEnabled() ) + pStateSet->AddState( AccessibleStateType::ENABLED ); + if( rCtrl.IsReallyVisible() ) + pStateSet->AddState( AccessibleStateType::SHOWING ); + if( rCtrl.IsVisible() ) + pStateSet->AddState( AccessibleStateType::VISIBLE ); + } + else + pStateSet->AddState( AccessibleStateType::DEFUNC ); + return pStateSet; +} + +// 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(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( 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) + : ScAccessibleCsvControl(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_Int32 SAL_CALL ScAccessibleCsvRuler::getAccessibleChildCount() +{ + ensureAlive(); + return 0; +} + +Reference< XAccessible > SAL_CALL ScAccessibleCsvRuler::getAccessibleChild( sal_Int32 /* nIndex */ ) +{ + ensureAlive(); + throw IndexOutOfBoundsException(); +} + +Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvRuler::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + ensureAlive(); + rtl::Reference pRelationSet = new AccessibleRelationSetHelper(); + + ScCsvRuler& rRuler = implGetRuler(); + ScCsvTableBox* pTableBox = rRuler.GetTableBox(); + ScCsvGrid& rGrid = pTableBox->GetGrid(); + + css::uno::Reference xAccObj(static_cast(rGrid.GetAccessible())); + if( xAccObj.is() ) + { + Sequence< Reference< XInterface > > aSeq{ xAccObj }; + pRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::CONTROLLER_FOR, aSeq ) ); + } + + return pRelationSet; +} + +Reference< XAccessibleStateSet > SAL_CALL ScAccessibleCsvRuler::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + rtl::Reference pStateSet = implCreateStateSet(); + if( isAlive() ) + { + pStateSet->AddState( AccessibleStateType::FOCUSABLE ); + pStateSet->AddState( AccessibleStateType::SINGLE_LINE ); + if( implGetRuler().HasFocus() ) + pStateSet->AddState( AccessibleStateType::FOCUSED ); + } + return pStateSet; +} + +// 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.getStr(), 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; + 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 ) + aResult = getTextAtIndex( nIndex - 1, 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 ) + aResult = getTextAtIndex( nIndex + 1, 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; +} + +// XInterface ----------------------------------------------------------------- + +Any SAL_CALL ScAccessibleCsvRuler::queryInterface( const css::uno::Type& rType ) +{ + Any aAny( ScAccessibleCsvRulerImpl::queryInterface( rType ) ); + return aAny.hasValue() ? aAny : ScAccessibleCsvControl::queryInterface( rType ); +} + +void SAL_CALL ScAccessibleCsvRuler::acquire() noexcept +{ + ScAccessibleCsvControl::acquire(); +} + +void SAL_CALL ScAccessibleCsvRuler::release() noexcept +{ + ScAccessibleCsvControl::release(); +} + +// XTypeProvider -------------------------------------------------------------- + +Sequence< css::uno::Type > SAL_CALL ScAccessibleCsvRuler::getTypes() +{ + return ::comphelper::concatSequences( ScAccessibleCsvControl::getTypes(), + Sequence { cppu::UnoType::get() }); +} + +Sequence< sal_Int8 > SAL_CALL ScAccessibleCsvRuler::getImplementationId() +{ + return css::uno::Sequence(); +} + +// 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 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) + : ScAccessibleCsvControl(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_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + ensureAlive(); + return implGetCellCount(); +} + +Reference ScAccessibleCsvGrid::getAccessibleCell(sal_Int32 nRow, sal_Int32 nColumn) +{ + sal_Int32 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 xNew = implCreateCellObj(nRow, nColumn); + maAccessibleChildren.insert(aI, XAccessibleSet::value_type(nIndex, xNew)); + return xNew; +} + +Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleChild( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + ensureAlive(); + ensureValidIndex( nIndex ); + + return getAccessibleCell(implGetRow(nIndex), implGetColumn(nIndex)); +} + +Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvGrid::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + ensureAlive(); + rtl::Reference pRelationSet = new AccessibleRelationSetHelper(); + + ScCsvGrid& rGrid = implGetGrid(); + ScCsvTableBox* pTableBox = rGrid.GetTableBox(); + ScCsvRuler& rRuler = pTableBox->GetRuler(); + + if (rRuler.IsVisible()) + { + css::uno::Reference xAccObj(static_cast(rRuler.GetAccessible())); + if( xAccObj.is() ) + { + Sequence< Reference< XInterface > > aSeq{ xAccObj }; + pRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::CONTROLLED_BY, aSeq ) ); + } + } + + return pRelationSet; +} + +Reference< XAccessibleStateSet > SAL_CALL ScAccessibleCsvGrid::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + rtl::Reference pStateSet = implCreateStateSet(); + if( isAlive() ) + { + pStateSet->AddState( AccessibleStateType::FOCUSABLE ); + pStateSet->AddState( AccessibleStateType::MULTI_SELECTABLE ); + pStateSet->AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + if( implGetGrid().HasFocus() ) + pStateSet->AddState( AccessibleStateType::FOCUSED ); + } + else + pStateSet->AddState( AccessibleStateType::DEFUNC ); + return pStateSet; +} + +// 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_Int32 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_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + ensureAlive(); + ensureValidIndex( nChildIndex ); + return implGetRow( nChildIndex ); +} + +sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumn( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + ensureAlive(); + ensureValidIndex( nChildIndex ); + return implGetColumn( nChildIndex ); +} + +// XAccessibleSelection ------------------------------------------------------- + +void SAL_CALL ScAccessibleCsvGrid::selectAccessibleChild( sal_Int32 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_Int32 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_Int32 SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleChildCount() +{ + SolarMutexGuard aGuard; + ensureAlive(); + return implGetRowCount() * implGetSelColumnCount(); +} + +Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleChild( sal_Int32 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_Int32 nSelectedChildIndex ) +{ + SolarMutexGuard aGuard; + ensureAlive(); + 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 ); +} + +// XInterface ----------------------------------------------------------------- + +Any SAL_CALL ScAccessibleCsvGrid::queryInterface( const css::uno::Type& rType ) +{ + Any aAny( ScAccessibleCsvGridImpl::queryInterface( rType ) ); + return aAny.hasValue() ? aAny : ScAccessibleCsvControl::queryInterface( rType ); +} + +void SAL_CALL ScAccessibleCsvGrid::acquire() noexcept +{ + ScAccessibleCsvControl::acquire(); +} + +void SAL_CALL ScAccessibleCsvGrid::release() noexcept +{ + ScAccessibleCsvControl::release(); +} + +// XTypeProvider -------------------------------------------------------------- + +Sequence< css::uno::Type > SAL_CALL ScAccessibleCsvGrid::getTypes() +{ + return ::comphelper::concatSequences( ScAccessibleCsvControl::getTypes(), + Sequence { + cppu::UnoType::get(), + cppu::UnoType::get() }); +} + +Sequence< sal_Int8 > SAL_CALL ScAccessibleCsvGrid::getImplementationId() +{ + return css::uno::Sequence(); +} + +// 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_Int32 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 ScAccessibleCsvGrid::implCreateCellObj( sal_Int32 nRow, sal_Int32 nColumn ) +{ + return new ScAccessibleCsvCell(implGetGrid(), implGetCellText(nRow, nColumn), nRow, nColumn); +} + +css::uno::Reference SAL_CALL ScAccessibleCsvGrid::getAccessibleParent() +{ + return implGetControl().GetDrawingArea()->get_accessible_parent(); +} + +ScAccessibleCsvCell::ScAccessibleCsvCell( + ScCsvGrid& rGrid, + const OUString& rCellText, + sal_Int32 nRow, sal_Int32 nColumn ) : + ScAccessibleCsvControl( rGrid ), + AccessibleStaticTextBase( SvxEditSourcePtr() ), + maCellText( rCellText ), + 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_Int32 SAL_CALL ScAccessibleCsvCell::getAccessibleChildCount() +{ + return AccessibleStaticTextBase::getAccessibleChildCount(); +} + +Reference< XAccessible > SAL_CALL ScAccessibleCsvCell::getAccessibleChild( sal_Int32 nIndex ) +{ + return AccessibleStaticTextBase::getAccessibleChild( nIndex ); +} + +sal_Int32 SAL_CALL ScAccessibleCsvCell::getAccessibleIndexInParent() +{ + SolarMutexGuard aGuard; + ensureAlive(); + return mnIndex; +} + +Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvCell::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + ensureAlive(); + return new AccessibleRelationSetHelper(); +} + +Reference< XAccessibleStateSet > SAL_CALL ScAccessibleCsvCell::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + rtl::Reference pStateSet = implCreateStateSet(); + if( isAlive() ) + { + const ScCsvGrid& rGrid = implGetGrid(); + pStateSet->AddState( AccessibleStateType::SINGLE_LINE ); + if( mnColumn != CSV_COLUMN_HEADER ) + pStateSet->AddState( AccessibleStateType::SELECTABLE ); + if( rGrid.HasFocus() && (rGrid.GetFocusColumn() == mnColumn) && (mnLine == CSV_LINE_HEADER) ) + pStateSet->AddState( AccessibleStateType::ACTIVE ); + if( rGrid.IsSelected( mnColumn ) ) + pStateSet->AddState( AccessibleStateType::SELECTED ); + } + return pStateSet; +} + +// XInterface ----------------------------------------------------------------- + +IMPLEMENT_FORWARD_XINTERFACE2( ScAccessibleCsvCell, ScAccessibleCsvControl, AccessibleStaticTextBase ) + +// XTypeProvider -------------------------------------------------------------- + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( ScAccessibleCsvCell, ScAccessibleCsvControl, 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(&rGrid.GetDrawingArea()->get_ref_device(), rGrid.GetEditEngine(), maCellText, implGetRealSize()) ) ); + return pEditSource; +} + +css::uno::Reference SAL_CALL ScAccessibleCsvCell::getAccessibleParent() +{ + ScCsvGrid& rGrid = implGetGrid(); + + ScAccessibleCsvGrid* pAcc = static_cast(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 000000000..52b5a6c53 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleDocument.cxx @@ -0,0 +1,2224 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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 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 mxLayerID; + std::optional mxZOrder; +}; + +} + +ScAccessibleShapeData::ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_) + : xShape(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& 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& 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 GetRelationSet(const ScAddress* pAddress) const; + + void VisAreaChanged() const; +private: + typedef std::vector SortedShapes; + typedef std::unordered_map, 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 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& xShapes) const; + + std::optional GetAnchor(const uno::Reference& xShape) const; + uno::Reference GetRelationSet(const ScAccessibleShapeData* pData) const; + void SetAnchor(const uno::Reference& xShape, ScAccessibleShapeData* pData) const; + void AddShape(const uno::Reference& xShape, bool bCommitChange) const; + void RemoveShape(const uno::Reference& xShape) const; + + bool FindShape(const uno::Reference& 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* pViewFrame = mpViewShell->GetViewFrame(); + if (pViewFrame) + { + xSelectionSupplier = uno::Reference(pViewFrame->GetFrame().GetController(), uno::UNO_QUERY); + if (xSelectionSupplier.is()) + { + xSelectionSupplier->addSelectionChangeListener(mpAccessibleDocument); + uno::Reference 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 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(&rHint); + + SdrObject* pObj = const_cast(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 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 xShape (pObj->getUnoShape(), uno::UNO_QUERY); + if (xShape.is()) + AddShape(xShape, true); + } + break; + case SdrHintKind::ObjectRemoved : // Removed drawing object from list + { + uno::Reference 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(pCurrentChild); + + 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(pReplacement); + + 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 (size_t i = 0; i < nSdrObjCount; ++i) + { + SdrObject* pObj = pDrawPage->GetObj(i); + if (pObj/* && (pObj->GetLayer() != SC_LAYER_INTERN)*/) + { + 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(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; + 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& 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 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 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 xShape; + if (IsSelected(nIndex, xShape) || !maZOrderedShapes[nIndex]->bSelectable) + return; + + uno::Reference 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 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 xShapes(mpViewShell->getSelectedXShapes()); + if (xShapes.is()) + { + sal_uInt32 nCount(xShapes->getCount()); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + uno::Reference 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 xShape; + if (!IsSelected(nChildIndex, xShape)) // returns false if it is the sheet + return; + + if (!xShape.is()) + return; + + uno::Reference 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(static_cast(nTab))); + } + } + return pDrawPage; +} + +rtl::Reference ScChildrenShapes::GetRelationSet(const ScAddress* pAddress) const +{ + rtl::Reference 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& 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(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(this)->GetAccessibleCaption( xUpGroupXShape ); + if( xAccGroupShape.is() ) + { + ::accessibility::AccessibleShape* pAccGroupShape = + static_cast< ::accessibility::AccessibleShape* >(xAccGroupShape.get()); + if( pAccGroupShape ) + { + sal_Int32 nCount = pAccGroupShape->getAccessibleChildCount(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference 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 ScChildrenShapes::GetAnchor(const uno::Reference& xShape) const +{ + if (mpViewShell) + { + SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape); + uno::Reference xShapeProp(xShape, uno::UNO_QUERY); + if (pSdrObj && xShapeProp.is()) + { + if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pSdrObj)) + return std::optional(pAnchor->maStart); + } + } + + return std::optional(); +} + +uno::Reference ScChildrenShapes::GetRelationSet(const ScAccessibleShapeData* pData) const +{ + rtl::Reference pRelationSet = new utl::AccessibleRelationSetHelper(); + + if (pData && mpAccessibleDocument) + { + uno::Reference 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 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& xShape, ScAccessibleShapeData* pData) const +{ + if (pData) + { + std::optional 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& 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 xShapes(mpViewShell->getSelectedXShapes()); + uno::Reference xEnumAcc(xShapes, uno::UNO_QUERY); + if (xEnumAcc.is()) + { + uno::Reference xEnum = xEnumAcc->createEnumeration(); + if (xEnum.is()) + { + uno::Reference 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); + + mpAccessibleDocument->CommitChange(aEvent); // new child - event + } +} + +void ScChildrenShapes::RemoveShape(const uno::Reference& xShape) const +{ + if (mbShapesNeedSorting) + { + std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess()); + mbShapesNeedSorting = false; + } + SortedShapes::iterator aItr; + if (FindShape(xShape, aItr)) + { + if (mpAccessibleDocument) + { + uno::Reference 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; + + 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& 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& 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 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(&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(&rHint) ) + { + if (pFocusGotHint->GetNewGridWin() == meSplitPos) + { + uno::Reference 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() ); + } + 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 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; + 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(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_Int32 SAL_CALL + ScAccessibleDocument::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + sal_Int32 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 SAL_CALL + ScAccessibleDocument::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Reference xAccessible; + if (nIndex >= 0) + { + sal_Int32 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. +uno::Reference SAL_CALL + ScAccessibleDocument::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::EDITABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +OUString SAL_CALL + ScAccessibleDocument::getAccessibleName() +{ + SolarMutexGuard g; + + OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET); + ScDocument* pScDoc = GetDocument(); + if (!pScDoc) + return aName; + + SfxObjectShell* 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_Int32 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_Int32 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 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_Int32 SAL_CALL + ScAccessibleDocument::getSelectedAccessibleChildCount( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + sal_Int32 nCount(0); + + if (mpChildrenShapes) + nCount = mpChildrenShapes->GetSelectedCount(); + + if (IsTableSelected()) + ++nCount; + + if (mxTempAcc.is()) + ++nCount; + + return nCount; +} + +uno::Reference SAL_CALL + ScAccessibleDocument::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Reference xAccessible; + if (mpChildrenShapes) + { + sal_Int32 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_Int32 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 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_CALL + ScAccessibleDocument::getImplementationId() +{ + return css::uno::Sequence(); +} + +///===== 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(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(mpViewShell->GetWindowByPos(meSplitPos)); + if (pWin) + { + aPoint = pWin->LogicToPixel(rPoint, pWin->GetDrawMapMode()); + aPoint += pWin->GetWindowExtentsRelative(nullptr).TopLeft(); + } + return aPoint; +} + +Size ScAccessibleDocument::LogicToPixel (const Size& rSize) const +{ + SolarMutexGuard aGuard; + IsObjectValid(); + Size aSize; + ScGridWindow* pWin = static_cast(mpViewShell->GetWindowByPos(meSplitPos)); + if (pWin) + aSize = pWin->LogicToPixel(rSize, pWin->GetDrawMapMode()); + return aSize; +} + + //===== internal ======================================================== + +rtl::Reference ScAccessibleDocument::GetRelationSet(const ScAddress* pAddress) const +{ + rtl::Reference 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; +} + +tools::Rectangle ScAccessibleDocument::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aRect; + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); + if (pWindow) + aRect = pWindow->GetWindowExtentsRelative(nullptr); + } + 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( + const uno::Reference& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC)); +} + +void ScAccessibleDocument::AddChild(const uno::Reference& 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(this); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.NewValue <<= mxTempAcc; + CommitChange( aEvent ); + } + } +} + +void ScAccessibleDocument::RemoveChild(const uno::Reference& 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(this); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.OldValue <<= mxTempAcc; + 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 000000000..78da26006 --- /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 + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ======================================================== + +ScAccessibleDocumentBase::ScAccessibleDocumentBase(const uno::Reference& 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 000000000..9656b82af --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx @@ -0,0 +1,1567 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 GetChild(sal_Int32 nIndex) const; + uno::Reference GetAt(const awt::Point& rPoint) const; + + void DataChanged(const tools::Rectangle& rVisRect); + +private: + ScPreviewShell* mpViewShell; + ScAccessibleDocumentPagePreview* mpAccDoc; + typedef std::vector 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(std::make_unique(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 ScNotesChildren::GetChild(sal_Int32 nIndex) const +{ + uno::Reference xAccessible; + + if (nIndex < mnParagraphs) + { + if (nIndex < static_cast(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 ScNotesChildren::GetAt(const awt::Point& rPoint) const +{ + uno::Reference 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) const + { + if (mpAccDoc) + { + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.Source = uno::Reference< XAccessibleContext >(mpAccDoc); + aEvent.OldValue <<= xAccessible; + + mpAccDoc->CommitChange(aEvent); // gone child - event + } + } +}; + +struct ScChildNew +{ + ScAccessibleDocumentPagePreview* mpAccDoc; + explicit ScChildNew(ScAccessibleDocumentPagePreview* pAccDoc) : mpAccDoc(pAccDoc) {} + void operator() (const uno::Reference& xAccessible) const + { + if (mpAccDoc) + { + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.Source = uno::Reference< XAccessibleContext >(mpAccDoc); + aEvent.NewValue <<= xAccessible; + + 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 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 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 GetBackShape(sal_Int32 nIndex) const; + sal_Int32 GetForeShapeCount() const; + uno::Reference GetForeShape(sal_Int32 nIndex) const; + sal_Int32 GetControlCount() const; + uno::Reference GetControl(sal_Int32 nIndex) const; + uno::Reference GetForegroundShapeAt(const awt::Point& rPoint) const; // inclusive controls + uno::Reference 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& xShape, SdrLayerID aLayerID); +// void RemoveShape(const uno::Reference& 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 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 (mpAccDoc); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.NewValue <<= xAcc; + mpAccDoc->CommitChange(aEvent); + ++aNewItr; + } + else + { + xAcc = GetAccShape(*aOldItr); + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference (mpAccDoc); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.OldValue <<= xAcc; + mpAccDoc->CommitChange(aEvent); + ++aOldItr; + } + } + while (aOldItr != aOldEnd) + { + xAcc = GetAccShape(*aOldItr); + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference (mpAccDoc); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.OldValue <<= xAcc; + mpAccDoc->CommitChange(aEvent); + ++aOldItr; + } + while (aNewItr != aNewEnd) + { + xAcc = GetAccShape(*aNewItr); + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference (mpAccDoc); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.NewValue <<= xAcc; + 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 ScShapeChildren::GetBackShape(sal_Int32 nIndex) const +{ + uno::Reference 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 ScShapeChildren::GetForeShape(sal_Int32 nIndex) const +{ + uno::Reference 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 ScShapeChildren::GetControl(sal_Int32 nIndex) const +{ + uno::Reference 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 ScShapeChildren::GetForegroundShapeAt(const awt::Point& rPoint) const //inclusive Controls +{ + uno::Reference 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 ScShapeChildren::GetBackgroundShapeAt(const awt::Point& rPoint) const +{ + uno::Reference 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; + const size_t nCount(pPage->GetObjCount()); + for (size_t i = 0; i < nCount; ++i) + { + SdrObject* pObj = pPage->GetObj(i); + if (pObj) + { + uno::Reference< drawing::XShape > xShape(pObj->getUnoShape(), uno::UNO_QUERY); + if (xShape.is()) + { + tools::Rectangle aRect(pWin->LogicToPixel(VCLPoint(xShape->getPosition()), aMapMode), pWin->LogicToPixel(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(static_cast(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& 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(&rHint) ) + { + CommitFocusLost(); + } + else if ( dynamic_cast(&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 xAcc = mpTable; + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + aEvent.OldValue <<= xAcc; + 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 xAcc = mpTable; + AccessibleEventObject aEvent; + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.Source = uno::Reference< XAccessibleContext >(this); + aEvent.NewValue <<= xAcc; + 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; + 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(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + { + // just grab the focus for the window + xAccessibleComponent->grabFocus(); + } + } +} + +//===== XAccessibleContext ============================================== + +sal_Int32 SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + tools::Long nRet = 0; + if ( mpViewShell ) + { + ScPagePreviewCountData aCount( mpViewShell->GetLocationData(), mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() ); + nRet = aCount.GetTotal(); + } + + return nRet; +} + +uno::Reference SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + uno::Reference 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_Int32 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. +uno::Reference SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + // never editable + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + + //===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessibleDocumentPagePreview::getImplementationName() +{ + return "ScAccessibleDocumentPagePreview"; +} + +uno::Sequence< OUString> SAL_CALL ScAccessibleDocumentPagePreview::getSupportedServiceNames() +{ + const css::uno::Sequence vals { "com.sun.star.AccessibleSpreadsheetPageView" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + +//===== XTypeProvider ======================================================= + +uno::Sequence SAL_CALL + ScAccessibleDocumentPagePreview::getImplementationId() +{ + return css::uno::Sequence(); +} + +//===== internal ======================================================== + +OUString ScAccessibleDocumentPagePreview::createAccessibleDescription() +{ + return STR_ACC_PREVIEWDOC_DESCR; +} + +OUString ScAccessibleDocumentPagePreview::createAccessibleName() +{ + OUString sName = ScResId(STR_ACC_PREVIEWDOC_NAME); + return sName; +} + +tools::Rectangle ScAccessibleDocumentPagePreview::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aRect; + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindow(); + if (pWindow) + aRect = pWindow->GetWindowExtentsRelative(nullptr); + } + 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( + const uno::Reference& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(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(); + + SfxObjectShell* 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 000000000..8412b313c --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleEditObject.cxx @@ -0,0 +1,604 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using ::com::sun::star::lang::IndexOutOfBoundsException; +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +ScAccessibleEditObject::ScAccessibleEditObject( + const uno::Reference& 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& 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(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 xRet; + if (containsPoint(rPoint)) + { + SolarMutexGuard aGuard; + IsObjectValid(); + + CreateTextHelper(); + + xRet = mpTextHelper->GetAt(rPoint); + } + + return xRet; +} + +tools::Rectangle ScAccessibleEditObject::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aScreenBounds; + + if ( mpWindow ) + { + if ( meObjectType == CellInEditMode ) + { + if ( mpEditView && mpEditView->GetEditEngine() ) + { + MapMode aMapMode( mpEditView->GetEditEngine()->GetRefMapMode() ); + aScreenBounds = mpWindow->LogicToPixel( mpEditView->GetOutputArea(), aMapMode ); + Point aCellLoc = aScreenBounds.TopLeft(); + tools::Rectangle aWindowRect = mpWindow->GetWindowExtentsRelative( nullptr ); + Point aWindowLoc = aWindowRect.TopLeft(); + Point aPos( aCellLoc.getX() + aWindowLoc.getX(), aCellLoc.getY() + aWindowLoc.getY() ); + aScreenBounds.SetPos( aPos ); + } + } + else + { + aScreenBounds = mpWindow->GetWindowExtentsRelative( nullptr ); + } + } + + 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_Int32 SAL_CALL + ScAccessibleEditObject::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + CreateTextHelper(); + return mpTextHelper->GetChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL + ScAccessibleEditObject::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + CreateTextHelper(); + return mpTextHelper->GetChild(nIndex); +} + +uno::Reference SAL_CALL + ScAccessibleEditObject::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + // all states are const, because this object exists only in one state + pStateSet->AddState(AccessibleStateType::EDITABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::SENSITIVE); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +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& xListener) +{ + CreateTextHelper(); + + mpTextHelper->AddEventListener(xListener); + + ScAccessibleContextBase::addAccessibleEventListener(xListener); +} + +void SAL_CALL + ScAccessibleEditObject::removeAccessibleEventListener(const uno::Reference& xListener) +{ + CreateTextHelper(); + + mpTextHelper->RemoveEventListener(xListener); + + ScAccessibleContextBase::removeAccessibleEventListener(xListener); +} + + //===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessibleEditObject::getImplementationName() +{ + return "ScAccessibleEditObject"; +} + +//===== XTypeProvider ======================================================= + +uno::Sequence SAL_CALL + ScAccessibleEditObject::getImplementationId() +{ + return css::uno::Sequence(); +} + + //==== internal ========================================================= + +bool ScAccessibleEditObject::IsDefunc( + const uno::Reference& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(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 pEditSrc = + std::make_unique(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) + { + SfxObjectShell* pObjSh = m_pScDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference xSheets = xSpreadDoc->getSheets(); + uno::Reference xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(m_curCellAddress.Tab()); + uno::Reference xTable; + if (aTable>>=xTable) + { + uno::Reference xCell = xTable->getCellByPosition(m_curCellAddress.Col(), m_curCellAddress.Row()); + if (xCell.is()) + { + uno::Reference 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_Int32 ) +{ +} + +sal_Bool SAL_CALL ScAccessibleEditObject::isAccessibleChildSelected( sal_Int32 nChildIndex ) +{ + uno::Reference xAcc = getAccessibleChild( nChildIndex ); + uno::Reference xContext; + if( xAcc.is() ) + xContext = xAcc->getAccessibleContext(); + if( xContext.is() ) + { + if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH ) + { + uno::Reference< css::accessibility::XAccessibleText > + xText(xAcc, uno::UNO_QUERY); + if( xText.is() ) + { + if( xText->getSelectionStart() >= 0 ) return true; + } + } + } + return false; +} + +void SAL_CALL ScAccessibleEditObject::clearAccessibleSelection( ) +{ +} + +void SAL_CALL ScAccessibleEditObject::selectAllAccessibleChildren( ) +{ +} + +sal_Int32 SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChildCount() +{ + sal_Int32 nCount = 0; + sal_Int32 TotalCount = getAccessibleChildCount(); + for( sal_Int32 i = 0; i < TotalCount; i++ ) + if( isAccessibleChildSelected(i) ) nCount++; + return nCount; +} + +uno::Reference SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + if ( nSelectedChildIndex > getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + sal_Int32 i1, i2; + for( i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ ) + if( isAccessibleChildSelected(i1) ) + { + if( i2 == nSelectedChildIndex ) + return getAccessibleChild( i1 ); + i2++; + } + return uno::Reference(); +} + +void SAL_CALL ScAccessibleEditObject::deselectAccessibleChild( + sal_Int32 ) +{ +} + +uno::Reference< XAccessibleRelationSet > ScAccessibleEditObject::getAccessibleRelationSet( ) +{ + SolarMutexGuard aGuard; + vcl::Window* pWindow = mpWindow; + rtl::Reference 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 >(); +} + +tools::Rectangle ScAccessibleEditControlObject::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aScreenBounds; + + if (m_pController && m_pController->GetDrawingArea()) + { + aScreenBounds = tools::Rectangle(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(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 000000000..a95093203 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessiblePageHeader.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +const sal_uInt8 MAX_AREAS = 3; + +ScAccessiblePageHeader::ScAccessiblePageHeader( const css::uno::Reference& 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()), + 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> 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(aOldAreas[i]); + + 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(maAreas[i]); + + 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 xRet; + + if (containsPoint(aPoint)) + { + SolarMutexGuard aGuard; + IsObjectValid(); + + sal_Int32 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(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + xAccessibleComponent->grabFocus(); + } +} + +//===== XAccessibleContext ============================================== + +sal_Int32 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(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_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + uno::Reference 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_Int32 SAL_CALL ScAccessiblePageHeader::getAccessibleIndexInParent() +{ + return mnIndex; +} + +uno::Reference< XAccessibleStateSet > SAL_CALL ScAccessiblePageHeader::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +//===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessiblePageHeader::getImplementationName() +{ + return "ScAccessiblePageHeader"; +} + +uno::Sequence SAL_CALL ScAccessiblePageHeader::getSupportedServiceNames() +{ + const css::uno::Sequence vals { "com.sun.star.text.AccessibleHeaderFooterView" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + +//==== internal ========================================================= + +OUString ScAccessiblePageHeader::createAccessibleDescription() +{ + OUString sDesc(mbHeader ? OUString(STR_ACC_HEADER_DESCR) : OUString(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)); +} + +tools::Rectangle ScAccessiblePageHeader::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aCellRect(GetBoundingBox()); + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindow(); + if (pWindow) + { + tools::Rectangle aRect = pWindow->GetWindowExtentsRelative(nullptr); + aCellRect.Move(aRect.Left(), aRect.Top()); + } + } + return 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( const uno::Reference& rxParentStates ) +{ + return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(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 000000000..9ea60780b --- /dev/null +++ b/sc/source/ui/Accessibility/AccessiblePageHeaderArea.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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + + //===== internal ======================================================== + +ScAccessiblePageHeaderArea::ScAccessiblePageHeaderArea( + const uno::Reference& 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 xRet; + if (containsPoint(rPoint)) + { + SolarMutexGuard aGuard; + IsObjectValid(); + + if(!mpTextHelper) + CreateTextHelper(); + + xRet = mpTextHelper->GetAt(rPoint); + } + + return xRet; +} + + //===== XAccessibleContext ============================================== + +sal_Int32 SAL_CALL + ScAccessiblePageHeaderArea::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mpTextHelper) + CreateTextHelper(); + return mpTextHelper->GetChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL + ScAccessiblePageHeaderArea::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mpTextHelper) + CreateTextHelper(); + return mpTextHelper->GetChild(nIndex); +} + +uno::Reference SAL_CALL + ScAccessiblePageHeaderArea::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc()) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +// XServiceInfo + +OUString SAL_CALL + ScAccessiblePageHeaderArea::getImplementationName() +{ + return "ScAccessiblePageHeaderArea"; +} + +uno::Sequence< OUString> SAL_CALL + ScAccessiblePageHeaderArea::getSupportedServiceNames() +{ + const css::uno::Sequence vals { "com.sun.star.sheet.AccessiblePageHeaderFooterAreasView" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + +//===== XTypeProvider ======================================================= + +uno::Sequence SAL_CALL + ScAccessiblePageHeaderArea::getImplementationId() +{ + return css::uno::Sequence(); +} + +//===== 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; +} + +tools::Rectangle ScAccessiblePageHeaderArea::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aRect; + if (mxParent.is()) + { + uno::Reference xContext = mxParent->getAccessibleContext(); + uno::Reference xComp(xContext, uno::UNO_QUERY); + if (xComp.is()) + { + // has the same size and position on screen like the parent + aRect = tools::Rectangle(VCLPoint(xComp->getLocationOnScreen()), VCLRectangle(xComp->getBounds()).GetSize()); + } + } + return aRect; +} + +tools::Rectangle ScAccessiblePageHeaderArea::GetBoundingBox() const +{ + tools::Rectangle aRect; + if (mxParent.is()) + { + uno::Reference xContext = mxParent->getAccessibleContext(); + uno::Reference 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( + std::make_unique( + 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 000000000..f3bc3e717 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessiblePreviewCell.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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +ScAccessiblePreviewCell::ScAccessiblePreviewCell( const css::uno::Reference& 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 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(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + xAccessibleComponent->grabFocus(); + } +} + +//===== XAccessibleContext ============================================== + +sal_Int32 SAL_CALL ScAccessiblePreviewCell::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mpTextHelper) + CreateTextHelper(); + return mpTextHelper->GetChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewCell::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mpTextHelper) + CreateTextHelper(); + return mpTextHelper->GetChild(nIndex); +} + +uno::Reference SAL_CALL ScAccessiblePreviewCell::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + if (IsOpaque()) + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::TRANSIENT); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + // MANAGES_DESCENDANTS (for paragraphs) + pStateSet->AddState(AccessibleStateType::MANAGES_DESCENDANTS); + } + return pStateSet; +} + +//===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessiblePreviewCell::getImplementationName() +{ + return "ScAccessiblePreviewCell"; +} + +uno::Sequence SAL_CALL ScAccessiblePreviewCell::getSupportedServiceNames() +{ + const css::uno::Sequence vals { "com.sun.star.table.AccessibleCellView" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + +//===== XTypeProvider ======================================================= + +uno::Sequence SAL_CALL + ScAccessiblePreviewCell::getImplementationId() +{ + return css::uno::Sequence(); +} + +//==== internal ========================================================= + +tools::Rectangle ScAccessiblePreviewCell::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aCellRect; + if (mpViewShell) + { + mpViewShell->GetLocationData().GetCellPosition( maCellAddress, aCellRect ); + vcl::Window* pWindow = mpViewShell->GetWindow(); + if (pWindow) + { + tools::Rectangle aRect = pWindow->GetWindowExtentsRelative(nullptr); + aCellRect.Move(aRect.Left(), aRect.Top()); + } + } + return aCellRect; +} + +tools::Rectangle ScAccessiblePreviewCell::GetBoundingBox() const +{ + tools::Rectangle aCellRect; + if (mpViewShell) + { + mpViewShell->GetLocationData().GetCellPosition( maCellAddress, aCellRect ); + uno::Reference xAccParent = const_cast(this)->getAccessibleParent(); + if (xAccParent.is()) + { + uno::Reference xAccParentContext = xAccParent->getAccessibleContext(); + uno::Reference 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( + const uno::Reference& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC)); +} + +bool ScAccessiblePreviewCell::IsEditable( + const uno::Reference& /* rxParentStates */) +{ + 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( + std::make_unique( + mpViewShell, maCellAddress))) ); + mpTextHelper->SetEventSource( this ); + + // paragraphs in preview are transient + ::accessibility::AccessibleTextHelper::VectorOfStates aChildStates; + aChildStates.push_back( AccessibleStateType::TRANSIENT ); + mpTextHelper->SetAdditionalChildStates( std::move(aChildStates) ); +} + +/* 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 000000000..1bda4744a --- /dev/null +++ b/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx @@ -0,0 +1,404 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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& 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 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(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + xAccessibleComponent->grabFocus(); + } +} + +//===== XAccessibleContext ============================================== + +sal_Int32 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mxTextHelper) + CreateTextHelper(); + return mxTextHelper->GetChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + if (!mxTextHelper) + CreateTextHelper(); + return mxTextHelper->GetChild(nIndex); +} + +sal_Int32 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleIndexInParent() +{ + return mnIndex; +} + +uno::Reference SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::TRANSIENT); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +//===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessiblePreviewHeaderCell::getImplementationName() +{ + return "ScAccessiblePreviewHeaderCell"; +} + +uno::Sequence SAL_CALL ScAccessiblePreviewHeaderCell::getSupportedServiceNames() +{ + const css::uno::Sequence 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_CALL + ScAccessiblePreviewHeaderCell::getImplementationId() +{ + return css::uno::Sequence(); +} + +//==== internal ========================================================= + +tools::Rectangle 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) + { + tools::Rectangle aRect = pWindow->GetWindowExtentsRelative(nullptr); + aCellRect.Move(aRect.Left(), aRect.Top()); + } + } + return 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 xAccParent = const_cast(this)->getAccessibleParent(); + if (xAccParent.is()) + { + uno::Reference xAccParentContext = xAccParent->getAccessibleContext(); + uno::Reference 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( const uno::Reference& rxParentStates ) +{ + return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC)); +} + +void ScAccessiblePreviewHeaderCell::CreateTextHelper() +{ + if (!mxTextHelper) + { + mxTextHelper.reset( new ::accessibility::AccessibleTextHelper( + std::make_unique( + std::make_unique( + 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 000000000..2f2cb48f6 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessiblePreviewTable.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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +ScAccessiblePreviewTable::ScAccessiblePreviewTable( const css::uno::Reference& 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(rColInfo.nDocIndex), static_cast(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(rColInfo.nDocIndex), static_cast(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 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(rColInfo.nDocIndex), static_cast(rRowInfo.nDocIndex), mpTableInfo->GetTab() ); + if ( rColInfo.bIsHeader || rRowInfo.bIsHeader ) + { + const bool bRotatedColHeader = rRowInfo.bIsHeader; + const bool bRotatedRowHeader = rColInfo.bIsHeader; + rtl::Reference pHeaderCell(new ScAccessiblePreviewHeaderCell(this, mpViewShell, aCellPos, + bRotatedColHeader, bRotatedRowHeader, nNewIndex)); + xRet = pHeaderCell.get(); + pHeaderCell->Init(); + } + else + { + rtl::Reference 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_Int32 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_Int32 nRet = nRow * mpTableInfo->GetCols() + nColumn; + return nRet; +} + +sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRow( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + FillTableInfo(); + + if ( !mpTableInfo || nChildIndex < 0 || nChildIndex >= static_cast(mpTableInfo->GetRows()) * mpTableInfo->GetCols() ) + throw lang::IndexOutOfBoundsException(); + + sal_Int32 nRow = nChildIndex / mpTableInfo->GetCols(); + return nRow; +} + +sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumn( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + FillTableInfo(); + + if ( !mpTableInfo || nChildIndex < 0 || nChildIndex >= static_cast(mpTableInfo->GetRows()) * mpTableInfo->GetCols() ) + throw lang::IndexOutOfBoundsException(); + + sal_Int32 nCol = nChildIndex % static_cast(mpTableInfo->GetCols()); + return nCol; +} + +//===== XAccessibleComponent ============================================ + +uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleAtPoint( const awt::Point& aPoint ) +{ + uno::Reference 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(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY); + if (xAccessibleComponent.is()) + xAccessibleComponent->grabFocus(); + } +} + +//===== XAccessibleContext ============================================== + +sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + FillTableInfo(); + + tools::Long nRet = 0; + if ( mpTableInfo ) + nRet = static_cast(mpTableInfo->GetCols()) * mpTableInfo->GetRows(); + return nRet; +} + +uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleChild( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + FillTableInfo(); + + uno::Reference 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_Int32 nRow = nIndex / nColumns; + + xRet = getAccessibleCellAt( nRow, nCol ); + } + } + + if ( !xRet.is() ) + throw lang::IndexOutOfBoundsException(); + + return xRet; +} + +sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleIndexInParent() +{ + return mnIndex; +} + +uno::Reference< XAccessibleStateSet > SAL_CALL ScAccessiblePreviewTable::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::MANAGES_DESCENDANTS); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +//===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessiblePreviewTable::getImplementationName() +{ + return "ScAccessiblePreviewTable"; +} + +uno::Sequence 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_CALL ScAccessiblePreviewTable::getImplementationId() +{ + return css::uno::Sequence(); +} + +//==== 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; +} + +tools::Rectangle ScAccessiblePreviewTable::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aCellRect(GetBoundingBox()); + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindow(); + if (pWindow) + { + tools::Rectangle aRect = pWindow->GetWindowExtentsRelative(nullptr); + aCellRect.Move(aRect.Left(), aRect.Top()); + } + } + return 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( const uno::Reference& rxParentStates ) +{ + return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(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 000000000..e435c37e7 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx @@ -0,0 +1,1667 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +static bool CompMinCol(const std::pair & pc1,const std::pair &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(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 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> 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 &pairCol : aVecCol) + { + SCCOL nCol = pairCol.second - pairCol.first + 1; + if (nCol + nCurrentIndex > nSelectedChildIndex) + { + return ScMyAddress(static_cast(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 &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 &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(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(&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(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(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 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(mpAccCell); + mpAccCell.clear(); + mpAccCell = GetAccessibleCellAt(aNewCell.Row(), aNewCell.Col()); + aEvent.NewValue <<= uno::Reference(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 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 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 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 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(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(nColumn)); + } + return bResult; +} + +rtl::Reference ScAccessibleSpreadsheet::GetAccessibleCellAt(sal_Int32 nRow, sal_Int32 nColumn) +{ + if (IsFormulaMode()) + { + ScAddress aCellAddress(static_cast(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(maRange.aStart.Col() + nColumn), + static_cast(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 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(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(nColumn), static_cast(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(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 SAL_CALL ScAccessibleSpreadsheet::getAccessibleRelationSet() +{ + rtl::Reference pRelationSet; + if(mpAccDoc) + pRelationSet = mpAccDoc->GetRelationSet(nullptr); + if (pRelationSet) + return pRelationSet; + return new utl::AccessibleRelationSetHelper(); +} + +uno::Reference SAL_CALL + ScAccessibleSpreadsheet::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + pStateSet->AddState(AccessibleStateType::MANAGES_DESCENDANTS); + if (IsEditable()) + pStateSet->AddState(AccessibleStateType::EDITABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + if (IsFocused()) + pStateSet->AddState(AccessibleStateType::FOCUSED); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::OPAQUE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + if (IsCompleteSheetSelected()) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + + ///===== XAccessibleSelection =========================================== + +void SAL_CALL ScAccessibleSpreadsheet::selectAccessibleChild( sal_Int32 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_Int32 SAL_CALL + ScAccessibleSpreadsheet::getSelectedAccessibleChildCount( ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + sal_Int32 nResult(0); + if (mpViewShell) + { + if (IsFormulaMode()) + { + nResult = GetRowAll() * 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 SAL_CALL + ScAccessibleSpreadsheet::getSelectedAccessibleChild( sal_Int32 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); + if( m_mapSelectionSend.find(addr) != m_mapSelectionSend.end() ) + xAccessible = m_mapSelectionSend[addr]; + else + xAccessible = getAccessibleCellAt(addr.Row(), addr.Col()); + } + } + return xAccessible; +} + +void SAL_CALL ScAccessibleSpreadsheet::deselectAccessibleChild( sal_Int32 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(nCol), nRow,mpViewShell->GetViewData().GetTabNo())) + ) + { + SelectCell(nRow, nCol, true); + } + return ; + } + if (mpViewShell->GetViewData().GetMarkData().IsCellMarked(static_cast(nCol), static_cast(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(nCol), nRow, rViewData.GetTabNo(), SC_REFTYPE_REF ); + mpViewShell->UpdateRef(static_cast(nCol), nRow, rViewData.GetTabNo()); + } + return ; + } + mpViewShell->SetTabNo( maRange.aStart.Tab() ); + + mpViewShell->DoneBlockMode( true ); // continue selecting + mpViewShell->InitBlockMode( static_cast(nCol), static_cast(nRow), maRange.aStart.Tab(), bDeselect ); + + mpViewShell->SelectionChanged(); +} + +/* +void ScAccessibleSpreadsheet::CreateSortedMarkedCells() +{ + mpSortedMarkedCells = new std::vector(); + 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 vals { "com.sun.star.AccessibleSpreadsheet" }; + return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals); +} + +//===== XTypeProvider ======================================================= + +uno::Sequence SAL_CALL + ScAccessibleSpreadsheet::getImplementationId() +{ + return css::uno::Sequence(); +} + +///===== XAccessibleEventBroadcaster ===================================== + +void SAL_CALL ScAccessibleSpreadsheet::addAccessibleEventListener(const uno::Reference& xListener) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + ScAccessibleTableBase::addAccessibleEventListener(xListener); + +} + +//==== internal ========================================================= + +tools::Rectangle ScAccessibleSpreadsheet::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aRect; + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos); + if (pWindow) + aRect = pWindow->GetWindowExtentsRelative(nullptr); + } + 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( + const uno::Reference& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(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(column), 0, maRange.aStart.Tab(), false, true ); + mpViewShell->MarkCursor( static_cast(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(column), 0, maRange.aStart.Tab(), false, true, false, true ); + mpViewShell->MarkCursor( static_cast(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(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 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 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_Int32 nIndex) const +{ + sal_Int32 nMaxIndex = (m_nMaxX - m_nMinX +1)*(m_nMaxY - m_nMinY +1) -1 ; + return nIndex <= nMaxIndex && nIndex >= 0 ; +} + +ScAddress ScAccessibleSpreadsheet::GetChildIndexAddress(sal_Int32 nIndex) const +{ + sal_Int32 nRowAll = GetRowAll(); + sal_uInt16 nColAll = GetColAll(); + if (nIndex < 0 || nIndex >= nRowAll * nColAll ) + { + return ScAddress(); + } + return ScAddress( + static_cast((nIndex - nIndex % nRowAll) / nRowAll + + m_nMinX), + nIndex % nRowAll + m_nMinY, + mpViewShell->GetViewData().GetTabNo() + ); +} + +sal_Int32 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 GetRowAll() * 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 000000000..5877c1e36 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleTableBase.cxx @@ -0,0 +1,470 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +ScAccessibleTableBase::ScAccessibleTableBase( + const uno::Reference& 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::get()) + { + return uno::Any(uno::Reference(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(nRow); + SCROW nEndRow = nStartRow; + SCCOL nStartCol = static_cast(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(nRow); + SCROW nEndRow = nStartRow; + SCCOL nStartCol = static_cast(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_Int32 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 (nRow * (maRange.aEnd.Col() + 1)) + nColumn; +} + +sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRow( sal_Int32 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_Int32 nChildIndex ) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + if (nChildIndex >= getAccessibleChildCount() || nChildIndex < 0) + throw lang::IndexOutOfBoundsException(); + + return nChildIndex % static_cast(maRange.aEnd.Col() - maRange.aStart.Col() + 1); +} + +// ===== XAccessibleContext ============================================== + +sal_Int32 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(maRange.aEnd.Row() - maRange.aStart.Row() + 1) * + static_cast(maRange.aEnd.Col() - maRange.aStart.Col() + 1); + if (nMax > SAL_MAX_INT32) + nMax = SAL_MAX_INT32; + if (nMax < 0) + return 0; + return static_cast(nMax); +} + +uno::Reference< XAccessible > SAL_CALL + ScAccessibleTableBase::getAccessibleChild(sal_Int32 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 SAL_CALL + ScAccessibleTableBase::getAccessibleRelationSet() +{ + OSL_FAIL("should be implemented in the abrevated class"); + return uno::Reference(); +} + +uno::Reference SAL_CALL + ScAccessibleTableBase::getAccessibleStateSet() +{ + OSL_FAIL("should be implemented in the abrevated class"); + uno::Reference< XAccessibleStateSet > xAccessibleStateSet; + return xAccessibleStateSet; +} + + ///===== XAccessibleSelection =========================================== + +void SAL_CALL ScAccessibleTableBase::selectAccessibleChild( sal_Int32 /* nChildIndex */ ) +{ +} + +sal_Bool SAL_CALL + ScAccessibleTableBase::isAccessibleChildSelected( sal_Int32 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_Int32 SAL_CALL + ScAccessibleTableBase::getSelectedAccessibleChildCount( ) +{ + return 0; +} + +uno::Reference SAL_CALL + ScAccessibleTableBase::getSelectedAccessibleChild( sal_Int32 /* nSelectedChildIndex */ ) +{ + uno::Reference < XAccessible > xAccessible; + return xAccessible; +} + +void SAL_CALL ScAccessibleTableBase::deselectAccessibleChild( sal_Int32 /* 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_CALL + ScAccessibleTableBase::getImplementationId() +{ + return css::uno::Sequence(); +} + +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 000000000..13c67f737 --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleText.cxx @@ -0,0 +1,1385 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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()); + 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()); + 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()); + } +} + +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 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()); + 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()); + 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 = static_cast( pViewShell->GetDocument().GetDocumentShell()); + return pDocSh; +} + +// ScAccessiblePreviewHeaderCellTextData: shared data between sub objects of an accessible cell text object + +ScAccessiblePreviewHeaderCellTextData::ScAccessiblePreviewHeaderCellTextData(ScPreviewShell* pViewShell, + const OUString& rText, const ScAddress& rP, bool bColHeader, bool bRowHeader) + : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP), + mpViewShell(pViewShell), + maText(rText), + mbColHeader(bColHeader), + mbRowHeader(bRowHeader) +{ +} + +ScAccessiblePreviewHeaderCellTextData::~ScAccessiblePreviewHeaderCellTextData() +{ + if (pEditEngine) + pEditEngine->SetNotifyHdl(Link()); + 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 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 = static_cast(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 = static_cast(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()); + 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 pEnginePool = EditEngine::CreatePool(); + pEnginePool->FreezeIdRanges(); + std::unique_ptr 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(mpViewShell); + return mxViewForwarder.get(); +} + +ScAccessibleNoteTextData::ScAccessibleNoteTextData(ScPreviewShell* pViewShell, + const OUString& sText, const ScAddress& aCellPos, bool bMarkNote) + : + mpViewShell(pViewShell), + mpDocSh(nullptr), + msText(sText), + maCellPos(aCellPos), + mbMarkNote(bMarkNote), + mbDataValid(false) +{ + if (pViewShell) + mpDocSh = static_cast(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()); + 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 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(mpViewShell); + return mxViewForwarder.get(); +} + +// CSV import ================================================================= + +class ScCsvViewForwarder : public SvxViewForwarder +{ + VclPtr 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, + const OUString& rCellText, const Size& rCellSize ) : + mpWindow( pWindow ), + mpEditEngine( pEditEngine ), + maCellText( rCellText ), + 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 000000000..c181656fe --- /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 +#include +#include +#include + +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(&rHint); + + document::EventObject aEvent; + if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) ) + return; + + std::unique_lock aGuard(maListenerMutex); + maEventListeners.forEach(aGuard, + [&aEvent](const css::uno::Reference& 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(pSdrHint->GetObject()); + uno::Reference 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 000000000..f6871ccff --- /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 +#include + +#include +#include +#include +#include +#include +#include + +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 OUStringLiteral strWildcardRange = u"%RANGE%"; + +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 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 000000000..cfcf53699 --- /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 +#include +#include +#include +#include + +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 000000000..7e9a23372 --- /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 +#include +#include +#include + +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 000000000..b2849d316 --- /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 +#include +#include +#include + +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 000000000..0924278c5 --- /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 + +#include +#include +#include +#include +#include + +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 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 000000000..1a87f5beb --- /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 + +#include +#include +#include +#include +#include + +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 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 000000000..76b2bade6 --- /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 + +#include +#include +#include +#include +#include + +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 pVariable1Iterator; + if (mGroupedBy == BY_COLUMN) + pVariable1Iterator.reset(new DataRangeByColumnIterator(mVariable1Range)); + else + pVariable1Iterator.reset(new DataRangeByRowIterator(mVariable1Range)); + + std::unique_ptr 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 000000000..c6cff45e8 --- /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 +#include +#include +#include +#include +#include +#include + +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(mxMinMagnitudeField->get_value()); + if (nDeciBels <= -150) + mfMinMag = 0.0; + else + mfMinMag = pow(10.0, static_cast(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 OUStringLiteral aSep(u";"); + + 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 000000000..4345b816b --- /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 +#include +#include +#include +#include + +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 OString& 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 const OUStringLiteral strWildcardNumber(u"%NUMBER%"); + + // 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 000000000..9d990ed5a --- /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 + +#include +#include +#include +#include +#include + +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 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 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 000000000..91b43cbe0 --- /dev/null +++ b/sc/source/ui/StatisticsDialogs/RandomNumberGeneratorDialog.cxx @@ -0,0 +1,482 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +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 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(PRECISION); + double parameter2 = parameterInteger2 / static_cast(PRECISION); + + std::optional aDecimalPlaces; + if (mxEnableRounding->get_active()) + { + aDecimalPlaces = static_cast(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 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_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_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; + } + } +} + +template +void ScRandomNumberGeneratorDialog::GenerateNumbers(RNG& randomGenerator, TranslateId pDistributionStringId, std::optional aDecimalPlaces) +{ + OUString aUndo = ScResId(STR_UNDO_DISTRIBUTION_TEMPLATE); + OUString aDistributionName = ScResId(pDistributionStringId); + aUndo = aUndo.replaceAll("$(DISTRIBUTION)", 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 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; + } + } +} + +/* 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 000000000..ad4adb1fe --- /dev/null +++ b/sc/source/ui/StatisticsDialogs/RegressionDialog.cxx @@ -0,0 +1,696 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* + 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")) + , mxPowerRadioButton(m_xBuilder->weld_radio_button("power-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(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 aCellGetterFunc = [&aTable](size_t nRowIdx, size_t nColIdx) -> const OUString& + { + return aTable[nRowIdx][nColIdx]; + }; + + // Cell writer lambda + std::function 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 aCellGetterFunc = [&aTable](size_t nRowIdx, size_t nColIdx) -> const OUString& + { + return aTable[nRowIdx][nColIdx]; + }; + + // Cell writer lambda + size_t nNumIndependentVars = mnNumIndependentVars; + std::function 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& rCellGetter, + size_t nRowsInTable, size_t nColsInTable, + AddressWalkerWriter& rOutput, + const std::function& 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 000000000..fad4ac6d0 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 aEditLink = LINK( this, ScSamplingDialog, GetEditFocusHandler ); + mxInputRangeEdit->SetGetFocusHdl( aEditLink ); + mxOutputRangeEdit->SetGetFocusHdl( aEditLink ); + Link 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 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 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 enlargening 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 000000000..7447ebf9a --- /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 + +#include +#include +#include +#include +#include + +#include + +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 OString& 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 aEditLink = LINK( this, ScStatisticsInputOutputDialog, GetEditFocusHandler ); + mxInputRangeEdit->SetGetFocusHdl( aEditLink ); + mxOutputRangeEdit->SetGetFocusHdl( aEditLink ); + Link 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 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 000000000..3c0f7ce98 --- /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 + +#include +#include +#include +#include +#include + +#include + +ScStatisticsTwoVariableDialog::ScStatisticsTwoVariableDialog( + SfxBindings* pSfxBindings, SfxChildWindow* pChildWindow, + weld::Window* pParent, ScViewData& rViewData, const OUString& rUIXMLDescription, const OString& 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 aEditLink = LINK( this, ScStatisticsTwoVariableDialog, GetEditFocusHandler ); + mxVariable1RangeEdit->SetGetFocusHdl( aEditLink ); + mxVariable2RangeEdit->SetGetFocusHdl( aEditLink ); + mxOutputRangeEdit->SetGetFocusHdl( aEditLink ); + + Link 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 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 000000000..864d4ac4f --- /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 + +#include +#include +#include +#include +#include + +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 pVariable1Iterator; + if (mGroupedBy == BY_COLUMN) + pVariable1Iterator.reset(new DataRangeByColumnIterator(mVariable1Range)); + else + pVariable1Iterator.reset(new DataRangeByRowIterator(mVariable1Range)); + + std::unique_ptr 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 000000000..be8431128 --- /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 + +#include +#include +#include + +#include + +#include +#include +#include +#include + +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& 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 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 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 000000000..a1731fa8f --- /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 + +#include +#include +#include +#include +#include + +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 pVariable1Iterator; + if (mGroupedBy == BY_COLUMN) + pVariable1Iterator.reset(new DataRangeByColumnIterator(mVariable1Range)); + else + pVariable1Iterator.reset(new DataRangeByRowIterator(mVariable1Range)); + + std::unique_ptr 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 000000000..0886fae89 --- /dev/null +++ b/sc/source/ui/app/client.cxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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; nPNrGetPage(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(pObject)->GetPersistName() == aName ) + pOle2Obj = static_cast(pObject); + } + pObject = aIter.Next(); + } + } + return pOle2Obj; +} + +void ScClient::RequestNewObjectArea( tools::Rectangle& aLogicRect ) +{ + SfxViewShell* pSfxViewSh = GetViewShell(); + ScTabViewShell* pViewSh = dynamic_cast( 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(static_cast(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( 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().nRotationAngle || pDrawObj->GetGeoStat().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(); + + // 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; + + tools::Rectangle aLogicRect = pDrawObj->GetLogicRect(); + Fraction aFractX = GetScaleWidth() * aVisSize.Width(); + Fraction aFractY = GetScaleHeight() * aVisSize.Height(); + aVisSize = Size( static_cast(aFractX), static_cast(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( 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 000000000..8cd335b2e --- /dev/null +++ b/sc/source/ui/app/drwtrans.cxx @@ -0,0 +1,738 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +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 pClipModel, ScDocShell* pContainerShell, + const TransferableObjectDescriptor& rDesc ) : + m_pModel( std::move(pClipModel) ), + m_aObjDesc( rDesc ), + 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(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(pObject)->GetGraphic().GetType() == GraphicType::Bitmap ) + m_bGrIsBit = true; + } + + // URL button + + SdrUnoObj* pUnoCtrl = dynamic_cast( pObject ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference& 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_pBookmark.reset( new INetBookmark( 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_pBookmark.reset(); + m_pDragSourceView.reset(); +} + +ScDrawTransferObj* ScDrawTransferObj::GetOwnClipboard(const uno::Reference& xTransferable) +{ + return comphelper::getFromUnoTunnel(xTransferable); +} + +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( 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_pBookmark ) // 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 ) + { + bOK = SetObject( m_pModel.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(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_pBookmark ) + { + bOK = SetINetBookmark( *m_pBookmark, rFlavor ); + } + } + return bOK; +} + +bool ScDrawTransferObj::WriteObject( tools::SvRef& 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(pUserObject); + 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 = m_pModel->GetItemPool(); + const SvxFontHeightItem& rDefaultFontHeight = rItemPool.GetDefaultItem(EE_CHAR_FONTHEIGHT); + + // SW should have no MasterPages + OSL_ENSURE(0 == m_pModel->GetMasterPageCount(), "SW with MasterPages (!)"); + + for(sal_uInt16 a(0); a < m_pModel->GetPageCount(); a++) + { + const SdrPage* pPage(m_pModel->GetPage(a)); + SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + const SvxFontHeightItem& rItem = pObj->GetMergedItem(EE_CHAR_FONTHEIGHT); + + if(rItem.GetHeight() == rDefaultFontHeight.GetHeight()) + { + pObj->SetMergedItem(rDefaultFontHeight); + } + } + } + + { + css::uno::Reference 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(pUserObject); + + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + uno::Reference < embed::XEmbedPersist > xPers( static_cast(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(pUserObject); + + try + { + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + + // mba: no 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(); + + std::unique_ptr pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ ); + if( pSrcStm ) + { + rxOStm->SetBufferSize( 0xff00 ); + rxOStm->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + 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; iGetMarkedSdrObj(); + + 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(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 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 ); + + 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( 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); +} + +const css::uno::Sequence< sal_Int8 >& ScDrawTransferObj::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theScDrawTransferObjUnoTunnelId; + return theScDrawTransferObjUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ScDrawTransferObj::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl( + rId, this, comphelper::FallbackToGetSomethingOf{}); +} + +/* 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 000000000..884e2e696 --- /dev/null +++ b/sc/source/ui/app/inputhdl.cxx @@ -0,0 +1,4635 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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& rResults, const OUString& rUserEntry, sal_Int32 nRingOrigin) +{ + sal_Int32 nResults = rResults.size(); + if (!nResults) + return 0; + + if (nResults == 1) + return rResults[0].getLength(); + + sal_Int32 nMinLen = rUserEntry.getLength(); + 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(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& 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().c_str(); + pViewShell->libreOfficeKitViewCallback( + LOK_CALLBACK_REFERENCE_MARKS, aPayload.getStr() ); +} + +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(" !~\""); + // 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; + sal_uInt16 nCount = 0; + ScRange aRange; + while ( nPos < nLen && nCount < RANGEFIND_MAX ) + { + // Skip separator + while ( nPos 0 && + '-' == pChar[nPos] && '[' == pChar[nPos-1] && + formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() ) + { + nPos++; + 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, nStart, nPos ) ); + + ESelection aSel( 0, nStart, 0, nPos ); + 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; + PutInOrder(nCol1, nCol2); + PutInOrder(nRow1, nRow2); + if (nCol1 == nCol2 && nRow1 == nRow2) + pDocSh->GetDocument().ExtendMerge(nCol1, nRow1, 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(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 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( 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() ); + + sal_Int32 nLen = pEngine->GetTextLen(0); + ESelection aSel( 0, nLen, 0, 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 ); + sal_Int32 nOldStart = rData.nSelStart; + sal_Int32 nOldEnd = rData.nSelEnd; + 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)); + ESelection aOldSel( 0, nOldStart, 0, nOldEnd ); + SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); + + DataChanging(); + + lcl_Replace( pTopView, aNewStr, aOldSel ); + lcl_Replace( pTableView, aNewStr, aOldSel ); + aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) ); + mpEditEngine->QuickSetAttribs( aSet, aOldSel ); + + bInRangeUpdate = true; + DataChanged(); + bInRangeUpdate = false; + + tools::Long nDiff = aNewStr.getLength() - static_cast(nOldEnd-nOldStart); + + rData.aRef = rNew; + rData.nSelEnd = rData.nSelEnd + nDiff; + rData.nColor = nNewColor; + + sal_uInt16 nCount = static_cast(pRangeFindList->Count()); + for (sal_uInt16 i=nIndex+1; iGetObject( i ); + rNext.nSelStart = rNext.nSelStart + nDiff; + rNext.nSelEnd = rNext.nSelEnd + 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(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( SC_MOD()->GetOptDigitLanguage() ); + } +} + +void ScInputHandler::ImplCreateEditEngine() +{ + if ( mpEditEngine ) + return; + + if ( pActiveViewSh ) + { + ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); + mpEditEngine = std::make_unique(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); + } + else + mpEditEngine = std::make_unique(nullptr, EditEngine::CreatePool().get(), nullptr, true); + + 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 xXSpellChecker1( LinguMgr::GetSpellChecker() ); + mpEditEngine->SetSpeller( xXSpellChecker1 ); + } + + bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue(); + if ( bHyphen ) { + css::uno::Reference 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; + } + + 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(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 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::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; + aPayload.append("[ "); + 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("{"); + aPayload.append("\"index\": "); + aPayload.append(static_cast(nCurIndex)); + aPayload.append(", "); + aPayload.append("\"signature\": \""); + aPayload.append(escapeJSON(ppFDesc->getSignature())); + aPayload.append("\", "); + aPayload.append("\"description\": \""); + aPayload.append(escapeJSON(ppFDesc->getDescription())); + aPayload.append("\"}, "); + } + } + ++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.getStr()); + } + // not tunnel tooltips in the lok case + return; + } + + OUStringBuffer aTipStr; + OUString aFuncNameStr; + OUString aDescFuncNameStr; + ::std::vector::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 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 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 ScInputHandler::LOKSendFormulabarUpdate(const SfxViewShell* pActiveViewSh, + const OUString& rText, + const ESelection& rSelection) +{ + OUString aSelection = + OUString::number(rSelection.nStartPos) + ";" + OUString::number(rSelection.nEndPos); + + std::unique_ptr pData = std::make_unique(); + (*pData)["action_type"] = "setText"; + (*pData)["text"] = rText; + (*pData)["selection"] = aSelection; + + sal_uInt64 nCurrentShellId = reinterpret_cast(pActiveViewSh); + std::string sWindowId = std::to_string(nCurrentShellId) + "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 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(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 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; iRemoveCharAttribs( 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; iRemoveCharAttribs( 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( 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; iGetView(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; iRemoveCharAttribs( 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 (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); + } + + 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); + } +} + +/** + * @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 + + 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 ) + pInputWin->SetTextString( aText ); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (pActiveViewSh) + { + // TODO: deprecated? + pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8().getStr()); + } + } + } + + // 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 (comphelper::LibreOfficeKit::isActive() && pActiveViewSh && pInputWin) + { + ScInputHandler::LOKSendFormulabarUpdate(pActiveViewSh, + 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( SfxViewShell::Current() ); + if ( !(pRefViewSh && pRefViewSh != pVisibleSh) ) + return; + + bool bFound = false; + SfxViewFrame* pRefFrame = pRefViewSh->GetViewFrame(); + SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst(); + while ( pOneFrame && !bFound ) + { + if ( pOneFrame == pRefFrame ) + 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; iGetView(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(const OUString& rString) +{ + sal_Int32 nLen = rString.getLength(); + for (sal_Int32 i=0; i '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; + + // 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 pObject; + std::unique_ptr 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 ? pActiveViewSh->GetFrameWin() : nullptr; + + if (pTopView) + pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views + if (pTableView) + pTableView->CompleteAutoCorrect(pFrameWin); + aString = GetEditText(mpEditEngine.get()); + } + lcl_RemoveTabs(aString); + lcl_RemoveTabs(aPreAutoCorrectString); + + // Test if valid (always with simple string) + if (bModified && nValidation && pActiveViewSh) + { + 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 && pActiveViewSh && !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 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. + if ( pActiveViewSh ) + { + 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 ( pActiveViewSh && bUniformAttribs ) + { + std::optional 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(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 ); + if (pExecuteSh) + pExecuteSh->StopEditShell(); + + aCursorPos.Set(pExecuteSh->GetViewData().GetDocument().MaxCol()+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 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)); + + SfxObjectShell* 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 && !comphelper::LibreOfficeKit::isActive()) + 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( SfxViewShell::Current() ); + + ImplCreateEditEngine(); + + if ( pState != pLastState.get() ) + { + pLastState.reset( pState ? new ScInputHdlState( *pState ) : nullptr); + } + + if ( pState && pActiveViewSh ) + { + ScModule* pScMod = SC_MOD(); + + ScTabViewShell* pScTabViewShell = dynamic_cast(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 + } + + if ( pInputWin ) + pInputWin->SetTextString(aString); + + if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) + { + EditView* pActiveView = pTopView ? pTopView : pTableView; + ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection(); + ScInputHandler::LOKSendFormulabarUpdate(pActiveViewSh, aString, aSel); + // TODO: deprecated? + pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8().getStr()); + } + } + + 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().getStr()); + } + + 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() ); + 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(); + ScInputHandler::LOKSendFormulabarUpdate(pActiveViewSh, 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 pObj = mpEditEngine->CreateTextObject(); + rDestEngine.SetTextCurrentDefaults(*pObj); + pObj.reset(); + + // Delete attributes + for (sal_Int32 i=0; i 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( const OUString& rStr ) +{ + if (!pRefViewSh) + pRefViewSh = pActiveViewSh; + + OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken..."); + + sal_Int32 nOldLen = nFormSelEnd - nFormSelStart; + sal_Int32 nNewLen = rStr.getLength(); + + OUStringBuffer aBuf(aFormText); + if (nOldLen) + aBuf.remove(nFormSelStart, nOldLen); + if (nNewLen) + aBuf.insert(nFormSelStart, rStr); + + 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, + const OUString& rString, + const EditTextObject* pData ) + : aCursorPos ( rCurPos ), + aStartPos ( rStartPos ), + aEndPos ( rEndPos ), + aString ( rString ), + 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 000000000..0b31b8160 --- /dev/null +++ b/sc/source/ui/app/inputwin.cxx @@ -0,0 +1,2703 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 pWin = VclPtr::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 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( pViewFrm->GetViewShell() ); + } + + return VclPtr::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::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( 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::DROPDOWNONLY, 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() ); + } + 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() ); // 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 ); + while ( pSh ) + { + ScInputHandler* pHdl = static_cast(pSh)->GetInputHandler(); + if ( pHdl && pHdl->GetInputWindow() == this ) + { + pHdl->SetInputWindow( nullptr ); + pHdl->StopInputWinEngine( false ); // reset pTopView pointer + } + pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell ); + } + } + + 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_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( 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); + break; + } + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + nStartPos = 0; + nEndPos = nLen; + break; + case CELLTYPE_FORMULA: + nEndPos = nLen; + break; + default: + mxTextWindow->SetTextString("="); + break; + } + } + + EditView* pView = mxTextWindow->GetEditView(); + if (pView) + { + if (comphelper::LibreOfficeKit::isActive()) + TextGrabFocus(); + pView->SetSelection( ESelection(0, nStartPos, 0, 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 ); + 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 ) +{ + if (rString.getLength() <= 32767) + mxTextWindow->SetTextString(rString); + else + mxTextWindow->SetTextString(rString.copy(0, 32767)); +} + +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( 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(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()); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + SetPaintTransparent(false); + 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()); + + 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); +} + +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 ) +{ + mxTextWndGroup->SetTextString(rString); +} + +void ScInputBarGroup::Resize() +{ + mxTextWndGroup->SetScrollPolicy(); + InterimItemWindow::Resize(); + TriggerToolboxLayout(); +} + +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::string_view command) +{ + if (command.empty()) + return; + + bool bSubTotal = false; + bool bRangeFinder = false; + OpCode eCode = ocSum; + if ( command == "sum" ) + { + eCode = ocSum; + } + else if ( command == "average" ) + { + eCode = ocAverage; + } + else if ( command == "max" ) + { + eCode = ocMax; + } + else if ( command == "min" ) + { + eCode = ocMin; + } + else if ( command == "count" ) + { + eCode = ocCount; + } + else if ( command == "counta" ) + { + eCode = ocCount2; + } + else if ( command == "product" ) + { + eCode = ocProduct; + } + else if (command == "stdev") + { + eCode = ocStDev; + } + else if (command == "stdevp") + { + eCode = ocStDevP; + } + else if (command == "var") + { + eCode = ocVar; + } + else if (command == "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 xBuilder(Application::CreateBuilder(pPopupParent, "modules/scalc/ui/autosum.ui")); + std::unique_ptr 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() +{ + vcl::Window *w=GetParent(); + ScInputWindow &rParent = dynamic_cast(*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 (comphelper::LibreOfficeKit::isActive()) + ScInputHandler::LOKSendFormulabarUpdate(SfxViewShell::Current(), "", 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) +{ + mxTextWnd->SetTextString(rString); +} + +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(*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(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 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 pNew; + ScDocShell* pDocSh = nullptr; + if ( mpViewShell ) + { + pDocSh = mpViewShell->GetViewData().GetDocShell(); + ScDocument& rDoc = mpViewShell->GetViewData().GetDocument(); + pNew = std::make_unique(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); + } + else + pNew = std::make_unique(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( 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(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(*m_xEditEngine)); + + m_xEditEngine->SetUpdateLayout( true ); + + // aString is the truth ... + if (bFilled && m_xEditEngine->GetText() == aString) + Invalidate(); // Repaint for (filled) Field + else + static_cast(m_xEditEngine.get())->SetTextCurrentDefaults(aString); // At least the right text then + + m_xEditView = std::make_unique(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(); +} + +bool ScTextWnd::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if (!HasFocus()) + { + StartEditEngine(); + if (CanFocus()) + TextGrabFocus(); + } + + bool bClickOnSelection = false; + if (m_xEditView) + { + m_xEditView->SetEditEngineUpdateLayout( true ); + bClickOnSelection = m_xEditView->IsSelectionAtPoint(rMEvt.GetPosPixel()); + } + if (!bClickOnSelection) + { + rtl::Reference xTransferable(new TransferDataContainer); + GetDrawingArea()->enable_drag_source(xTransferable, DND_ACTION_NONE); + } + else + { + rtl::Reference 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(); + pViewFrm->GetDispatcher()->ExecutePopup("formulabar", &mrGroupBar.GetVclParent(), &aPos); + } + } + else if ( nCommand == CommandEventId::Wheel ) + { + //don't call InputChanged for CommandEventId::Wheel + } + else if ( nCommand == CommandEventId::Swipe ) + { + //don't call InputChanged for CommandEventId::Swipe + } + else if ( nCommand == CommandEventId::LongPress ) + { + //don't call InputChanged for CommandEventId::LongPress + } + 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; + + Point aSelectionStartEnd = rCEvt.GetMousePosPixel(); + m_xEditView->SetSelection(ESelection(0, aSelectionStartEnd.X(), + 0, aSelectionStartEnd.Y())); + + 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()); + m_xEditEngine->SetModifyHdl(Link()); + 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 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 ) +{ + if (comphelper::LibreOfficeKit::isActive()) + { + ESelection aSel = m_xEditView ? m_xEditView->GetSelection() : ESelection(); + ScInputHandler::LOKSendFormulabarUpdate(SfxViewShell::Current(), rNewString, aSel); + } + + // 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( 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(m_xEditEngine.get())->SetTextCurrentDefaults(rNewString); + } + + aString = rNewString; + + if (!maAccTextDatas.empty()) + maAccTextDatas.back()->TextChanged(); + + bInputMode = false; + } + + SetScrollBarRange(); + DoScroll(); +} + +const OUString& ScTextWnd::GetTextString() const +{ + return aString; +} + +bool ScTextWnd::IsInputActive() +{ + return HasFocus(); +} + +void ScTextWnd::MakeDialogEditView() +{ + if ( m_xEditView ) return; + + std::unique_ptr pNew; + ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); + if ( pViewSh ) + { + ScDocument& rDoc = pViewSh->GetViewData().GetDocument(); + pNew = std::make_unique(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); + } + else + pNew = std::make_unique(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( m_xEditEngine->GetEmptyItemSet() ); + EditEngine::SetFontInfoInItemSet( *pSet, aTextFont ); + lcl_ExtendEditFontAttribs( *pSet ); + if ( bIsRTL ) + lcl_ModifyRTLDefaults( *pSet ); + static_cast(m_xEditEngine.get())->SetDefaults( std::move(pSet) ); + m_xEditEngine->SetUpdateLayout( bPrevUpdateLayout ); + + m_xEditView = std::make_unique(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 xHelper(m_xHelper); + SetDragDataTransferrable(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( pObjSh) ) + { + ScDocument& rDoc = pDocShell->GetDocument(); + + m_xWidget->append_text(ScResId(STR_MANAGE_NAMES)); + m_xWidget->append_separator("separator"); + + ScRange aDummy; + std::set 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; iGetFunction( 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; + + // Does the list of range names need updating? + if ( auto pEventHint = dynamic_cast(&rHint) ) + { + SfxEventHintId nEventId = pEventHint->GetEventId(); + if ( nEventId == SfxEventHintId::ActivateDoc ) + FillRangeNames(); + } + else + { + const SfxHintId nHintId = rHint.GetId(); + 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() +{ + 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( 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) + { + sal_uInt16 nId = ScNameDlgWrapper::GetChildWindowId(); + SfxViewFrame* pViewFrm = pViewSh->GetViewFrame(); + SfxChildWindow* pWnd = pViewFrm->GetChildWindow( nId ); + + SC_MOD()->SetRefDialog( nId, pWnd == nullptr ); + } + 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(); +} + +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( 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 000000000..1ff48e7fe --- /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 + +#include +#include + +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 000000000..58daba682 --- /dev/null +++ b/sc/source/ui/app/msgpool.cxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +SfxItemInfo const aMsgItemInfos[] = +{ + { 0, true }, // SCITEM_STRING + { 0, true }, // SCITEM_SEARCHDATA - stop using this! + { SID_SORT, true }, // SCITEM_SORTDATA + { SID_QUERY, true }, // SCITEM_QUERYDATA + { SID_SUBTOTALS, true }, // SCITEM_SUBTDATA + { SID_CONSOLIDATE, true }, // SCITEM_CONSOLIDATEDATA + { SID_PIVOT_TABLE, true }, // SCITEM_PIVOTDATA + { SID_SOLVE, true }, // SCITEM_SOLVEDATA + { SID_SCUSERLISTS, true }, // SCITEM_USERLIST + { 0, 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 000000000..330b8479b --- /dev/null +++ b/sc/source/ui/app/rfindlst.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 +#include + +#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(const OUString& rName) : + aDocName( rName ), + 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::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 000000000..2256ee2e1 --- /dev/null +++ b/sc/source/ui/app/scdll.cxx @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Controls + +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +// Child windows +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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(&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); + + // 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(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 000000000..1529f9920 --- /dev/null +++ b/sc/source/ui/app/scmod.cxx @@ -0,0 +1,2325 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ShellClass_ScModule +#include + +#include +#include +#include + +#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", {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 +} + +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 ) +{ + 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(pObjSh) ) + { + if ( bArrows ) + ScDetectiveFunc( pDocSh->GetDocument(), 0 ).UpdateAllArrowColors(); + if ( bComments ) + ScDetectiveFunc::UpdateAllComments( pDocSh->GetDocument() ); + } + pObjSh = SfxObjectShell::GetNext( *pObjSh ); + } + } + } + + // force all views to repaint, using the new options + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while(pViewShell) + { + if (ScTabViewShell* pViewSh = dynamic_cast(pViewShell)) + { + pViewSh->PaintGrid(); + pViewSh->PaintTop(); + pViewSh->PaintLeft(); + pViewSh->PaintExtras(); + + ScInputHandler* pHdl = pViewSh->GetInputHandler(); + if ( pHdl ) + pHdl->ForgetLastPattern(); // EditEngine BackgroundColor may change + } + else if ( dynamic_cast( pViewShell) != nullptr ) + { + vcl::Window* pWin = pViewShell->GetWindow(); + if (pWin) + pWin->Invalidate(); + } + 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(pObjSh) ) + { + OutputDevice* pPrinter = pDocSh->GetPrinter(); + if ( pPrinter ) + pPrinter->SetDigitLanguage( GetOptDigitLanguage() ); + + pDocSh->CalcOutputFactor(); + + SCTAB nTabCount = pDocSh->GetDocument().GetTableCount(); + for (SCTAB nTab=0; nTabAdjustRowHeight( 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(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(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(pItem)->GetValue(); + else if ( pReqArgs && SfxItemState::SET == pReqArgs->GetItemState( nSlot, true, &pItem ) ) + bSet = static_cast(pItem)->GetValue(); + else + { // Toggle + ScDocShell* pDocSh = dynamic_cast( SfxObjectShell::Current() ); + if ( pDocSh ) + bSet = !pDocSh->GetDocument().GetDocOptions().IsAutoSpell(); + else + bSet = !GetDocOptions().IsAutoSpell(); + } + + SfxItemSetFixed 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(static_cast(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(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) + { + auto const & p = pReqArgs->Get(SID_PSZ_FUNCTION); + assert(dynamic_cast(&p) && "wrong Parameter"); + const SfxUInt32Item& rItem = static_cast(p); + + 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( SfxObjectShell::Current() ); + if ( pDocSh ) + { + ScDocument& rDoc = pDocSh->GetDocument(); + LanguageType eNewLang = static_cast(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( 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( 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(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 xTransferable; + + if ((pViewShell = dynamic_cast(SfxViewShell::Current()))) + xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); + else if ((pViewShell = dynamic_cast(SfxViewShell::GetFirst()))) + xTransferable.set(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); + else if ((pViewFrame = SfxViewFrame::GetFirst())) + { + css::uno::Reference 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) && nSetOptions( 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; +} + +SvtAccessibilityOptions& ScModule::GetAccessOptions() +{ + if ( !m_pAccessOptions ) + { + m_pAccessOptions.reset( new SvtAccessibilityOptions ); + m_pAccessOptions->AddListener(this); + } + + return *m_pAccessOptions; +} + +SvtCTLOptions& ScModule::GetCTLOptions() +{ + if ( !m_pCTLOptions ) + { + m_pCTLOptions.reset( new SvtCTLOptions ); + m_pCTLOptions->AddListener(this); + } + + return *m_pCTLOptions; +} + +SvtUserOptions& ScModule::GetUserOptions() +{ + if( !m_pUserOptions ) + { + m_pUserOptions.reset( new SvtUserOptions ); + } + return *m_pUserOptions; +} + +LanguageType ScModule::GetOptDigitLanguage() +{ + SvtCTLOptions::TextNumerals eNumerals = GetCTLOptions().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( SfxViewShell::Current() ); + ScDocShell* pDocSh = dynamic_cast( 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(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(nVal); + if (eOld != eNew) + { + aAppOptions.SetKeyBindingType(eNew); + bSaveAppOptions = true; + ScDocShell::ResetKeyBindings(eNew); + } + } + + // 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(pObjSh) ) + { + pOneDocSh->CalcOutputFactor(); + SCTAB nTabCount = pOneDocSh->GetDocument().GetTableCount(); + for (SCTAB nTab=0; nTabAdjustRowHeight( 0, pDocSh->GetDocument().MaxRow(), nTab ); + } + pObjSh = SfxObjectShell::GetNext( *pObjSh ); + } + + // for all (tab-) views: + SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell ); + while ( pSh ) + { + ScTabViewShell* pOneViewSh = static_cast(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 ); + } +} + +/** + * 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( 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( const OUString& rStr ) +{ + ScInputHandler* pHdl = GetInputHdl(); + if (pHdl) + pHdl->InputReplaceSelection( rStr ); +} + +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(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(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(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(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(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(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(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(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 ScModule::CreateItemSet( sal_uInt16 nId ) +{ + std::optional 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>); + + 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(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() ) ); + + // 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( aOptions ) ); + } + return pRet; +} + +void ScModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet ) +{ + if(SID_SC_EDITOPTIONS == nId) + { + ModifyOptions( rSet ); + } +} + +std::unique_ptr ScModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) +{ + std::unique_ptr 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(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& rWnd, weld::Window* pWndAncestor) +{ + std::vector, weld::Window*>> & rlRefWindow = m_mapRefController[nSlotId]; + + if (std::find_if(rlRefWindow.begin(), rlRefWindow.end(), + [rWnd](const std::pair, weld::Window*>& rCandidate) + { + return rCandidate.first.get() == rWnd.get(); + }) == rlRefWindow.end()) + { + rlRefWindow.emplace_back(rWnd, pWndAncestor); + } +} + +void ScModule::UnregisterRefController(sal_uInt16 nSlotId, const std::shared_ptr& rWnd) +{ + auto iSlot = m_mapRefController.find( nSlotId ); + + if( iSlot == m_mapRefController.end() ) + return; + + std::vector, weld::Window*>> & rlRefWindow = iSlot->second; + + auto i = std::find_if(rlRefWindow.begin(), rlRefWindow.end(), + [rWnd](const std::pair, 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 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, 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 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())); + + 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 000000000..6ba63e4dc --- /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 +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +static bool lcl_IsURLButton( SdrObject* pObject ) +{ + bool bRet = false; + + SdrUnoObj* pUnoCtrl = dynamic_cast( pObject ); + if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const uno::Reference& 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::CreateFromView( ScTabView* pView ) +{ + rtl::Reference 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(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 pTransferObj = new ScTransferObj( std::move(pClipDoc), 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 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 pTransferObj = new ScDrawTransferObj( std::move(pModel), pDocSh, 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 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 000000000..d0be7e0ae --- /dev/null +++ b/sc/source/ui/app/transobj.cxx @@ -0,0 +1,921 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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& pClipDoc, const TransferableObjectDescriptor& rDesc ) : + m_pDoc( pClipDoc ), + m_nNonFiltered(0), + m_aObjDesc( rDesc ), + 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( nCol2 + nCol1 ); + nRow2 = sal::static_int_cast( 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& xTransferable) +{ + return comphelper::getFromUnoTunnel(xTransferable); +} + +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.meType == CELLTYPE_EDIT) + { + const EditTextObject* pObj = aCell.mpEditText; + 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::get() ) ) + { + OUString aString; + if ( aObj.ExportString( aString, nFormat ) ) + bOK = SetString( aString ); + } + else if ( rFlavor.DataType.equals( cppu::UnoType>::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& 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(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(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 xEditTrans = pEngine->CreateTransferable( aSel ); + TransferableDataHelper aEditHelper( xEditTrans ); + + bRet = aEditHelper.GetSotStorageStream( rFlavor, rxOStm ); + } + } + break; + + case SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT: + { + ScTabEditEngine* pEngine = static_cast(pUserObject); + pEngine->Write(*rxOStm, EETextFormat::Xml); + bRet = (rxOStm->GetError() == ERRCODE_NONE); + } + break; + + case SCTRANS_TYPE_EMBOBJ: + { + // TODO/MBA: testing + SfxObjectShell* pEmbObj = static_cast(pUserObject); + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + + // mba: no 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(); + + std::unique_ptr pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ ); + if( pSrcStm ) + { + rxOStm->SetBufferSize( 0xff00 ); + rxOStm->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + 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->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() +{ + ScCellRangesBase* pRangesObj = comphelper::getFromUnoTunnel( m_xDragSourceRanges ); + if (pRangesObj) + return pRangesObj->GetDocShell(); + + return nullptr; // none set +} + +ScMarkData ScTransferObj::GetSourceMarkData() const +{ + ScMarkData aMarkData(m_pDoc->GetSheetLimits()); + ScCellRangesBase* pRangesObj = comphelper::getFromUnoTunnel( m_xDragSourceRanges ); + if (pRangesObj) + { + const ScRangeList& rRanges = pRangesObj->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 ); + + 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); + } + } + + if (m_pDoc->GetDrawLayer() || m_pDoc->HasNotes()) + pDocSh->MakeDrawLayer(); + + // 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 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& 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); + } + } + } + } +} + +const css::uno::Sequence< sal_Int8 >& ScTransferObj::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theScTransferUnoTunnelId; + return theScTransferUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ScTransferObj::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl( + rId, this, comphelper::FallbackToGetSomethingOf{}); +} + +/* 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 000000000..ac98c6fc4 --- /dev/null +++ b/sc/source/ui/app/typemap.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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +#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 000000000..91cb50b07 --- /dev/null +++ b/sc/source/ui/app/uiitems.cxx @@ -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 . + */ + +#include + +#include +#include +#include + +#include +#include + +/** + * Status update for entry field + */ +ScInputStatusItem::ScInputStatusItem( + sal_uInt16 nWhichP, const ScAddress& rCurPos, const ScAddress& rStartPos, + const ScAddress& rEndPos, const OUString& rString, const EditTextObject* pData ) : + SfxPoolItem ( nWhichP ), + aCursorPos ( rCurPos ), + aStartPos ( rStartPos ), + aEndPos ( rEndPos ), + aString ( rString ), + 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(rItem).aStartPos) + && (aEndPos == static_cast(rItem).aEndPos) + && (aCursorPos == static_cast(rItem).aCursorPos) + && (aString == static_cast(rItem).aString); + //TODO: Compare Edit data! +} + +ScInputStatusItem* ScInputStatusItem::Clone( SfxItemPool * ) const +{ + return new ScInputStatusItem( *this ); +} + +void ScInputStatusItem::SetMisspellRanges( const std::vector* 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(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(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(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(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(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(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(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(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 000000000..734ae94d6 --- /dev/null +++ b/sc/source/ui/attrdlg/attrdlg.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 . + */ + +#undef SC_DLLIMPLEMENTATION + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 ); + AddTabPage( "cellprotection" , ScTabPageProtection::Create, nullptr ); +} + +ScAttrDlg::~ScAttrDlg() +{ +} + +void ScAttrDlg::PageCreated(const OString& 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 ); + assert(pInfoItem && "FontListItem not found :-("); + aSet.Put (SvxFontListItem(static_cast(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 000000000..a8a457c5c --- /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 + +#include +#include + +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( + 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 000000000..8b59e672c --- /dev/null +++ b/sc/source/ui/attrdlg/scdlgfact.cxx @@ -0,0 +1,1370 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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(); +} + +//for dataform +short AbstractScDataFormDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx AbstractScDataFormDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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(); +} + +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(); +} + +short AbstractScMoveTableDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx AbstractScMoveTableDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractScNameCreateDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +std::vector 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 OString& sDlgHelpId, const OString& sLbHelpId ) +{ + m_xDlg->SetDescription( rTitle, rFixedText, sDlgHelpId, sLbHelpId ); +} + +std::vector 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 OString& rDlgHelpId, const OString& rEnHelpId, const OString& 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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::IsKeepAskingSet() const +{ + return m_xDlg->isKeepAskingSet(); +} + +BitmapEx AbstractScTextImportOptionsDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString 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 OString &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 ScAbstractTabController_Impl::getAllPageUIXMLDescriptions() const +{ + return m_xDlg->getAllPageUIXMLDescriptions(); +} + +bool ScAbstractTabController_Impl::selectPageByUIXMLDescription(const OString& rUIXMLDescription) +{ + return m_xDlg->selectPageByUIXMLDescription(rUIXMLDescription); +} + +BitmapEx ScAbstractTabController_Impl::createScreenshot() const +{ + return m_xDlg->createScreenshot(); +} + +OString 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 OString &rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* ScAsyncTabController_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +// =========================Factories for createdialog =================== +VclPtr ScAbstractDialogFactory_Impl::CreateScImportAsciiDlg(weld::Window* pParent, + const OUString& aDatName, + SvStream* pInStream, ScImportAsciiCall eCall) +{ + return VclPtr::Create(std::make_shared(pParent, aDatName,pInStream, eCall)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScTextImportOptionsDlg(weld::Window* pParent) +{ + return VclPtr::Create(std::make_unique(pParent)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScAutoFormatDlg(weld::Window* pParent, + ScAutoFormat* pAutoFormat, + const ScAutoFormatData* pSelFormatData, + ScViewData& rViewData) +{ + return VclPtr::Create(std::make_unique(pParent, pAutoFormat, pSelFormatData, rViewData)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScColRowLabelDlg(weld::Window* pParent, + bool bCol, bool bRow) +{ + return VclPtr::Create(std::make_unique(pParent, bCol, bRow)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScSortWarningDlg(weld::Window* pParent, const OUString& rExtendText, const OUString& rCurrentText) +{ + return VclPtr::Create(std::make_unique(pParent, rExtendText, rCurrentText)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScCondFormatMgrDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList ) +{ + return VclPtr::Create(std::make_shared(pParent, rDoc, pFormatList)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDataPilotDatabaseDlg(weld::Window* pParent) +{ + return VclPtr::Create(std::make_shared(pParent)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDataPilotSourceTypeDlg( + weld::Window* pParent, bool bEnableExternal) +{ + return VclPtr::Create(std::make_shared(pParent, bEnableExternal)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDataPilotServiceDlg(weld::Window* pParent, + const std::vector& rServices) +{ + return VclPtr::Create(std::make_shared(pParent, rServices)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDeleteCellDlg(weld::Window* pParent, + bool bDisallowCellMove) +{ + return VclPtr::Create(std::make_unique(pParent, bDisallowCellMove)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDataFormDlg(weld::Window* pParent, + ScTabViewShell* pTabViewShell) +{ + return VclPtr::Create(std::make_unique(pParent, pTabViewShell)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDeleteContentsDlg(weld::Window* pParent) +{ + return VclPtr::Create(std::make_unique(pParent)); +} + +VclPtr 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::Create(std::make_unique(pParent, rDocument,eFillDir, eFillCmd,eFillDateCmd, aStartStr,fStep,fMax,nSelectHeight,nSelectWidth,nPossDir)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateAbstractScGroupDlg(weld::Window* pParent, bool bUnGroup) +{ + return VclPtr::Create(std::make_shared(pParent, bUnGroup, true/*bRows*/)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScInsertCellDlg(weld::Window* pParent, + bool bDisallowCellMove) +{ + return VclPtr::Create(std::make_unique(pParent, bDisallowCellMove)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScInsertContentsDlg(weld::Window* pParent, + const OUString* pStrTitle) +{ + return VclPtr::Create(std::make_unique(pParent, pStrTitle)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScInsertTableDlg(weld::Window* pParent, ScViewData& rViewData, + SCTAB nTabCount, bool bFromFile) +{ + return VclPtr::Create(std::make_unique(pParent, rViewData,nTabCount, bFromFile)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScSelEntryDlg(weld::Window* pParent, + const std::vector &rEntryList) +{ + return VclPtr::Create(std::make_unique(pParent, rEntryList)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScLinkedAreaDlg(weld::Widget* pParent) +{ + return VclPtr::Create(std::make_unique(pParent)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScMetricInputDlg(weld::Window* pParent, + const OString& sDialogName, + tools::Long nCurrent, + tools::Long nDefault, + FieldUnit eFUnit, + sal_uInt16 nDecimals, + tools::Long nMaximum , + tools::Long nMinimum ) +{ + return VclPtr::Create(std::make_unique(pParent, sDialogName, nCurrent ,nDefault, eFUnit, + nDecimals, nMaximum , nMinimum)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScMoveTableDlg(weld::Window* pParent, + const OUString& rDefault) +{ + return VclPtr::Create(std::make_unique(pParent, rDefault)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScNameCreateDlg(weld::Window * pParent, CreateNameFlags nFlags) +{ + return VclPtr::Create(std::make_unique(pParent, nFlags)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScNamePasteDlg(weld::Window * pParent, ScDocShell* pShell) +{ + return VclPtr::Create(std::make_unique(pParent, pShell)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScPivotFilterDlg(weld::Window* pParent, + const SfxItemSet& rArgSet, sal_uInt16 nSourceTab) +{ + return VclPtr::Create(std::make_unique(pParent, rArgSet, nSourceTab)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDPFunctionDlg(weld::Widget* pParent, + const ScDPLabelDataVector& rLabelVec, + const ScDPLabelData& rLabelData, + const ScPivotFuncData& rFuncData) +{ + return VclPtr::Create(std::make_shared(pParent, rLabelVec, rLabelData, rFuncData)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDPSubtotalDlg(weld::Widget* pParent, + ScDPObject& rDPObj, + const ScDPLabelData& rLabelData, + const ScPivotFuncData& rFuncData, + const ScDPNameVec& rDataFields) +{ + return VclPtr::Create(std::make_shared(pParent, rDPObj, rLabelData, rFuncData, rDataFields, true/*bEnableLayout*/)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDPNumGroupDlg(weld::Window* pParent, const ScDPNumGroupInfo& rInfo) +{ + return VclPtr::Create(std::make_unique(pParent, rInfo)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDPDateGroupDlg( + weld::Window* pParent, const ScDPNumGroupInfo& rInfo, sal_Int32 nDatePart, const Date& rNullDate) +{ + return VclPtr::Create(std::make_unique(pParent, rInfo, nDatePart, rNullDate)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScDPShowDetailDlg ( + weld::Window* pParent, ScDPObject& rDPObj, css::sheet::DataPilotFieldOrientation nOrient ) +{ + return VclPtr::Create(std::make_unique(pParent, rDPObj, nOrient)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScNewScenarioDlg(weld::Window* pParent, const OUString& rName, + bool bEdit, bool bSheetProtected) +{ + return VclPtr::Create(std::make_unique(pParent, rName, bEdit, bSheetProtected)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScShowTabDlg(weld::Window* pParent) +{ + return VclPtr::Create(std::make_shared(pParent)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScGoToTabDlg(weld::Window* pParent) +{ + return VclPtr::Create(std::make_shared(pParent)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScStringInputDlg(weld::Window* pParent, + const OUString& rTitle, const OUString& rEditTitle, const OUString& rDefault, const OString& rHelpId, + const OString& rEditHelpId) +{ + return VclPtr::Create(std::make_unique(pParent, rTitle, rEditTitle, + rDefault, rHelpId, rEditHelpId)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScTabBgColorDlg( + weld::Window* pParent, + const OUString& rTitle, + const OUString& rTabBgColorNoColorText, + const Color& rDefaultColor) +{ + return VclPtr::Create(std::make_unique(pParent, rTitle, rTabBgColorNoColorText, rDefaultColor)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScImportOptionsDlg(weld::Window* pParent, + bool bAscii, + const ScImportOptions* pOptions, + const OUString* pStrTitle, + bool bOnlyDbtoolsEncodings, + bool bImport) +{ + return VclPtr::Create(std::make_unique(pParent, bAscii, pOptions, pStrTitle, true/*bMultiByte*/, bOnlyDbtoolsEncodings, bImport)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScAttrDlg(weld::Window* pParent, const SfxItemSet* pCellAttrs) +{ + return VclPtr::Create(std::make_shared(pParent, pCellAttrs)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScHFEditDlg( weld::Window* pParent, + const SfxItemSet& rCoreSet, + const OUString& rPageStyle, + sal_uInt16 nResId ) +{ + std::shared_ptr xDlg; + + switch (nResId) + { + case RID_SCDLG_HFED_HEADER: + case RID_SCDLG_HFEDIT_HEADER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFED_FOOTER: + case RID_SCDLG_HFEDIT_FOOTER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_SHAREDFIRSTHEADER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_SHAREDLEFTHEADER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_SHAREDFIRSTFOOTER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_SHAREDLEFTFOOTER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_LEFTHEADER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_RIGHTHEADER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_LEFTFOOTER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_RIGHTFOOTER: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_SHDR: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_SFTR: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + case RID_SCDLG_HFEDIT_ALL: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + default: + case RID_SCDLG_HFEDIT: + xDlg = std::make_shared(pParent, rCoreSet, rPageStyle); + break; + } + + return xDlg ? VclPtr::Create(std::move(xDlg)) : nullptr; +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScStyleDlg(weld::Window* pParent, + SfxStyleSheetBase& rStyleBase, + bool bPage) +{ + return VclPtr::Create(std::make_shared(pParent, rStyleBase, bPage)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScSubTotalDlg(weld::Window* pParent, const SfxItemSet& rArgSet) +{ + return VclPtr::Create(std::make_shared(pParent, rArgSet)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScCharDlg( + weld::Window* pParent, const SfxItemSet* pAttr, const SfxObjectShell* pDocShell, bool bDrawText) +{ + return VclPtr::Create(std::make_shared(pParent, pAttr, pDocShell, bDrawText)); +} + +VclPtr ScAbstractDialogFactory_Impl::CreateScParagraphDlg( + weld::Window* pParent, const SfxItemSet* pAttr) +{ + return VclPtr::Create(std::make_shared(pParent, pAttr)); +} + +std::shared_ptr ScAbstractDialogFactory_Impl::CreateScSortDlg(weld::Window* pParent, const SfxItemSet* pArgSet) +{ + return std::make_shared(std::make_shared(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 000000000..b1703930c --- /dev/null +++ b/sc/source/ui/attrdlg/scdlgfact.hxx @@ -0,0 +1,806 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class AbstractScImportAsciiDlg_Impl : public AbstractScImportAsciiDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScImportAsciiDlg_Impl(std::shared_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScAutoFormatDlg_Impl : public AbstractScAutoFormatDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScAutoFormatDlg_Impl(std::unique_ptr 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 m_xDlg; +public: + explicit AbstractScColRowLabelDlg_Impl(std::unique_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScCondFormatManagerDlg_Impl : public AbstractScCondFormatManagerDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScCondFormatManagerDlg_Impl(std::shared_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override; + virtual std::unique_ptr GetConditionalFormatList() override; + virtual bool CondFormatsChanged() const override; + virtual void SetModified() override; + virtual ScConditionalFormat* GetCondFormatSelected() override; +}; + +class AbstractScDataPilotDatabaseDlg_Impl :public AbstractScDataPilotDatabaseDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScDataPilotDatabaseDlg_Impl(std::shared_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScDataPilotSourceTypeDlg_Impl :public AbstractScDataPilotSourceTypeDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScDataPilotSourceTypeDlg_Impl(std::shared_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScDataPilotServiceDlg_Impl : public AbstractScDataPilotServiceDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScDataPilotServiceDlg_Impl(std::shared_ptr 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::unique_ptr m_xDlg; +public: + explicit AbstractScDeleteCellDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual DelCellCmd GetDelCellCmd() const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +//for dataform +class AbstractScDataFormDlg_Impl : public AbstractScDataFormDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScDataFormDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractScDeleteContentsDlg_Impl : public AbstractScDeleteContentsDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScDeleteContentsDlg_Impl(std::unique_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScFillSeriesDlg_Impl:public AbstractScFillSeriesDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScFillSeriesDlg_Impl(std::unique_ptr 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 m_xDlg; +public: + explicit AbstractScGroupDlg_Impl(std::shared_ptr 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::unique_ptr m_xDlg; +public: + explicit AbstractScInsertCellDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual InsCellCmd GetInsCellCmd() const override ; +}; + +class AbstractScInsertContentsDlg_Impl : public AbstractScInsertContentsDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScInsertContentsDlg_Impl(std::unique_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScInsertTableDlg_Impl : public AbstractScInsertTableDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScInsertTableDlg_Impl(std::unique_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScSelEntryDlg_Impl : public AbstractScSelEntryDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScSelEntryDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetSelectedEntry() const override; +}; + +class AbstractScLinkedAreaDlg_Impl : public AbstractScLinkedAreaDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScLinkedAreaDlg_Impl(std::unique_ptr 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::unique_ptr m_xDlg; +public: + explicit AbstractScMetricInputDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual int GetInputValue() const override; +}; + +class AbstractScMoveTableDlg_Impl : public AbstractScMoveTableDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScMoveTableDlg_Impl(std::unique_ptr 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 OString GetScreenshotId() const override; +}; + +class AbstractScNameCreateDlg_Impl : public AbstractScNameCreateDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScNameCreateDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual CreateNameFlags GetFlags() const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractScNamePasteDlg_Impl : public AbstractScNamePasteDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScNamePasteDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual std::vector GetSelectedNames() const override; +}; + +class AbstractScPivotFilterDlg_Impl : public AbstractScPivotFilterDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScPivotFilterDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual const ScQueryItem& GetOutputItem() override; +}; + +class AbstractScDPFunctionDlg_Impl : public AbstractScDPFunctionDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScDPFunctionDlg_Impl(std::shared_ptr 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 m_xDlg; +public: + explicit AbstractScDPSubtotalDlg_Impl(std::shared_ptr 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 m_xDlg; +public: + explicit AbstractScDPNumGroupDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual ScDPNumGroupInfo GetGroupInfo() const override; +}; + +class AbstractScDPDateGroupDlg_Impl : public AbstractScDPDateGroupDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScDPDateGroupDlg_Impl(std::unique_ptr 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 m_xDlg; +public: + explicit AbstractScDPShowDetailDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetDimensionName() const override; +}; + +class AbstractScNewScenarioDlg_Impl : public AbstractScNewScenarioDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScNewScenarioDlg_Impl(std::unique_ptr 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 m_xDlg; +public: + explicit AbstractScShowTabDlg_Impl(std::shared_ptr 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 OString& sDlgHelpId, const OString& sLbHelpId) override; + virtual OUString GetEntry(sal_Int32 nPos) const override; + virtual std::vector GetSelectedRows() const override; +}; + +class AbstractScGoToTabDlg_Impl : public AbstractScGoToTabDlg +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractScGoToTabDlg_Impl(std::shared_ptr 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 OString& rDlgHelpId, const OString& rEnHelpId, const OString& rLbHelpId) override; + virtual OUString GetSelectedEntry() const override; +}; + +class AbstractScSortWarningDlg_Impl : public AbstractScSortWarningDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScSortWarningDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; +}; + +class AbstractScStringInputDlg_Impl : public AbstractScStringInputDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScStringInputDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual OUString GetInputString() const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractScTabBgColorDlg_Impl : public AbstractScTabBgColorDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScTabBgColorDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual void GetSelectedColor( Color& rColor ) const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractScImportOptionsDlg_Impl : public AbstractScImportOptionsDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractScImportOptionsDlg_Impl(std::unique_ptr 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 m_xDlg; +public: + explicit AbstractScTextImportOptionsDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual LanguageType GetLanguageType() const override; + virtual bool IsDateConversionSet() const override; + virtual bool IsKeepAskingSet() const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class ScAbstractTabController_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr m_xDlg; +public: + explicit ScAbstractTabController_Impl(std::shared_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString &rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual std::vector getAllPageUIXMLDescriptions() const override; + virtual bool selectPageByUIXMLDescription(const OString& rUIXMLDescription) override; + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class ScAsyncTabController_Impl : public ScAsyncTabController +{ + std::shared_ptr m_xDlg; +public: + explicit ScAsyncTabController_Impl(std::shared_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual bool StartExecuteAsync(VclAbstractDialog::AsyncContext &rCtx) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual void SetCurPageId( const OString &rName ) override; +}; + +//AbstractDialogFactory_Impl implementations +class ScAbstractDialogFactory_Impl : public ScAbstractDialogFactory +{ + +public: + virtual ~ScAbstractDialogFactory_Impl() {} + + virtual VclPtr CreateScImportAsciiDlg(weld::Window* pParent, + const OUString& aDatName, + SvStream* pInStream, + ScImportAsciiCall eCall) override; + + virtual VclPtr CreateScTextImportOptionsDlg(weld::Window* pParent) override; + + virtual VclPtr CreateScAutoFormatDlg(weld::Window* pParent, + ScAutoFormat* pAutoFormat, + const ScAutoFormatData* pSelFormatData, + ScViewData& rViewData) override; + virtual VclPtr CreateScColRowLabelDlg (weld::Window* pParent, + bool bCol, + bool bRow) override; + + virtual VclPtr CreateScSortWarningDlg(weld::Window* pParent, const OUString& rExtendText, const OUString& rCurrentText ) override; + + virtual VclPtr CreateScCondFormatMgrDlg(weld::Window* pParent, ScDocument& rDoc, const ScConditionalFormatList* pFormatList ) override; + + virtual VclPtr CreateScDataPilotDatabaseDlg(weld::Window* pParent) override; + + virtual VclPtr CreateScDataPilotSourceTypeDlg(weld::Window* pParent, + bool bEnableExternal) override; + + virtual VclPtr CreateScDataPilotServiceDlg(weld::Window* pParent, + const std::vector& rServices) override; + virtual VclPtr CreateScDeleteCellDlg(weld::Window* pParent, bool bDisallowCellMove ) override; + + //for dataform + virtual VclPtr CreateScDataFormDlg(weld::Window* pParent, ScTabViewShell* pTabViewShell) override; + + virtual VclPtr CreateScDeleteContentsDlg(weld::Window* pParent) override; + + virtual VclPtr 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 CreateAbstractScGroupDlg(weld::Window* pParent, bool bUnGroup = false) override; + + virtual VclPtr CreateScInsertCellDlg(weld::Window* pParent, + bool bDisallowCellMove) override; + + virtual VclPtr CreateScInsertContentsDlg(weld::Window* pParent, + const OUString* pStrTitle = nullptr) override; + + virtual VclPtr CreateScInsertTableDlg(weld::Window* pParent, ScViewData& rViewData, + SCTAB nTabCount, bool bFromFile) override; + + virtual VclPtr CreateScSelEntryDlg(weld::Window* pParent, const std::vector &rEntryList) override; + + virtual VclPtr CreateScLinkedAreaDlg(weld::Widget* pParent) override; + + virtual VclPtr CreateScMetricInputDlg(weld::Window* pParent, + const OString& sDialogName, + tools::Long nCurrent, + tools::Long nDefault, + FieldUnit eFUnit, + sal_uInt16 nDecimals, + tools::Long nMaximum, + tools::Long nMinimum = 0 ) override; + + virtual VclPtr CreateScMoveTableDlg(weld::Window * pParent, + const OUString& rDefault) override; + + virtual VclPtr CreateScNameCreateDlg(weld::Window * pParent, + CreateNameFlags nFlags) override; + + virtual VclPtr CreateScNamePasteDlg(weld::Window * pParent, ScDocShell* pShell) override; + + virtual VclPtr CreateScPivotFilterDlg(weld::Window* pParent, const SfxItemSet& rArgSet, + sal_uInt16 nSourceTab) override; + + virtual VclPtr CreateScDPFunctionDlg(weld::Widget* pParent, + const ScDPLabelDataVector& rLabelVec, + const ScDPLabelData& rLabelData, + const ScPivotFuncData& rFuncData ) override; + + virtual VclPtr CreateScDPSubtotalDlg(weld::Widget* pParent, + ScDPObject& rDPObj, + const ScDPLabelData& rLabelData, + const ScPivotFuncData& rFuncData, + const ScDPNameVec& rDataFields ) override; + + virtual VclPtr CreateScDPNumGroupDlg(weld::Window* pParent, + const ScDPNumGroupInfo& rInfo) override; + + virtual VclPtr CreateScDPDateGroupDlg(weld::Window* pParent, + const ScDPNumGroupInfo& rInfo, + sal_Int32 nDatePart, + const Date& rNullDate) override; + + virtual VclPtr CreateScDPShowDetailDlg(weld::Window* pParent, + ScDPObject& rDPObj, + css::sheet::DataPilotFieldOrientation nOrient) override; + + virtual VclPtr CreateScNewScenarioDlg(weld::Window* pParent, const OUString& rName, + bool bEdit, bool bSheetProtected) override; + virtual VclPtr CreateScShowTabDlg(weld::Window* pParent) override; + virtual VclPtr CreateScGoToTabDlg(weld::Window* pParent) override; + + virtual VclPtr CreateScStringInputDlg(weld::Window* pParent, + const OUString& rTitle, + const OUString& rEditTitle, + const OUString& rDefault, + const OString& rHelpId, + const OString& rEditHelpId) override; + + virtual VclPtr 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 CreateScImportOptionsDlg(weld::Window* pParent, bool bAscii, + const ScImportOptions* pOptions, + const OUString* pStrTitle, + bool bOnlyDbtoolsEncodings, + bool bImport = true) override; + + virtual VclPtr CreateScAttrDlg(weld::Window* pParent, + const SfxItemSet* pCellAttrs) override; + + virtual VclPtr CreateScHFEditDlg(weld::Window* pParent, + const SfxItemSet& rCoreSet, + const OUString& rPageStyle, + sal_uInt16 nResId ) override; + + virtual VclPtr CreateScStyleDlg(weld::Window* pParent, + SfxStyleSheetBase& rStyleBase, + bool bPage) override; + + virtual VclPtr CreateScSubTotalDlg(weld::Window* pParent, + const SfxItemSet& rArgSet) override; + virtual VclPtr CreateScCharDlg(weld::Window* pParent, + const SfxItemSet* pAttr, const SfxObjectShell* pDocShell, bool bDrawText) override; + + virtual VclPtr CreateScParagraphDlg(weld::Window* pParent, + const SfxItemSet* pAttr) override; + + virtual std::shared_ptr 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 000000000..4351539a9 --- /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 + +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 000000000..af0ed1d6a --- /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 +#include + +#include +#include + +const WhichRangesContainer ScTabPageProtection::pProtectionRanges( + svl::Items); + +// 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 ScTabPageProtection::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rAttrSet) +{ + return std::make_unique(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(&pProtAttr) ); + + // Is this a Default-Item? + if ( eItemState == SfxItemState::DEFAULT ) + pProtAttr = static_cast(&(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(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 000000000..29ab64e84 --- /dev/null +++ b/sc/source/ui/cctrl/cbnumberformat.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 +#include +#include +#include +#include +#include +#include +#include + +ScNumberFormat::ScNumberFormat(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/scalc/ui/numberbox.ui", "NumberBox", true, + reinterpret_cast(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 000000000..b7f99f731 --- /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 +#include +#include +#include +#include +#include + + +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(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 000000000..82e7488c9 --- /dev/null +++ b/sc/source/ui/cctrl/checklistmenu.cxx @@ -0,0 +1,1737 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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(), 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 aWidths + { + o3tl::narrowing(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() +{ + int nWidth = (mxMenu->get_text_height() * 3) / 4; + mxDropDown->SetOutputSizePixel(Size(nWidth, nWidth)); + DecorationView aDecoView(mxDropDown.get()); + aDecoView.DrawSymbol(tools::Rectangle(Point(0, 0), Size(nWidth, nWidth)), + SymbolType::SPIN_RIGHT, mxDropDown->GetTextColor(), + 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::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()); + 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) + , 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) + : 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")) + , 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) +{ + 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(); + 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)); + 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); +} + +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() +{ + 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){ + 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 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); + + OString 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()) + { + selectCurrentMemberOnly(&rBtn == mxBtnSelectSingle.get()); + std::unique_ptr xEntry = mpChecks->make_iterator(); + if (!mpChecks->get_cursor(xEntry.get())) + xEntry.reset(); + 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); + } +} + +IMPL_LINK_NOARG(ScCheckListMenuControl, EdModifyHdl, weld::Entry&, void) +{ + OUString aSearchText = mxEdSearch->get_text(); + aSearchText = ScGlobal::getCharClass().lowercase( aSearchText ); + bool bSearchTextEmpty = aSearchText.isEmpty(); + size_t n = maMembers.size(); + 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 < n; ++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 < n; ++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 aShownIndexes; + + for (size_t i = 0; i < n; ++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 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 == n ) + 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, 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(); + if (nNumChecked == maMembers.size()) + // 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 xYearEntry; + std::unique_ptr 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 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) +{ + 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 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 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); + ScCheckListMember aMemYear; + aMemYear.maName = aYearName; + aMemYear.maRealName = rsName; + aMemYear.mbDate = true; + aMemYear.mbLeaf = false; + aMemYear.mbVisible = bVisible; + aMemYear.mxParent.reset(); + aMemYear.meDatePartType = ScCheckListMember::YEAR; + maMembers.emplace_back(std::move(aMemYear)); + } + + std::unique_ptr 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); + ScCheckListMember aMemMonth; + aMemMonth.maName = aMonthName; + aMemMonth.maRealName = rsName; + aMemMonth.mbDate = true; + aMemMonth.mbLeaf = false; + aMemMonth.mbVisible = bVisible; + aMemMonth.mxParent = std::move(xYearEntry); + aMemMonth.meDatePartType = ScCheckListMember::MONTH; + maMembers.emplace_back(std::move(aMemMonth)); + maYearMonthMap[aYearName + aMonthName] = maMembers.size() - 1; + } + + std::unique_ptr 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); + 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.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 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.mxParent.reset(); + maMembers.emplace_back(std::move(aMember)); +} + +std::unique_ptr ScCheckListMenuControl::FindEntry(const weld::TreeIter* pParent, std::u16string_view sNode) +{ + std::unique_ptr 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& 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 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 ScCheckListMenuControl::GetAllChecked() +{ + std::unordered_set vResults(0); + + std::unique_ptr 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 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 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 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 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 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 ScCheckListMenuControl::ShowCheckEntry(const OUString& sName, ScCheckListMember& rMember, bool bShow, bool bCheck) +{ + std::unique_ptr 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 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 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 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 xParent(mpChecks->make_iterator(rMember.mxParent.get())); + while (xParent && !mpChecks->iter_has_child(*xParent)) + { + std::unique_ptr 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 xEntry = mpChecks->make_iterator(); + bool bEntry = mpChecks->get_cursor(xEntry.get()); + if (bEntry) + { + 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 nVisMemCount = 0; + + if (nMaxMemberWidth == -1) + nMaxMemberWidth = mnCheckWidthReq; + + if (!mpChecks->n_children() && !mbHasDates) + { + std::vector 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 xEntry = mpChecks->make_iterator(); + std::vector> 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 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 == n) + { + // 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 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 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; + 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 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); +} + +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(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 000000000..4c9fbbc6a --- /dev/null +++ b/sc/source/ui/cctrl/dpcontrol.cxx @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +ScDPFieldButton::ScDPFieldButton(OutputDevice* pOutDev, const StyleSettings* pStyle, const Fraction* pZoomY, ScDocument* pDoc) : + mpDoc(pDoc), + mpOutDev(pOutDev), + mpStyle(pStyle), + mbBaseButton(true), + mbPopupButton(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::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). + GetFont( aAttrFont, SC_AUTOCOL_BLACK, 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) + drawPopupButton(); + + 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(maZoomY) > 1.0 ? static_cast(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::drawPopupButton() +{ + Point aPos; + Size aSize; + getPopupBoundingBox(aPos, aSize); + + float fScaleFactor = mpOutDev->GetDPIScaleFactor(); + + // Background & outer black border + mpOutDev->SetLineColor(COL_BLACK); + Color aBackgroundColor + = mbHasHiddenMember ? mpStyle->GetHighlightColor() + : mbPopupPressed ? mpStyle->GetShadowColor() : mpStyle->GetFaceColor(); + 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)); + } +} + +/* 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 000000000..fd9d1e6b0 --- /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 +#include +#include +#include +#include + +namespace { + +sal_Unicode lclGetDecSep() +{ + return ScGlobal::getLocaleData().getNumDecimalSep()[0]; +} + +} // namespace + +ScDoubleField::ScDoubleField(std::unique_ptr 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 000000000..7d3443bb0 --- /dev/null +++ b/sc/source/ui/cctrl/tbzoomsliderctrl.cxx @@ -0,0 +1,457 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// 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(rTbx.GetItemWindow( nId )); + OSL_ENSURE( pBox ,"Control not found!" ); + + if ( SfxItemState::DEFAULT != eState || pState->IsVoidItem() ) + { + SvxZoomSliderItem aZoomSliderItem( 100 ); + pBox->Disable(); + pBox->UpdateFromItem( &aZoomSliderItem ); + } + else + { + pBox->Enable(); + OSL_ENSURE( dynamic_cast( 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 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 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(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(const css::uno::Reference< css::frame::XDispatchProvider>& rDispatchProvider, + sal_uInt16 nCurrentZoom) + : mnCurrentZoom( nCurrentZoom ), + mnMinZoom( 10 ), + mnMaxZoom( 400 ), + mbOmitPaint( false ), + m_xDispatchProvider(rDispatchProvider) +{ + 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(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(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 000000000..920fee3c0 --- /dev/null +++ b/sc/source/ui/condformat/colorformat.cxx @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#include + +#include +#include +#include +#include + +namespace { + +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(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")) + , mxBtnCancel(m_xBuilder->weld_button("cancel")) + , 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 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 000000000..b4759bb39 --- /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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ScCondFormatList::ScCondFormatList(ScCondFormatDlg* pDialogParent, + std::unique_ptr xWindow, + std::unique_ptr 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( 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( 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( pEntry ) ) ); + break; + case ScFormatEntry::Type::Iconset: + maEntries.emplace_back(new ScIconSetFrmtEntry( this, mpDoc, maPos, static_cast( pEntry ) ) ); + break; + case ScFormatEntry::Type::Date: + maEntries.emplace_back(new ScDateFrmtEntry( this, mpDoc, static_cast( 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 ScCondFormatList::GetConditionalFormat() const +{ + if(maEntries.empty()) + return nullptr; + + std::unique_ptr 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(p); + EntryContainer::iterator itr = std::find_if(maEntries.begin(), maEntries.end(), + [](const std::unique_ptr& 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(p); + EntryContainer::iterator itr = std::find_if(maEntries.begin(), maEntries.end(), + [](const std::unique_ptr& 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& 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.Justify(); // 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 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 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 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().Put(*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().Put(*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 000000000..77175fa64 --- /dev/null +++ b/sc/source/ui/condformat/condformatdlgentry.cxx @@ -0,0 +1,1530 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +// 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.toUtf8()); +} + +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 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() ); + std::unique_ptr ta(aComp.CompileString(aFormula)); + + // Error, warn the user + if( ta->GetCodeError() != FormulaError::NONE || ( ta->GetLen() == 0 ) ) + { + rEdit.set_message_type(weld::EntryMessageType::Error); + mxFtVal->set_label(ScResId(STR_VALID_DEFERROR)); + return; + } + + // Recognized col/row name or string token, warn the user + formula::FormulaToken* token = ta->FirstToken(); + formula::StackVar t = token->GetType(); + OpCode op = token->GetOpCode(); + if( ( op == ocColRowName ) || + ( ( op == ocBad ) && ( t == formula::svString ) ) + ) + { + rEdit.set_message_type(weld::EntryMessageType::Warning); + mxFtVal->set_label(ScResId(STR_UNQUOTED_STRING)); + 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(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(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 mxBuilder; +private: + std::unique_ptr mxGrid; + std::unique_ptr mxImgIcon; + std::unique_ptr mxFtEntry; + std::unique_ptr mxEdEntry; + std::unique_ptr 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(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(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(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 000000000..b0bf511c3 --- /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 + +#include +#include + +ScCondFormatDlgItem::ScCondFormatDlgItem(std::shared_ptr 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/condformathelper.cxx b/sc/source/ui/condformat/condformathelper.cxx new file mode 100644 index 000000000..00509b7e2 --- /dev/null +++ b/sc/source/ui/condformat/condformathelper.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/. + */ + +#include + +#include +#include +#include +#include +#include +#include + +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(rFormat.GetEntry(0)); + ScConditionMode eMode = pEntry->GetOperation(); + if(eMode == ScConditionMode::Direct) + { + aBuffer.append(getTextForType(FORMULA)); + aBuffer.append(" "); + aBuffer.append(pEntry->GetExpression(rPos, 0)); + } + else + { + aBuffer.append(getTextForType(CONDITION)); + aBuffer.append(" "); + aBuffer.append(getExpression(static_cast(eMode))); + aBuffer.append(" "); + if(eMode == ScConditionMode::Between || eMode == ScConditionMode::NotBetween) + { + aBuffer.append(pEntry->GetExpression(rPos, 0)); + aBuffer.append(" "); + aBuffer.append(ScResId(STR_COND_AND)); + aBuffer.append(" "); + aBuffer.append(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: + { + aBuffer.append(getTextForType(DATE)); + aBuffer.append(" "); + sal_Int32 nDateEntry = static_cast(static_cast(rFormat.GetEntry(0))->GetDateType()); + aBuffer.append(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)); + aBuffer.append(" "); + 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(" "); + aBuffer.append(ScResId(STR_COND_AND)); + aBuffer.append(" "); + aBuffer.append(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 000000000..79f41bf5c --- /dev/null +++ b/sc/source/ui/condformat/condformatmgr.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/. + */ + +#include +#include +#include +#include +#include +#include +#include + +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 aWidths + { + o3tl::narrowing(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().toUtf8()); + + UpdateButtonSensitivity(); +} + +ScCondFormatManagerDlg::~ScCondFormatManagerDlg() +{ + // tdf#101285 - Remember position of dialog + SvtViewOptions aDlgOpt(EViewType::Dialog, "CondFormatDialog"); + OString sWindowState + = m_xDialog->get_window_state(WindowStateMask::Pos); + aDlgOpt.SetWindowState(OUString::fromUtf8(sWindowState)); +} + +std::unique_ptr 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 000000000..29391c378 --- /dev/null +++ b/sc/source/ui/dataprovider/csvdataprovider.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/. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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(const char* p, size_t n, bool /*transient*/) + { + if (mnCol > mpDoc->MaxCol()) + return; + + double mfValue = 0.0; + if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', mfValue)) + { + mpDoc->SetValue(mnCol, mnRow, 0, mfValue); + } + else + { + OString aStr(p, n); + mpDoc->SetString(mnCol, mnRow, 0, OStringToOUString(aStr, RTL_TEXTENCODING_UTF8)); + } + + ++mnCol; + } +}; + +} + +namespace sc { +CSVFetchThread::CSVFetchThread( + ScDocument& rDoc, const OUString& mrURL, std::function aImportFinishedHdl, + std::vector>&& rDataTransformations) + : Thread("CSV Fetch Thread") + , mrDocument(rDoc) + , maURL(mrURL) + , mbTerminate(false) + , maDataTransformations(std::move(rDataTransformations)) + , maImportFinishedHdl(std::move(aImportFinishedHdl)) +{ + maConfig.delimiters.push_back(','); + maConfig.text_qualifier = '"'; +} + +CSVFetchThread::~CSVFetchThread() +{ +} + +bool CSVFetchThread::IsRequestedTerminate() +{ + osl::MutexGuard aGuard(maMtxTerminate); + return mbTerminate; +} + +void CSVFetchThread::RequestTerminate() +{ + osl::MutexGuard aGuard(maMtxTerminate); + mbTerminate = true; +} + +void CSVFetchThread::EndThread() +{ + RequestTerminate(); +} + +void CSVFetchThread::execute() +{ + OStringBuffer aBuffer(64000); + DataProvider::FetchStreamFromURL(maURL, aBuffer); + if (mbTerminate) + return; + + CSVHandler aHdl(&mrDocument); + orcus::csv_parser parser(aBuffer.getStr(), aBuffer.getLength(), 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 = static_cast(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 000000000..72f674a7b --- /dev/null +++ b/sc/source/ui/dataprovider/dataprovider.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/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "htmldataprovider.hxx" +#include "xmldataprovider.hxx" +#include "sqldataprovider.hxx" +#include +#include +#include + +using namespace com::sun::star; + +namespace sc { + +std::unique_ptr 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(rBuffer.getStr()), rBuffer.getLength(), StreamMode::READ); + return std::unique_ptr(pStream); + } + catch(...) + { + rBuffer.setLength(0); + return nullptr; + } +} + +ExternalDataSource::ExternalDataSource(const OUString& rURL, + const OUString& rProvider, ScDocument* pDoc) + : maURL(rURL) + , maProvider(rProvider) + , 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(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& mpDataTransformation) +{ + maDataTransformations.push_back(mpDataTransformation); +} + +const std::vector>& 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& ExternalDataMapper::getDataSources() const +{ + return maDataSources; +} + +std::vector& 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(aDestRange.aEnd.Col() - aDestRange.aStart.Col(), nEndCol); + aDestRange.aEnd.SetCol(aDestRange.aStart.Col() + nColSize); + + SCROW nRowSize = std::min(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 = static_cast(mpDoc->GetDocumentShell()); + if (pDocShell) + pDocShell->PostPaint(aDestRange, PaintPartFlags::All); +} + +ScDBDataManager::ScDBDataManager(const OUString& rDBName, ScDocument* pDoc): + maDBName(rDBName), + 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 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(pDoc, rDataSource); + else if (rDataProvider == "org.libreoffice.calc.html") + return std::make_shared(pDoc, rDataSource); + else if (rDataProvider == "org.libreoffice.calc.xml") + return std::make_shared(pDoc, rDataSource); + else if (rDataProvider == "org.libreoffice.calc.sql") + return std::make_shared(pDoc, rDataSource); + } + else + { + SAL_WARN("sc", "no external data provider supported yet"); + return std::shared_ptr(); + } + + return std::shared_ptr(); +} + +std::vector DataProviderFactory::getDataProviders() +{ + std::vector 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 000000000..62c82adb9 --- /dev/null +++ b/sc/source/ui/dataprovider/datatransformation.cxx @@ -0,0 +1,1275 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +Date getDate(double nDateTime, const SvNumberFormatter* pFormatter) +{ + Date aDate = pFormatter->GetNullDate(); + aDate.AddDays(static_cast(::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&& 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 & 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&& rColumns, const OUString& rMergeString): + maColumns(std::move(rColumns)), + maMergeString(rMergeString) +{ +} + +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 & 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&& 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& TextTransformation::getColumns() const +{ + return mnCol; +} + +AggregateFunction::AggregateFunction(std::set&& 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::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::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& AggregateFunction::getColumns() const +{ + return maColumns; +} + +NumberTransformation::NumberTransformation(std::set&& nCol, + const NUMBER_TRANSFORM_TYPE rType) + : mnCol(std::move(nCol)) + , maType(rType) + , maPrecision(-1) +{ +} + +NumberTransformation::NumberTransformation(std::set&& 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, rtl::math::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& NumberTransformation::getColumn() const +{ + return mnCol; +} + +ReplaceNullTransformation::ReplaceNullTransformation(std::set&& nCol, + const OUString& sReplaceWith) + : mnCol(std::move(nCol)) + , msReplaceWith(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& 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&& 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& DateTimeTransformation::getColumn() const +{ + return mnCol; +} + +FindReplaceTransformation::FindReplaceTransformation(SCCOL nCol, const OUString& aFindString, const OUString& aReplaceString) + : mnCol(nCol) + , maFindString(aFindString) + , maReplaceString(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, const OUString& aFindString) + : mnCol(nCol) + , maFindString(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 000000000..8b241ddfe --- /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 +#include +#include +#include +#include + +#include + +#include + +#include +#include + +namespace sc { + +class HTMLFetchThread : public salhelper::Thread +{ + ScDocument& mrDocument; + OUString maURL; + OUString maID; + const std::vector> maDataTransformations; + std::function 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 aImportFinishedHdl, + std::vector>&& rTransformations); + + virtual void execute() override; +}; + +HTMLFetchThread::HTMLFetchThread( + ScDocument& rDoc, const OUString& rURL, const OUString& rID, + std::function aImportFinishedHdl, + std::vector>&& 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(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(const_cast(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 000000000..f978ebdd1 --- /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 + +namespace sc +{ +class HTMLFetchThread; + +class HTMLDataProvider : public DataProvider +{ +private: + ScDocument* mpDocument; + rtl::Reference 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 000000000..a7d27dfa9 --- /dev/null +++ b/sc/source/ui/dataprovider/sqldataprovider.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 "sqldataprovider.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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> maDataTransformations; + std::function maImportFinishedHdl; + +public: + SQLFetchThread(ScDocument& rDoc, const OUString& rID, std::function aImportFinishedHdl, + std::vector>&& rTransformations); + + virtual void execute() override; +}; + +SQLFetchThread::SQLFetchThread( + ScDocument& rDoc, const OUString& rID, std::function aImportFinishedHdl, + std::vector>&& 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 xContext + = sdb::DatabaseContext::create(comphelper::getProcessComponentContext()); + uno::Any aSourceAny = xContext->getByName(aDatabase); + + uno::Reference xSource(aSourceAny, uno::UNO_QUERY); + if (!xSource.is()) + return; + + uno::Reference xHandler( + task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), + nullptr), + uno::UNO_QUERY_THROW); + + uno::Reference xConnection = xSource->connectWithCompletion(xHandler); + + uno::Reference xStatement = xConnection->createStatement(); + + uno::Reference xResult + = xStatement->executeQuery("SELECT * FROM " + aTable); + + if (xResult.is()) + { + Reference xMetaDataSupplier(xResult, UNO_QUERY); + + Reference xMetaData = xMetaDataSupplier->getMetaData(); + + Reference xRow(xResult, UNO_QUERY); + + SCCOL nColCount = static_cast(xMetaData->getColumnCount()); + + while (xResult->next()) + { + SCROW nRow = static_cast(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 000000000..fbbca7601 --- /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 + +namespace sc +{ +class SQLFetchThread; + +class SQLDataProvider : public DataProvider +{ +private: + ScDocument* mpDocument; + rtl::Reference 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 000000000..4ffa45bd1 --- /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 +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sc +{ +class XMLFetchThread : public salhelper::Thread +{ + ScDocument& mrDocument; + OUString maURL; + OUString maID; + ScOrcusImportXMLParam maParam; + std::unique_ptr mpXMLContext; + const std::vector> maDataTransformations; + std::function maImportFinishedHdl; + +public: + XMLFetchThread(ScDocument& rDoc, const OUString&, const ScOrcusImportXMLParam& rParam, + const OUString& rID, std::function aImportFinishedHdl, + std::vector>&& rTransformations); + virtual void execute() override; +}; + +XMLFetchThread::XMLFetchThread( + ScDocument& rDoc, const OUString& rURL, const ScOrcusImportXMLParam& rParam, + const OUString& rID, std::function aImportFinishedHdl, + std::vector>&& 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. + mpXMLContext->importXML(maParam); + + for (auto& itr : maDataTransformations) + { + itr->Transform(mrDocument); + } + + SolarMutexGuard aGuard; + 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 000000000..7be3f95c3 --- /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 + +namespace sc +{ +class XMLFetchThread; + +class XMLDataProvider : public DataProvider +{ +private: + ScDocument* mpDocument; + rtl::Reference 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 000000000..9244a4d7e --- /dev/null +++ b/sc/source/ui/dbgui/PivotLayoutDialog.cxx @@ -0,0 +1,719 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace css::uno; +using namespace css::sheet; + +ScItemValue::ScItemValue(OUString const & aName, SCCOL nColumn, PivotFunc nFunctionMask) : + maName(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")) + , 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()) + , mxOptions(m_xBuilder->weld_expander("options")) + , mxMore(m_xBuilder->weld_expander("more")) +{ + // Source UI + Link aLink2 = LINK(this, ScPivotLayoutDialog, ToggleSource); + mxSourceRadioNamedRange->connect_toggled(aLink2); + mxSourceRadioSelection->connect_toggled(aLink2); + + mxSourceEdit->SetReferences(this, mxSourceLabel.get()); + mxSourceButton->SetReferences(this, mxSourceEdit.get()); + + Link 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 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); + } + else + { + mxCheckAddFilter->set_active(pSaveData->GetFilterButton()); + mxCheckDrillToDetail->set_active(pSaveData->GetDrillDown()); + } + + 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(maPivotTableObject)); + + + SfxDispatcher* pDispatcher = GetBindings().GetDispatcher(); + SfxCallMode const nCallMode = SfxCallMode::SLOT | SfxCallMode::RECORD; + const SfxPoolItem* pResult = pDispatcher->ExecuteList(SID_PIVOT_TABLE, + nCallMode, { &aPivotItem }); + + if (pResult != nullptr) + { + // 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()); + + Reference 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 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& 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 000000000..c173d7ca7 --- /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 +#include +#include + +#include +#include + +ScPivotLayoutTreeList::ScPivotLayoutTreeList(std::unique_ptr 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(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(pItemValue)); + OUString sId(weld::toId(pItemValue)); + mxControl->append(sId, pItemValue->maName); + } +} + +void ScPivotLayoutTreeList::InsertEntryForSourceTarget(weld::TreeView& rSource, int nTarget) +{ + ScItemValue* pItemValue = weld::fromId(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(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 000000000..45af29a4f --- /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 +#include + +ScPivotLayoutTreeListBase::ScPivotLayoutTreeListBase(std::unique_ptr 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 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 xEachEntry(mxControl->make_iterator()); + if (!mxControl->get_iter_first(*xEachEntry)) + return; + do + { + ScItemValue* pItemValue = weld::fromId(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 000000000..19992fc31 --- /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 +#include + +#include +#include + +#include +#include +#include +#include + +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 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(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, + rCurrentLabelData, nEntry](int nResult) mutable { + if (nResult == RET_OK) + { + ScPivotFuncData& rFunctionData = pCurrentItemValue->maFunctionData; + rFunctionData.mnFuncMask = mpFunctionDlg->GetFuncMask(); + rCurrentLabelData.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(pItemValue)); + OUString sId(weld::toId(pItemValue)); + mxControl->append(sId, sDataItemName); + } +} + +void ScPivotLayoutTreeListData::PushDataFieldNames(std::vector& rDataFieldNames) +{ + std::unique_ptr xLoopEntry(mxControl->make_iterator()); + if (!mxControl->get_iter_first(*xLoopEntry)) + return; + + do + { + ScItemValue* pEachItemValue = weld::fromId(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(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(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 xEachEntry(mxControl->make_iterator()); + if (!mxControl->get_iter_first(*xEachEntry)) + return; + do + { + ScItemValue* pItemValue = weld::fromId(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 000000000..e4a2276da --- /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 +#include +#include + +#include +#include + +ScPivotLayoutTreeListLabel::ScPivotLayoutTreeListLabel(std::unique_ptr 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 const & pLabelData : rLabelVector) + { + ScItemValue* pValue = new ScItemValue(pLabelData->maName, pLabelData->mnCol, pLabelData->mnFuncMask); + maItemValues.push_back(std::unique_ptr(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 000000000..933491efb --- /dev/null +++ b/sc/source/ui/dbgui/asciiopt.cxx @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +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), + bEvaluateFormulas(true), + bSkipEmptyCells(false), + bSaveAsShown(true), + bSaveFormulas(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(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(o3tl::toInt32(o3tl::getToken(aToken, 0, '/', nP))); + } + } + + // Token 5: Language. + if (nPos >= 0) + { + eLang = static_cast(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 +} + +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(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 ) + ); + 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 000000000..1dde53c18 --- /dev/null +++ b/sc/source/ui/dbgui/consdlg.cxx @@ -0,0 +1,535 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace +{ + void INFOBOX(weld::Window* pWindow, TranslateId id) + { + std::unique_ptr 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( + rArgSet.Get( rArgSet.GetPool()-> + GetWhich( SID_CONSOLIDATE ) ) + ).GetData() ) + , rViewData ( static_cast(SfxViewShell::Current())-> + GetViewData() ) + , rDoc ( static_cast(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_xExpander(m_xBuilder->weld_expander("more")) + , 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; iappend_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; + (iappend_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 pDataAreas(new ScArea[nDataAreaCount]); + + for ( sal_Int32 i=0; iget_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 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; ifind_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 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 000000000..409e898b4 --- /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 +#include +#include +#include + +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( 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( 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 000000000..6752fcb78 --- /dev/null +++ b/sc/source/ui/dbgui/csvgrid.cxx @@ -0,0 +1,1418 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// *** edit engine *** +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// *** 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 xPopup, ScCsvTableBox* pTableBox) + : ScCsvControl(rData) + , mpTableBox(pTableBox) + , mpBackgrDev( VclPtr::Create() ) + , mpGridDev( VclPtr::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); + 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 ).nColor; + + 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 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&& 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(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 ) +{ + OString 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& 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(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& 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& 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 ); + + /* #i60296# If string contains mixed script types, the space character + U+0020 may be drawn with a wrong width (from non-fixed-width Asian or + Complex font). Now we draw every non-space portion separately. */ + sal_Int32 nCharIxInt {aPlainText.isEmpty() ? -1 : 0}; + while (nCharIxInt>=0) + { + sal_Int32 nBeginIx = nCharIxInt; + const OUString aToken = aPlainText.getToken( 0, ' ', nCharIxInt ); + if( !aToken.isEmpty() ) + { + sal_Int32 nX = rPos.X() + GetCharWidth() * nBeginIx; + mpEditEngine->SetTextCurrentDefaults( aToken ); + mpEditEngine->Draw(*mpBackgrDev, Point(nX, rPos.Y())); + } + } + + 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& 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 ScCsvGrid::CreateAccessible() +{ + rtl::Reference 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 000000000..3af7645e2 --- /dev/null +++ b/sc/source/ui/dbgui/csvruler.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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; + +constexpr OUStringLiteral SEP_PATH = u"Office.Calc/Dialogs/CSVImport"; +constexpr OUStringLiteral FIXED_WIDTH_LIST = u"FixedWidthList"; + +static void load_FixedWidthList(ScCsvSplits &rSplits) +{ + SequenceaValues; + const Any *pProperties; + Sequence 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(rSplits[i]); + sSplits.append(";"); + } + + OUString sFixedWidthLists = sSplits.makeStringAndClear(); + Sequence aValues; + Any *pProperties; + Sequence 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 ScCsvRuler::CreateAccessible() +{ + rtl::Reference 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 000000000..575bd53d0 --- /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 + +#include + +#include + +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(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 000000000..10dba1b81 --- /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 +#include + +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 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 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(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 000000000..198d82783 --- /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 +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +#include +#include + +// 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 xContext = sdb::DatabaseContext::create( + comphelper::getProcessComponentContext() ); + const uno::Sequence 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 xContext = sdb::DatabaseContext::create( + comphelper::getProcessComponentContext() ); + + uno::Any aSourceAny = xContext->getByName( aDatabaseName ); + uno::Reference xSource(aSourceAny, uno::UNO_QUERY); + if ( !xSource.is() ) return; + + uno::Reference xHandler( + task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), nullptr), + uno::UNO_QUERY_THROW); + + uno::Reference xConnection = xSource->connectWithCompletion( xHandler ); + + uno::Reference xItems; + if ( nSelect == DP_TYPELIST_TABLE ) + { + // get all tables + + uno::Reference xTablesSupp( xConnection, uno::UNO_QUERY ); + if ( !xTablesSupp.is() ) return; + + xItems = xTablesSupp->getTables(); + } + else + { + // get all queries + + uno::Reference xQueriesSupp( xConnection, uno::UNO_QUERY ); + if ( !xQueriesSupp.is() ) return; + + xItems = xQueriesSupp->getQueries(); + } + + if ( !xItems.is() ) return; + + // fill list + const uno::Sequence 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 000000000..9843a2bb9 --- /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 +#include + +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& 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 000000000..a2ac72209 --- /dev/null +++ b/sc/source/ui/dbgui/dbnamdlg.cxx @@ -0,0 +1,645 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class DBSaveData; + +} + +static std::unique_ptr xSaveObj; + +namespace +{ + void ERRORBOX(weld::Window* pParent, const OUString& rString) + { + std::unique_ptr 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; + } +} + + +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_xExpander(m_xBuilder->weld_expander("more")) +{ + 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; + aBuf.append(aStrSource); + if (pDBData) + { + aBuf.append(' '); + aBuf.append(pDBData->GetSourceString()); + } + m_xFTSource->set_label(aBuf.makeStringAndClear()); + + aBuf.append(aStrOperations); + if (pDBData) + { + aBuf.append(' '); + 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 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 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 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 000000000..550695cc3 --- /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 +#include +#include +#include + +#include +#include + +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( 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 000000000..2af415670 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define QUERY_ENTRY_COUNT 4 +#define INVALID_HEADER_POS std::numeric_limits::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)) + , aStrTextColor(ScResId(SCSTR_FILTER_TEXT_COLOR_COND)) + , aStrBackgroundColor(ScResId(SCSTR_FILTER_BACKGROUND_COLOR_COND)) + , nWhichQuery(rArgSet.GetPool()->GetWhich(SID_QUERY)) + , theQueryData(static_cast(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(aStrTextColor) != -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 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( + 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(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(rEntry.eOp); + nFieldSelPos = GetFieldSelPos( static_cast(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() ? aStrTextColor : 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(nFieldSelPos) - 1) : static_cast(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(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(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(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 xWaiter; + std::vector 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(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 r = + m_EntryLists.insert(std::make_pair(nColumn, std::make_unique())); + 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::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(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(nFieldSelPos) - 1; + EntryList* pList = m_EntryLists[nColumn].get(); + if (!pList) + return; + + std::set aColors; + OUString sSelectedCondition = maCondLbArr[nPos]->get_active_text(); + if (sSelectedCondition == aStrTextColor) + 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 == aStrTextColor + ? 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 == aStrTextColor && 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(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(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 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(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(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(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(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(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(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(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(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(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(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(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(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() == aStrTextColor || rLb.get_active_text() == aStrBackgroundColor) + { + bEnableColorLb = true; + op = SC_EQUAL; + } + else + { + op = static_cast(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() == aStrTextColor) + { + 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(nField) - 1) : static_cast(0); + + ScQueryOp eOp = static_cast(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(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(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(rEntry.eOp); + if(rEntry.bDoQuery) + nFieldSelPos = GetFieldSelPos( static_cast(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() ? aStrTextColor : 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(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(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 000000000..decaa622b --- /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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +// 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; iget_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 000000000..b285c6ae9 --- /dev/null +++ b/sc/source/ui/dbgui/imoptdlg.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 +#include +#include +#include +#include +#include +#include + +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 + 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(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"; + } +} + +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 + + 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 000000000..e87d676b5 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +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(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( + 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(rEntry.eOp); + sal_uInt16 nFieldSelPos = GetFieldSelPos( static_cast(rEntry.nField) ); + + aFieldLbArr[i]->set_active( nFieldSelPos ); + aCondLbArr [i]->set_active( nCondPos ); + UpdateValueList( static_cast(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(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(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(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(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(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(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(nField) - 1) : static_cast(0); + rEntry.eOp = eOp; + } + } + + theParam.GetEntry(1).eConnect = (nConnect1 != -1) + ? static_cast(nConnect1) + : SC_AND; + theParam.GetEntry(2).eConnect = (nConnect2 != -1) + ? static_cast(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 000000000..90a13e920 --- /dev/null +++ b/sc/source/ui/dbgui/pvfundlg.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 . + */ + +#undef SC_DLLIMPLEMENTATION + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +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& rMembers, int nEmptyPos) +{ + bool bEmpty = false; + vector::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& 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 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 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")) + , mxExpander(m_xBuilder->weld_expander("expander")) + , 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& 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(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; + } + 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; + } + 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); + 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( 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); + + // sorting mode + m_xRbSortAsc->connect_toggled( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) ); + m_xRbSortDesc->connect_toggled( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) ); + m_xRbSortMan->connect_toggled( LINK( this, ScDPSubtotalOptDlg, RadioClickHdl ) ); + + 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); + RadioClickHdl(*pRBtn); + + // *** 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, RadioClickHdl, weld::Toggleable&, rBtn, void) +{ + if (!rBtn.get_active()) + return; + + m_xLbSortBy->set_sensitive(m_xRbSortMan->get_active()); +} + +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; nDimGetExistingDimensionByName(aName) : nullptr; + if ( !pDimension || (pDimension->GetOrientation() != nOrient) ) + { + if (pDimension) + { + const std::optional & 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(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 000000000..543914e14 --- /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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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(SfxViewShell::Current())->GetViewData().GetDocument(); + + m_xEdName->set_text(aName); + + if ( !ScDocument::ValidTabName( aName ) ) + { + std::unique_ptr 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 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 000000000..126689d7b --- /dev/null +++ b/sc/source/ui/dbgui/scuiasciiopt.cxx @@ -0,0 +1,943 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//! 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_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 CSVImportOptionNames = +{ + "MergeDelimiters", + "Separators", + "TextSeparators", + "FixedWidth", + "RemoveSpace", + "EvaluateFormulas", + "FromRow", + "CharSet", + "QuotedFieldAsText", + "DetectSpecialNumbers", + "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(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(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(aStr.toInt32()); // Ascii + } + } + return c; +} + +static void lcl_CreatePropertiesNames ( OUString& rSepPath, Sequence& rNames, ScImportAsciiCall eCall ) +{ + sal_Int32 nProperties = 0; + + switch(eCall) + { + case SC_IMPORTFILE: + rSepPath = aSep_Path; + nProperties = 11; + break; + case SC_PASTETEXT: + rSepPath = aSep_Path_Clpbrd; + nProperties = 12; + 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_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& rFixedWidth, sal_Int32& rFromRow, sal_Int32& rCharSet, + sal_Int32& rLanguage, bool& rSkipEmptyCells, bool& rRemoveSpace, + bool& rEvaluateFormulas, ScImportAsciiCall eCall ) +{ + SequenceaValues; + const Any *pProperties; + Sequence 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_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 bFixedWidth, sal_Int32 nFromRow, + sal_Int32 nCharSet, sal_Int32 nLanguage, bool bSkipEmptyCells, bool bRemoveSpace, bool bEvaluateFormulas, + ScImportAsciiCall eCall ) +{ + Sequence aValues; + Any *pProperties; + Sequence 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_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, const OUString& 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")) + , mxCkbEvaluateFormulas(m_xBuilder->weld_check_button("evaluateformulas")) + , mxCkbSkipEmptyCells(m_xBuilder->weld_check_button("skipemptycells")) + , mxFtType(m_xBuilder->weld_label("textcolumntype")) + , 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 += " - [" + aDatName + "]"; + m_xDialog->set_title(aName); + } + break; + default: + break; + } + + // To be able to prefill the correct values based on the file extension + bool bIsTSV = (aDatName.endsWithIgnoreAsciiCase(".tsv") || aDatName.endsWithIgnoreAsciiCase(".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 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, 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); + 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(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 aSeparatorClickHdl =LINK( this, ScImportAsciiDlg, SeparatorClickHdl ); + mxCbTextSep->connect_changed( LINK( this, ScImportAsciiDlg, SeparatorComboBoxHdl ) ); + mxCkbTab->connect_toggled( aSeparatorClickHdl ); + mxCkbSemicolon->connect_toggled( aSeparatorClickHdl ); + mxCkbComma->connect_toggled( aSeparatorClickHdl ); + mxCkbAsOnce->connect_toggled( aSeparatorClickHdl ); + mxCkbQuotedAsText->connect_toggled( aSeparatorClickHdl ); + mxCkbDetectNumber->connect_toggled( aSeparatorClickHdl ); + mxCkbEvaluateFormulas->connect_toggled( aSeparatorClickHdl ); + mxCkbSkipEmptyCells->connect_toggled( aSeparatorClickHdl ); + mxCkbSpace->connect_toggled( aSeparatorClickHdl ); + mxCkbRemoveSpace->connect_toggled( aSeparatorClickHdl ); + mxCkbOther->connect_toggled( aSeparatorClickHdl ); + mxEdOther->connect_changed(LINK(this, ScImportAsciiDlg, SeparatorEditHdl)); + + // *** 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(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 ) ); + mxFtType->set_sensitive(false); + 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); + } + 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.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(), + mxRbFixed->get_active(), + mxNfRow->get_value(), + mxLbCharSet->get_active(), + static_cast(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); +} + +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; + + mxFtType->set_sensitive( bEnable ); + 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 000000000..e04f0b672 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ScDelimiterTable + +class ScDelimiterTable +{ +public: + explicit ScDelimiterTable( const OUString& rDelTab ) + : theDelTab ( rDelTab ), + 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(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(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_xBtnOk(m_xBuilder->weld_button("ok")) + , 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(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 000000000..3aeb31923 --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +// DEFINE -------------------------------------------------------------------- + +namespace +{ + void ERRORBOX(weld::Window* pParent, TranslateId rid) + { + std::unique_ptr 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( + 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 aLinkEdit = LINK(this, ScSpecialFilterDlg, RefInputEditHdl); + Link 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( + 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 000000000..22af2230b --- /dev/null +++ b/sc/source/ui/dbgui/sortdlg.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 . + */ + +#undef SC_DLLIMPLEMENTATION + +#include +#include +#include + +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); +} + +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 000000000..599280735 --- /dev/null +++ b/sc/source/ui/dbgui/sortkeydlg.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/. + */ + +#include +#include +#include + +#include +#include + +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 ( m_aSortKeyItems.size() > 0 ) + { + pSortKeyItem->m_xLbSort->set_buildable_name( + pSortKeyItem->m_xLbSort->get_buildable_name() + OString::number(m_aSortKeyItems.size() + 1)); + } + + m_aSortKeyItems.push_back(std::unique_ptr(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 000000000..924716a6f --- /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 +#include +#include + +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 000000000..99ddc425d --- /dev/null +++ b/sc/source/ui/dbgui/textimportoptions.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 +#include +#include +#include +#include + +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_xBtnConvertDate(m_xBuilder->weld_check_button("convertdata")) + , m_xBtnKeepAsking(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_xBtnConvertDate->get_active(); +} + +bool ScTextImportOptionsDlg::isKeepAskingSet() const +{ + return m_xBtnKeepAsking->get_active(); +} + +void ScTextImportOptionsDlg::init() +{ + m_xBtnOk->connect_clicked(LINK(this, ScTextImportOptionsDlg, OKHdl)); + Link aLink = LINK(this, ScTextImportOptionsDlg, RadioHdl); + m_xRbAutomatic->connect_toggled(aLink); + m_xRbCustom->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, RadioHdl, 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); + } +} + +/* 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 000000000..35a24e633 --- /dev/null +++ b/sc/source/ui/dbgui/tpsort.cxx @@ -0,0 +1,855 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +using namespace com::sun::star; + +/* + * 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(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 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; im_xLbSort->connect_changed(LINK(this, ScTabPageSortFields, SelectHdl)); + } +} + +std::unique_ptr ScTabPageSortFields::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* pArgSet) +{ + return std::make_unique(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; im_xLbSort->connect_changed( LINK( this, + ScTabPageSortFields, SelectHdl ) ); + } + nSortKeyCount = aSortData.GetSortKeyCount(); + FillFieldLists(0); + + for ( sal_uInt16 i=0; im_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; im_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; im_xLbSort->set_active(0); + + for ( sal_uInt16 i=0; im_xBtnUp->set_active(true); + + m_aSortWin.m_aSortKeyItems[0]->EnableField(); + m_aSortWin.m_aSortKeyItems[1]->EnableField(); + for ( sal_uInt16 i=2; iDisableField(); + } + + // 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 nSortPos; + + for ( sal_uInt16 i=0; im_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 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; iPut( 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 && im_xLbSort->insert_text(i, aFieldName); + + i++; + } + } + else + { + OUString aFieldName; + SCROW nMaxRow = aSortData.nRow2; + SCROW row; + + for ( row=nFirstSortRow; row<=nMaxRow && im_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; nm_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 nCurSel; + for ( sal_uInt16 i=0; im_xLbSort->get_active() ); + + FillFieldLists(0); + + for ( sal_uInt16 i=0; im_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)) + , 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_xColWrap.reset(new CollatorWrapper(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()); + } + + FillUserSortListBox(); + + // get available languages + + m_xLbLanguage->SetLanguageList( SvxLanguageListFlags::ALL | SvxLanguageListFlags::ONLY_KNOWN, false ); + m_xLbLanguage->InsertLanguage( LANGUAGE_SYSTEM ); +} + +std::unique_ptr ScTabPageSortOptions::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet) +{ + return std::make_unique(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 aAlgos = m_xColWrap->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(GetDialogController()); + if (!pDlg) + return; +} + +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 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; iappend_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; iget_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 aAlgos = m_xColWrap->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 000000000..01431c815 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +// 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() + OString::number(rTabNumber)); + mxLbColumns->set_buildable_name(mxLbColumns->get_buildable_name() + OString::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; iset_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 pFunctions; + std::unique_ptr 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(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; iget_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 && iGetSheetLimits()); 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; nn_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 ScTpSubTotalGroup1::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rArgSet ) +{ + return std::make_unique( pPage, pController, *rArgSet ); +} + +std::unique_ptr ScTpSubTotalGroup2::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rArgSet ) +{ + return std::make_unique( pPage, pController, *rArgSet ); +} + +std::unique_ptr ScTpSubTotalGroup3::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rArgSet ) +{ + return std::make_unique( 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 ScTpSubTotalOptions::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rArgSet) +{ + return std::make_unique(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; iappend_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 000000000..d194fdc10 --- /dev/null +++ b/sc/source/ui/dbgui/validate.cxx @@ -0,0 +1,927 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifdef SC_DLLIMPLEMENTATION +#undef SC_DLLIMPLEMENTATION +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +// cell range picker +#include +#include +#include +#include +#include + + +#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#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 const 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 ScTPValidationValue::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rArgSet) +{ + return std::make_unique(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( + lclGetValModeFromPos( nLbPos ) ) ) ); + rArgSet->Put( SfxUInt16Item( FID_VALID_CONDMODE, sal::static_int_cast( 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(GetDialogController()); +} + +void ScTPValidationValue::SetupRefDlg() +{ + ScValidationDlg *pValidationDlg = GetValidationDlg(); + if( !pValidationDlg ) + return; + + if( !pValidationDlg->SetupRefDlg() ) + return; + + pValidationDlg->SetHandler( this ); + pValidationDlg->SetSetRefHdl( static_cast( &ScTPValidationValue::SetReferenceHdl ) ); + pValidationDlg->SetSetActHdl( static_cast( &ScTPValidationValue::SetActiveHdl ) ); + pValidationDlg->SetRefInputStartPreHdl( static_cast( &ScTPValidationValue::RefInputStartPreHdl ) ); + pValidationDlg->SetRefInputDonePostHdl( static_cast( &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, IS_MOBILE ? OUString("modules/scalc/ui/validationhelptabpage-mobile.ui") + : OUString("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")) +{ + m_xEdInputHelp->set_size_request(m_xEdInputHelp->get_approximate_digit_width() * 40, m_xEdInputHelp->get_height_rows(13)); +} + +ScTPValidationHelp::~ScTPValidationHelp() +{ +} + +std::unique_ptr ScTPValidationHelp::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rArgSet) +{ + return std::make_unique(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, + IS_MOBILE ? OUString("modules/scalc/ui/erroralerttabpage-mobile.ui") + : OUString("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 ScTPValidationError::Create(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rArgSet) +{ + return std::make_unique(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(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* pViewFrm = pTabViewShell->GetViewFrame(); + SfxChildWindow* pWnd = pViewFrm->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* pViewFrm = pTabViewShell->GetViewFrame(); + if ( pViewFrm->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(pWnd)->LockVisible( true ); + bFreeWindowLock = static_cast(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(pWnd)->LockVisible( bVisLock ); + static_cast(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 000000000..f085e737c --- /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 +#include +#include +#include + +namespace sc +{ +SparklineDataRangeDialog::SparklineDataRangeDialog(SfxBindings* pBindings, + SfxChildWindow* pChildWindow, + weld::Window* pWindow, ScViewData& rViewData) + : ScAnyRefDlgController(pBindings, pChildWindow, pWindow, + u"modules/scalc/ui/sparklinedatarangedialog.ui", + "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 aEditLink + = LINK(this, SparklineDataRangeDialog, EditFocusHandler); + mxDataRangeEdit->SetGetFocusHdl(aEditLink); + aEditLink = LINK(this, SparklineDataRangeDialog, LoseEditFocusHandler); + mxDataRangeEdit->SetLoseFocusHdl(aEditLink); + + Link aButtonLink + = LINK(this, SparklineDataRangeDialog, ButtonFocusHandler); + mxDataRangeButton->SetGetFocusHdl(aButtonLink); + aButtonLink = LINK(this, SparklineDataRangeDialog, LoseButtonFocusHandler); + mxDataRangeButton->SetLoseFocusHdl(aButtonLink); + + Link 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 000000000..f27e0eb4a --- /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 +#include +#include +#include +#include + +#include + +#include +#include + +namespace sc +{ +SparklineDialog::SparklineDialog(SfxBindings* pBindings, SfxChildWindow* pChildWindow, + weld::Window* pWindow, ScViewData& rViewData) + : ScAnyRefDlgController(pBindings, pChildWindow, pWindow, + u"modules/scalc/ui/sparklinedialog.ui", "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 aEditLink = LINK(this, SparklineDialog, EditFocusHandler); + mxInputRangeEdit->SetGetFocusHdl(aEditLink); + mxOutputRangeEdit->SetGetFocusHdl(aEditLink); + aEditLink = LINK(this, SparklineDialog, LoseEditFocusHandler); + mxInputRangeEdit->SetLoseFocusHdl(aEditLink); + mxOutputRangeEdit->SetLoseFocusHdl(aEditLink); + + Link aButtonLink = LINK(this, SparklineDialog, ButtonFocusHandler); + mxInputRangeButton->SetGetFocusHdl(aButtonLink); + mxOutputRangeButton->SetGetFocusHdl(aButtonLink); + aButtonLink = LINK(this, SparklineDialog, LoseButtonFocusHandler); + mxInputRangeButton->SetLoseFocusHdl(aButtonLink); + mxOutputRangeButton->SetLoseFocusHdl(aButtonLink); + + Link 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 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()); + mxColorNegative->SelectEntry(maAttributes.getColorNegative()); + mxColorMarker->SelectEntry(maAttributes.getColorMarkers()); + mxColorHigh->SelectEntry(maAttributes.getColorHigh()); + mxColorLow->SelectEntry(maAttributes.getColorLow()); + mxColorFirst->SelectEntry(maAttributes.getColorFirst()); + mxColorLast->SelectEntry(maAttributes.getColorLast()); + + 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->GetSelectEntryColor()); + maAttributes.setColorNegative(mxColorNegative->GetSelectEntryColor()); + maAttributes.setColorMarkers(mxColorMarker->GetSelectEntryColor()); + maAttributes.setColorHigh(mxColorHigh->GetSelectEntryColor()); + maAttributes.setColorLow(mxColorLow->GetSelectEntryColor()); + maAttributes.setColorFirst(mxColorFirst->GetSelectEntryColor()); + maAttributes.setColorLast(mxColorLast->GetSelectEntryColor()); + + auto& rDocFunc = mrViewData.GetDocShell()->GetDocFunc(); + + if (mpSparklineGroup) + { + rDocFunc.ChangeSparklineGroupAttributes(mpSparklineGroup, maAttributes); + } + else + { + auto pNewSparklineGroup = std::make_shared(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 000000000..ab0e57907 --- /dev/null +++ b/sc/source/ui/dialogs/searchresults.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 aWidths + { + o3tl::narrowing(mxList->get_approximate_digit_width() * 10), + o3tl::narrowing(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(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 ) +{ + ListWrapper aList(*mxList); + std::vector 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 aTotal(ScResId(SCSTR_TOTAL, aList.mnCount)); + OUString aSearchResults = aTotal.replaceFirst("%1", OUString::number(aList.mnCount)); + if (aList.mnCount > ListWrapper::mnMaximum) + aSearchResults += " " + ScGlobal::ReplaceOrAppend( aSkipped, u"%1", OUString::number( ListWrapper::mnMaximum ) ); + mxSearchResults->set_label(aSearchResults); + + 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(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 000000000..8d3a89a16 --- /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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +ScAreaLink::ScAreaLink( SfxObjectShell* pShell, const OUString& rFile, + const OUString& rFilter, const OUString& rOpt, + const OUString& rArea, const ScRange& rDest, + sal_Int32 nRefreshDelaySeconds ) : + ::sfx2::SvBaseLink(SfxLinkUpdateMode::ONCALL,SotClipboardFormatId::SIMPLE_FILE), + ScRefreshTimer ( nRefreshDelaySeconds ), + m_pDocSh(static_cast(pShell)), + aFileName (rFile), + aFilterName (rFilter), + aOptions (rOpt), + aSourceArea (rArea), + aDestArea (rDest), + bAddUndo (true), + bInCreate (false), + bDoInsert (true) +{ + OSL_ENSURE(dynamic_cast< const ScDocShell *>( pShell ) != nullptr, "ScAreaLink with wrong ObjectShell"); + SetRefreshHandler( LINK( this, ScAreaLink, RefreshHdl ) ); + SetRefreshControl( &m_pDocSh->GetDocument().GetRefreshTimerControlAddress() ); +} + +ScAreaLink::~ScAreaLink() +{ + StopRefreshTimer(); +} + +void ScAreaLink::Edit(weld::Window* pParent, const Link& /* rEndEditHdl */ ) +{ + // use own dialog instead of SvBaseLink::Edit... + ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); + + ScopedVclPtr 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 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( 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 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(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( 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 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 000000000..5b6eaa30c --- /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 +#include + +#include +#include +#include + +static sal_uLong TimeNow() // seconds +{ + return static_cast(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 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::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::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::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::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 000000000..4bcbbaf99 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +namespace com::sun::star::ui { class XUIElement; } + +namespace sc { + +static o3tl::enumarray 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(now.Seconds) + static_cast(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(const char* p, size_t n, bool /*transient*/) + { + if (mnCols >= mnColCount) + return; + + DataStream::Cell aCell; + if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', aCell.mfValue)) + { + aCell.mbValue = true; + } + else + { + aCell.mbValue = false; + aCell.maStr.Pos = std::distance(mpLineHead, p); + aCell.maStr.Size = n; + } + mrLine.maCells.push_back(aCell); + + ++mnCols; + } +}; + +} + +namespace datastreams { + +class ReaderThread : public salhelper::Thread +{ + std::unique_ptr mpStream; + size_t mnColCount; + bool mbTerminate; + osl::Mutex maMtxTerminate; + + std::queue> maPendingLines; + std::queue> maUsedLines; + osl::Mutex maMtxLines; + + osl::Condition maCondReadStream; + osl::Condition maCondConsume; + + orcus::csv::parser_config maConfig; + +public: + + ReaderThread(std::unique_ptr pData, size_t nColCount): + Thread("ReaderThread"), + mpStream(std::move(pData)), + mnColCount(nColCount), + mbTerminate(false) + { + maConfig.delimiters.push_back(','); + maConfig.text_qualifier = '"'; + } + + bool isTerminateRequested() + { + osl::MutexGuard aGuard(maMtxTerminate); + return mbTerminate; + } + + void requestTerminate() + { + osl::MutexGuard aGuard(maMtxTerminate); + mbTerminate = true; + } + + void endThread() + { + requestTerminate(); + maCondReadStream.set(); + } + + void waitForNewLines() + { + maCondConsume.wait(); + maCondConsume.reset(); + } + + std::unique_ptr 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( std::unique_ptr pLines ) + { + maUsedLines.push(std::move(pLines)); + } + + osl::Mutex& getLinesMutex() + { + return maMtxLines; + } + +private: + virtual void execute() override + { + while (!isTerminateRequested()) + { + std::unique_ptr pLines; + osl::ResettableMutexGuard aGuard(maMtxLines); + + if (!maUsedLines.empty()) + { + // Re-use lines from previous runs. + pLines = std::move(maUsedLines.front()); + maUsedLines.pop(); + aGuard.clear(); // unlock + } + else + { + aGuard.clear(); // unlock + pLines.reset(new DataStream::LinesType(10)); + } + + // Read & store new lines from stream. + for (DataStream::Line & rLine : *pLines) + { + rLine.maCells.clear(); + mpStream->ReadLine(rLine.maLine); + CSVHandler aHdl(rLine, mnColCount); + orcus::csv_parser parser(rLine.maLine.getStr(), rLine.maLine.getLength(), aHdl, maConfig); + parser.parse(); + } + + aGuard.reset(); // lock + while (!isTerminateRequested() && maPendingLines.size() >= 8) + { + // pause reading for a bit + aGuard.clear(); // unlock + maCondReadStream.wait(); + maCondReadStream.reset(); + aGuard.reset(); // lock + } + maPendingLines.push(std::move(pLines)); + 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 const OUStringLiteral sResourceURL( u"private:resource/toolbar/datastreams" ); + 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, sal_uInt32 nSettings) +{ + DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove, nSettings); + 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, sal_uInt32 nSettings) : + 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, nSettings); +} + +DataStream::~DataStream() +{ + if (mbRunning) + StopImport(); + + if (mxReaderThread.is()) + { + mxReaderThread->endThread(); + mxReaderThread->join(); + } + mpLines.reset(); +} + +DataStream::Line DataStream::ConsumeLine() +{ + if (!mpLines || mnLinesCount >= mpLines->size()) + { + mnLinesCount = 0; + if (mxReaderThread->isTerminateRequested()) + return Line(); + + osl::ResettableMutexGuard aGuard(mxReaderThread->getLinesMutex()); + if (mpLines) + mxReaderThread->pushUsedLines(std::move(mpLines)); + + while (!mxReaderThread->hasNewLines()) + { + aGuard.clear(); // unlock + mxReaderThread->waitForNewLines(); + aGuard.reset(); // lock + } + + mpLines = mxReaderThread->popNewLines(); + mxReaderThread->resumeReadStream(); + } + return mpLines->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, const sal_uInt32 nSettings) +{ + msURL = rURL; + meMove = eMove; + meOrigMove = eMove; + mnSettings = nSettings; + + 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 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 000000000..ee59f3623 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace ::com::sun::star; + +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 pUndoColl; + if (bUndo) + pUndoColl.reset( new ScDBCollection( *pDocColl ) ); + + std::unique_ptr 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( &rDocShell, std::move(pUndoColl), + std::make_unique( *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 pUndoColl; + if (bUndo) + pUndoColl.reset( new ScDBCollection( *pDocColl ) ); + + rDoc.PreprocessDBDataUpdate(); + rDBs.erase(iter); + rDoc.CompileHybridFormula(); + + if (bUndo) + { + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &rDocShell, std::move(pUndoColl), + std::make_unique( *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 pNewData(new ScDBData(rNew, **iterOld)); + + std::unique_ptr 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( &rDocShell, std::move(pUndoColl), + std::make_unique( *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 pUndoColl; + if (bUndo) + pUndoColl.reset( new ScDBCollection( *pDocColl ) ); + + *pData = rNewData; + if (bAreaChanged) + rDoc.CompileDBFormula(); + + if (bUndo) + { + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &rDocShell, std::move(pUndoColl), + std::make_unique( *pDocColl ) ) ); + } + + aModificator.SetDocumentModified(); +} + +void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector& rDelAreaList ) +{ + ScDocShellModificator aModificator(rDocShell); + ScDocument& rDoc = rDocShell.GetDocument(); + ScDBCollection* pOldColl = rDoc.GetDBCollection(); + std::unique_ptr 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(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(&rDocShell, std::move(pUndoColl), + std::make_unique(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 pUndoTab; + std::unique_ptr pUndoRange; + std::unique_ptr 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(nOutStartCol), 0, + nTab, static_cast(nOutEndCol), rDoc.MaxRow(), nTab, + InsertDeleteFlags::NONE, false, *pUndoDoc); + rDoc.CopyToDocument(0, static_cast(nOutStartRow), + nTab, rDoc.MaxCol(), static_cast(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( &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(&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(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; iGetArea( 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 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 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( &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 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 pUndoTab; + std::unique_ptr pUndoRange; + std::unique_ptr 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(nOutStartCol), 0, nTab, static_cast(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( &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 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( + &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 aListOfObjects = + sc::tools::getAllPivotChartsConnectedTo(rDPObj.GetName(), &rDocShell); + + ScDrawLayer* pModel = rDoc.GetDrawLayer(); + + if (pModel && !aListOfObjects.empty()) + { + std::unique_ptr 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(*pChartObject)); + pChartObject->getSdrPageFromSdrObject()->RemoveObject(pChartObject->GetOrdNum()); + } + } + } + } + + ScDocumentUniquePtr pOldUndoDoc; + std::unique_ptr 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( + &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 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 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(&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 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( + &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 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 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 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( ( 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 000000000..b7a0f95c9 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +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 xFrame = pFrame->GetFrame().GetFrameInterface(); + + uno::Reference xBeamerFrame = xFrame->findFrame( + "_beamer", + frame::FrameSearchFlag::CHILDREN); + if (!xBeamerFrame.is()) + return; + + uno::Reference xController = xBeamerFrame->getController(); + uno::Reference 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& 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 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 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 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 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 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 xExecute( xRowSet, uno::UNO_QUERY ); + if ( xExecute.is() ) + { + uno::Reference 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 xMeta; + uno::Reference 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 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 xRow( xRowSet, uno::UNO_QUERY ); + if ( nColCount > 0 && xRow.is() ) + { + nEndCol = static_cast( rParam.nCol1 + nColCount - 1 ); + + uno::Sequence aColTypes( nColCount ); // column types + uno::Sequence aColCurr( nColCount ); // currency flag is not in types + sal_Int32* pTypeArr = aColTypes.getArray(); + sal_Bool* pCurrArr = aColCurr.getArray(); + for (tools::Long i=0; igetColumnType( i+1 ); + pCurrArr[i] = xMeta->isCurrency( i+1 ); + } + + // read column names + nCol = rParam.nCol1; + for (tools::Long i=0; iSetString( 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; iIsKeepFmt(); + 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( 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 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 pRedoDBData(new ScDBData(*pDBData)); + + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &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 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 000000000..752843797 --- /dev/null +++ b/sc/source/ui/docshell/docfunc.cxx @@ -0,0 +1,5922 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; +using ::std::vector; + +void ScDocFunc::NotifyDrawUndo( std::unique_ptr 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( 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 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(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 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( &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 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( &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 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( &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 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( &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 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( &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 xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent())); + if (bUndo) + pModel->BeginCalcUndo(false); + bool bOverflow; + bool bDone = ScDetectiveFunc(rDoc, nTab).MarkInvalid( bOverflow ); + std::unique_ptr 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 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 pUndo; + if (bUndo) + pUndo = pModel->GetCalcUndo(); + if (bDone) + { + ScDetOpList* pOldList = rDoc.GetDetOpList(); + std::unique_ptr pUndoList; + if (bUndo && pOldList) + pUndoList.reset(new ScDetOpList(*pOldList)); + + rDoc.ClearDetectiveOperations(); + + if (bUndo) + { + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &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; nTabCount(); + 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 pUndo = pModel->GetCalcUndo(); + if (pUndo) + { + pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) ); + // associate with the last action + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( std::move(pUndo), &rDocShell ), + bAutomatic ); + } + } + rDocShell.SetDrawModified(); + bDone = true; + } + return bDone; +} + +static void lcl_collectAllPredOrSuccRanges( + const ScRangeList& rSrcRanges, vector& rRefTokens, ScDocShell& rDocShell, + bool bPred) +{ + ScDocument& rDoc = rDocShell.GetDocument(); + vector 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& rRefTokens) +{ + lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true); +} + +void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector& 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 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 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( &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(&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(&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& 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 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(&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(&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 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 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(&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& 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 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(const SfxItemSet& rItemSet, sal_Int32 nTempIndex) : + nIndex(nTempIndex), aItemSet(rItemSet) {} + }; + +} + +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> 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(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 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 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( 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 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( 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()); + } +} + +ScPostIt* ScDocFunc::ImportNote( const ScAddress& rPos, const OUString& rNoteText ) +{ + ScDocShellModificator aModificator( rDocShell ); + ScDocument& rDoc = rDocShell.GetDocument(); + + std::unique_ptr pOldNote = rDoc.ReleaseNote( rPos ); + SAL_WARN_IF(pOldNote, "sc.ui", "imported data has >1 notes on same cell? at pos " << rPos); + + // create new note + ScPostIt* pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, /*nNoteId*/0 ); + + rDoc.SetStreamValid(rPos.Tab(), false); + + aModificator.SetDocumentModified(); + + return pNewNote; +} + +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( + &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( 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( + &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) + break; + + for( SCTAB j = rTab+1; j 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 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 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(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(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(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(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 pTabs(new SCTAB[nSelCount]); + std::unique_ptr pScenarios(new SCTAB[nSelCount]); + nUndoPos = 0; + for (const auto& rTab : aMark) + { + if (rTab >= nTabCount) + break; + + SCTAB nCount = 0; + for( SCTAB j=rTab+1; jLeaveListAction(); + } + + rDocShell.GetUndoManager()->AddUndoAction( std::make_unique( + &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(nEndRow-nStartRow+1)); + break; + case INS_CELLSRIGHT: + case INS_INSCOLS_BEFORE: + case INS_INSCOLS_AFTER: + aRange.aEnd.IncCol(static_cast(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; jMarkRange( 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) + break; + + for( SCTAB j = rTab+1; j 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 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 pRefUndoDoc; + std::unique_ptr 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; jInitUndo( 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(nEndRow-nStartRow+1), pRefUndoDoc.get(), nullptr, &aFullMark ); + nPaintEndRow = rDoc.MaxRow(); + break; + case DelCellCmd::Rows: + rDoc.DeleteRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast(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(nEndCol-nStartCol+1), pRefUndoDoc.get(), nullptr, &aFullMark ); + nPaintEndCol = rDoc.MaxCol(); + break; + case DelCellCmd::Cols: + rDoc.DeleteCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast(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 pTabs( new SCTAB[nSelCount]); + std::unique_ptr pScenarios( new SCTAB[nSelCount]); + SCTAB nUndoPos = 0; + + for (const auto& rTab : aMark) + { + if (rTab >= nTabCount) + break; + + SCTAB nCount = 0; + for( SCTAB j=rTab+1; jLeaveListAction(); + } + + rDocShell.GetUndoManager()->AddUndoAction( std::make_unique( + &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( aRange.aEnd.Row()-nDecreaseRowCount)); + break; + case DelCellCmd::CellsLeft: + case DelCellCmd::Cols: + aRange.aEnd.SetCol(static_cast( 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) + 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; jOnLOKInsertDeleteColumn(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( + &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 ) +{ + SfxObjectShell& 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( &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 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(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 theTabs; + theTabs.push_back(nTab); + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &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 undoTabs { nTab }; + rDocShell.GetUndoManager()->AddUndoAction( std::make_unique( &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( &rDocShell, nTab, bRTL ) ); + } + + rDocShell.PostPaint( 0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, 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( &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( &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( &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& 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 pUndoTab; + std::vector 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(nStart), 0, nTab, static_cast(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc ); + } + else + { + pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true ); + rDoc.CopyToDocument( 0, static_cast(nStart), nTab, rDoc.MaxCol(), static_cast(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(nStartNo); nCol<=static_cast(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(nStartNo), + static_cast(nEndNo), nTab, bShow ); + else + bOutline = bOutline || rDoc.UpdateOutlineRow( + static_cast(nStartNo), + static_cast(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( + &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(rPos.Col()) : + static_cast(rPos.Row()); + if (nPos == 0) + return false; // first column / row + + ScBreakType nBreak = bColumn ? + rDoc.HasColBreak(static_cast(nPos), nTab) : + rDoc.HasRowBreak(static_cast(nPos), nTab); + if (nBreak & ScBreakType::Manual) + return true; + + if (bRecord) + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, true ) ); + + if (bColumn) + rDoc.SetColBreak(static_cast(nPos), nTab, false, true); + else + rDoc.SetRowBreak(static_cast(nPos), nTab, false, true); + + rDoc.InvalidatePageBreaks(nTab); + rDoc.UpdatePageBreaks( nTab ); + + rDoc.SetStreamValid(nTab, false); + + if (bColumn) + { + rDocShell.PostPaint( static_cast(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(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(rPos.Col()) : + static_cast(rPos.Row()); + + ScBreakType nBreak; + if (bColumn) + nBreak = rDoc.HasColBreak(static_cast(nPos), nTab); + else + nBreak = rDoc.HasRowBreak(static_cast(nPos), nTab); + if (!(nBreak & ScBreakType::Manual)) + // There is no manual break. + return false; + + if (bRecord) + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, false ) ); + + if (bColumn) + rDoc.RemoveColBreak(static_cast(nPos), nTab, false, true); + else + rDoc.RemoveRowBreak(static_cast(nPos), nTab, false, true); + + rDoc.UpdatePageBreaks( nTab ); + + rDoc.SetStreamValid(nTab, false); + + if (bColumn) + { + rDocShell.PostPaint( static_cast(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 p; + if (!rProtect.isProtected() && rDoc.IsUndoEnabled()) + { + // In case of unprotecting, use a copy of passed ScTableProtection object for undo + p = std::make_unique(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(*pProtect); + } + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique(&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(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 p; + if (!rProtect.isProtected() && rDoc.IsUndoEnabled()) + { + // In case of unprotecting, use a copy of passed ScTableProtection object for undo + p = std::make_unique(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(*pProtect); + } + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique(&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 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 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 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( &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( &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 aCols(1, sc::ColRowSpan(nStartCol,nEndCol)); + std::vector 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( &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 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( &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( &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( &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( aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1 ) : + static_cast( 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( aSourceArea.aEnd.Row() - nCount ) ); + break; + case FILL_TO_RIGHT: + aSourceArea.aEnd.SetCol( sal::static_int_cast( aSourceArea.aEnd.Col() - nCount ) ); + break; + case FILL_TO_TOP: + aSourceArea.aStart.SetRow( sal::static_int_cast( aSourceArea.aStart.Row() + nCount ) ); + break; + case FILL_TO_LEFT: + aSourceArea.aStart.SetCol( sal::static_int_cast( 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( &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( aSourceArea.aEnd.Row() + nCount ) ); + break; + case FILL_TO_TOP: + if (nCount > sal::static_int_cast( aSourceArea.aStart.Row() )) + { + OSL_FAIL("FillAuto: Row < 0"); + nCount = aSourceArea.aStart.Row(); + } + aDestArea.aStart.SetRow( sal::static_int_cast( aSourceArea.aStart.Row() - nCount ) ); + break; + case FILL_TO_RIGHT: + aDestArea.aEnd.SetCol( sal::static_int_cast( aSourceArea.aEnd.Col() + nCount ) ); + break; + case FILL_TO_LEFT: + if (nCount > sal::static_int_cast( aSourceArea.aStart.Col() )) + { + OSL_FAIL("FillAuto: Col < 0"); + nCount = aSourceArea.aStart.Col(); + } + aDestArea.aStart.SetCol( sal::static_int_cast( 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( &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 pDrawUndo = rDoc.GetDrawLayer() ? rDoc.GetDrawLayer()->GetCalcUndo() : nullptr; + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique(&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( &rDocShell, rOption, ScDocumentUniquePtr(pUndoDoc) ) ); + } + } + aModificator.SetDocumentModified(); + + return true; +} + +void ScDocFunc::ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab ) +{ + SetNewRangeNames( std::unique_ptr(new ScRangeName(rNewRanges)), true, nTab ); +} + +void ScDocFunc::SetNewRangeNames( std::unique_ptr 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 pUndoRanges(new ScRangeName(*pOld)); + std::unique_ptr pRedoRanges(new ScRangeName(*pNewRanges)); + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &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>& rRangeMap) +{ + ScDocShellModificator aModificator(rDocShell); + ScDocument& rDoc = rDocShell.GetDocument(); + + if (rDoc.IsUndoEnabled()) + { + std::map aOldRangeMap; + rDoc.GetRangeNameMap(aOldRangeMap); + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique(&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 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(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 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(ppSortArray.get()), nValidCount, sizeof(ScRangeData*), + &ScRangeData_QsortNameCompare ); + OUString aName; + OUStringBuffer aContent; + OUString aFormula; + SCROW nOutRow = nStartRow; + for (j=0; jGetName(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( &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 (nLinkPosGetLinks()[nLinkPos].get(); + ScAreaLink* pLink = dynamic_cast(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(pBase); + rDocShell.GetUndoManager()->AddUndoAction( + std::make_unique( &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( &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 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 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(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), aCompleteRange)); + } + + if(pRepaintRange) + rDocShell.PostPaint(*pRepaintRange, PaintPartFlags::Grid); + + 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(&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(&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 pSparklineGroup) +{ + std::vector 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(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(rDocShell, rAddress); + // delete sparkline by "redoing" + pUndoDeleteSparkline->Redo(); + rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDeleteSparkline)); + + return true; +} + +bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr const& pSparklineGroup, SCTAB nTab) +{ + if (!pSparklineGroup) + return false; + + auto& rDocument = rDocShell.GetDocument(); + + if (!rDocument.HasTable(nTab)) + return false; + + auto pUndo = std::make_unique(rDocShell, pSparklineGroup, nTab); + // delete sparkline group by "redoing" + pUndo->Redo(); + rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); + return true; +} + +bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr const& pExistingSparklineGroup, + sc::SparklineAttributes const& rNewAttributes) +{ + auto pUndo = std::make_unique(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 const& rpGroup) +{ + auto pUndo = std::make_unique(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(rDocShell, rRange); + // ungroup sparklines by "redoing" + pUndo->Redo(); + rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); + return true; +} + +bool ScDocFunc::ChangeSparkline(std::shared_ptr const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange) +{ + auto pUndo = std::make_unique(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 000000000..ce1a25d61 --- /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 +#include +#include +#include +#include +#include + +#include +#include + +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& pSpans, + bool bMulti, bool bDrawUndo ) +{ + std::unique_ptr pUndo( + new ScUndoDeleteContents( + pDocSh, rMark, rRange, std::move(pUndoDoc), bMulti, nFlags, bDrawUndo)); + pUndo->SetDataSpans(pSpans); + + pUndoMgr->AddUndoAction(std::move(pUndo)); +} + +std::shared_ptr DocFuncUtil::getNonEmptyCellSpans( + const ScDocument& rDoc, const ScMarkData& rMark, const ScRange& rRange ) +{ + auto pDataSpans = std::make_shared(); + 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 r = + pDataSpans->insert(std::make_pair(nTab, std::make_unique())); + + 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 000000000..219101f72 --- /dev/null +++ b/sc/source/ui/docshell/docsh.cxx @@ -0,0 +1,3482 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "docshimp.hxx" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace com::sun::star; +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 OUStringLiteral pFilterLotus = u"Lotus"; +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 OUStringLiteral pFilterDBase = u"dBase"; +constexpr OUStringLiteral pFilterDif = u"DIF"; +const char16_t pFilterSylk[] = u"SYLK"; +constexpr OUStringLiteral pFilterHtml = u"HTML (StarCalc)"; +constexpr OUStringLiteral pFilterHtmlWebQ = u"calc_HTML_WebQuery"; +const char16_t pFilterRtf[] = u"Rich Text Format (StarCalc)"; + +#define ShellClass_ScDocShell +#include + +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 ScDocShell::GetDocColors() +{ + return m_pDocument->GetDocColors(); +} + +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; nDPCreateNewName() ); + } + } + } + } + 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, 0); + pStrm->SetRefreshOnEmptyLine(r.mbRefreshOnEmpty); + sc::DocumentLinkManager& rMgr = rShell.GetDocument().GetDocLinkManager(); + rMgr.setDataStream(pStrm); +} + +class MessageWithCheck : public weld::MessageDialogController +{ +private: + std::unique_ptr m_xWarningOnBox; +public: + MessageWithCheck(weld::Window *pParent, const OUString& rUIFile, const OString& 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()); + } + } + + // 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; + ErrCode 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 xDPS(GetModel(), uno::UNO_QUERY_THROW); + uno::Reference xDocProps = xDPS->getDocumentProperties(); + + ScRecalcOptions nRecalcMode = + static_cast(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 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 + // for tdf#99729 (if required, it will be overridden in *::ReadUserDataSequence()) + if (IsOwnStorageFormat(rMedium)) + { + if (m_pDocument->GetDrawLayer()) + m_pDocument->GetDrawLayer()->SetAnchoredTextOverflowLegacy(true); + } + + 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(); + + if (!m_bUcalcTest) + { + /* 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.GetError()) + rMedium.SetError(SVSTREAM_FILEFORMAT_ERROR); + + if (rMedium.GetError()) + SetError(rMedium.GetError()); + + 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(&rHint) ) // Template changed + NotifyStyle( *pStyleSheetHint ); + else if ( auto pStlHint = dynamic_cast(&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 ( auto pEventHint = dynamic_cast(&rHint) ) + { + SfxEventHintId nEventId = pEventHint->GetEventId(); + + switch ( nEventId ) + { + 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 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() ) + { + 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( 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 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; + } + + uno::Reference< frame::XStorable > xStor( GetModel(), uno::UNO_QUERY_THROW ); + // 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 = SfxItemSet::GetItem(GetMedium()->GetItemSet(), 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 = SfxItemSet::GetItem(GetMedium()->GetItemSet(), 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 ); + xStor->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 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 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 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(); + } + } + + const SfxEventHint* pSfxEventHint = dynamic_cast(&rHint); + if (!pSfxEventHint) + return; + + switch( pSfxEventHint->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) +{ + 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(aTokens[0].toInt32()); + if (aTokens.size() > 1) + rDateConvert = static_cast(aTokens[1].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 aColWidthParam; + ScRange aColWidthRange; + // Set optimal row height after import? + bool bSetRowHeights = false; + + vector 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 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; + SfxItemSet* pSet = rMedium.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->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 (!GetError()) + 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 (!GetError()) + SetError(eError); + if( eError.IsWarning() ) + bRet = true; + } + else + bRet = true; + } + else if (aFltName == "Gnumeric Spreadsheet") + { + ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters(); + if (!pOrcus) + return false; + + bRet = pOrcus->importGnumeric(*m_pDocument, rMedium); + } + else if (aFltName == "MS Excel 2003 XML Orcus") + { + ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters(); + if (!pOrcus) + return false; + + bRet = pOrcus->importExcel2003XML(*m_pDocument, rMedium); + } + else if (aFltName == SC_TEXT_CSV_FILTER_NAME) + { + SfxItemSet* pSet = rMedium.GetItemSet(); + const SfxStringItem* pOptionsItem; + ScAsciiOptions aOptions; + bool bOptInit = false; + + if ( pSet && + (pOptionsItem = pSet->GetItemIfSet( SID_FILE_FILTEROPTIONS )) ) + { + aOptions.ReadFromString( pOptionsItem->GetValue() ); + bOptInit = true; + } + + if ( !bOptInit ) + { + // default for ascii import (from API without options): + // ISO8859-1/MS_1252 encoding, comma, double quotes + + aOptions.SetCharSet( RTL_TEXTENCODING_MS_1252 ); + 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); + + bool bIsMobile = comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current() + && SfxViewShell::Current()->isLOKMobilePhone(); + // 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 (!bIsMobile) + // 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*/); + + bOverflowRow = aImpEx.IsOverflowRow(); + bOverflowCol = aImpEx.IsOverflowCol(); + bOverflowCell = aImpEx.IsOverflowCell(); + } + else + { + OSL_FAIL( "No Stream" ); + } + } + + if (eError != ERRCODE_NONE) + { + if (!GetError()) + SetError(eError); + if( eError.IsWarning() ) + bRet = true; + } + else if (!GetError() && (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; + SfxItemSet* pSet = rMedium.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->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 (!GetError()) + 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; + SfxItemSet* pSet = rMedium.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->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 (!GetError()) + 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 (!GetError()) + SetError(eError); + if( eError.IsWarning() ) + bRet = true; + } + else if (!GetError() && (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 (!GetError()) + 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 (!GetError()) + 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 (!GetError()) + 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; + SfxItemSet* pSet = rMedium.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->GetItemIfSet( SID_FILE_FILTEROPTIONS )) ) + { + OUString aFilterOption = pOptionsItem->GetValue(); + lcl_parseHtmlFilterOption(aFilterOption, eLang, bDateConvert); + } + + 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 ); + if (eError != ERRCODE_NONE) + { + if (!GetError()) + 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 (!GetError()) + SetError(eError); + if( eError.IsWarning() ) + bRet = true; + } + } + else + { + if (!GetError()) + { + SAL_WARN("sc.filter", "No match for filter '" << aFltName << "' in ConvertFrom"); + SetError(SCERR_IMPORT_NI); + } + } + + 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(aZoom) / GetOutputFactor(); // Factor is printer display ratio + double nPPTY = ScGlobal::nScreenPPTY * static_cast(aZoom); + ScopedVclPtrInstance< VirtualDevice > pVirtDev; + // all sheets (for Excel import) + SCTAB nTabCount = m_pDocument->GetTableCount(); + for (SCTAB nTab=0; nTabGetCellArea( 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(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 pFilter = rMed.GetFilter(); + if (!pFilter) + return false; + + if (pFilter->GetProviderName() == "orcus") + { + ScOrcusFilters* pOrcus = ScFormatFilter::Get().GetOrcusFilters(); + if (!pOrcus) + return false; + + const OUString& rFilterName = pFilter->GetName(); + if (rFilterName == "gnumeric") + { + if (!pOrcus->importGnumeric(*m_pDocument, rMed)) + return false; + } + else if (rFilterName == "csv") + { + if (!pOrcus->importCSV(*m_pDocument, rMed)) + return false; + } + else if (rFilterName == "xlsx") + { + if (!pOrcus->importXLSX(*m_pDocument, rMed)) + return false; + } + else if (rFilterName == "ods") + { + if (!pOrcus->importODS(*m_pDocument, rMed)) + 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); + } + + // skip saving recovery file instead of showing re-type password dialog window + if ( bNeedsRehash && rMedium.GetFilter()->GetFilterName() == "calc8" && + // it seems, utl::MediaDescriptor::PROP_AUTOSAVEEVENT is true at Save As, too, + // so check the backup path + rMedium.GetName().startsWith( SvtPathOptions().GetBackupPath() ) ) + { + 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 && bNeedsRehash) + { + 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 +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; +} + +template +void escapeTextSep(sal_Int32 nPos, const StrT& rStrDelim, StrT& rStr) +{ + while (nPos >= 0) + { + StrBufT aBuf(rStr); + aBuf.insert(nPos, rStrDelim); + rStr = aBuf.makeStringAndClear(); + nPos = rStr.indexOf(rStrDelim, nPos+1+rStrDelim.getLength()); + } +} + +} + +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; + + 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 + { + 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->meType == CELLTYPE_STRING) + { + aString = pCell->mpString->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->meType; + 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->mpFormula->GetFormula(); + bString = true; + } + else if ((nErrCode = pCell->mpFormula->GetErrCode()) != FormulaError::NONE) + { + aString = ScGlobal::GetErrorString( nErrCode ); + bString = true; + } + else if (pCell->mpFormula->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->mpFormula->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->mpString->getString(); + bString = true; + break; + case CELLTYPE_EDIT : + { + const EditTextObject* pObj = pCell->mpEditText; + 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); + + escapeTextSep( + nPos, OUString(cStrDelim), aUniString); + + 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); + + escapeTextSep( + nPos, aStrDelimDecoded, aStrDec); + + // 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); + + escapeTextSep( + nPos, aStrDelimEncoded, aStrEnc); + + // 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() ); + 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* pItemSet = rMed.GetItemSet(); + if( pItemSet && pItemSet->GetItemState( SID_PASSWORD ) == SfxItemState::SET ) + { + bDoSave = ScWarnPassword::WarningOnPassword( rMed ); + // #i42858# remove password from medium (warn only one time) + if( bDoSave ) + pItemSet->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 && !GetError() ) + 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; + SfxItemSet* pSet = rMed.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->GetItemIfSet( SID_FILE_FILTEROPTIONS )) ) + { + sItStr = pOptionsItem->GetValue(); + } + + if ( sItStr.isEmpty() ) + { + // default for ascii export (from API without options): + // ISO8859-1/MS_1252 encoding, comma, double quotes + + ScImportOptions aDefOptions( ',', '"', RTL_TEXTENCODING_MS_1252 ); + 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 & 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 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.GetError()) + rMed.SetError(SCWARN_EXPORT_ASCII); + } + } + } + else if (aFltName == pFilterDBase) + { + OUString sCharSet; + SfxItemSet* pSet = rMed.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->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; + + ErrCode 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 (!GetError()) + SetError(eError); + if ( bHasMemo && IsDocument( aTmpFile ) ) + KillFile( aTmpFile ); + } + else + { + bRet = true; + if ( bHasMemo ) + { + const SfxStringItem* pNameItem = rMed.GetItemSet()->GetItem( SID_FILE_NAME ); + 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 && !GetError()) + 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 && !GetError()) + SetError(eError); + } + } + else if (aFltName == pFilterDif) + { + SvStream* pStream = rMed.GetOutStream(); + if (pStream) + { + OUString sItStr; + SfxItemSet* pSet = rMed.GetItemSet(); + const SfxStringItem* pOptionsItem; + if ( pSet && + (pOptionsItem = pSet->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.GetError()) + 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 ) + { + SfxItemSet* pSet = rMed.GetItemSet(); + OUString sFilterOptions; + + if (const SfxStringItem* pOptionsItem = pSet->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(*new StringErrorInfo( + SCWARN_EXPORT_NONCONVERTIBLE_CHARS, + aImExport.GetNonConvertibleChars(), + DialogMask::ButtonsOk | DialogMask::MessageInfo)); + } + } + } + else + { + if (GetError()) + 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 ScDocShell::CreateDocFunc() +{ + return std::make_unique( *this ); +} + +ScDocShell::ScDocShell( const SfxModelFlags i_nSfxCreationFlags, const std::shared_ptr& pDoc ) : + SfxObjectShell( i_nSfxCreationFlags ), + m_pDocument ( pDoc ? pDoc : std::make_shared( 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_bUcalcTest ( false ), + 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_pSolverSaveData.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.nPageCount + + static_cast(ScPrintFunc( this, pPrinter, i ).GetTotalPages()) ); +} + +std::shared_ptr ScDocShell::CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet &rSet) +{ + std::shared_ptr xDlg = std::make_shared(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(); +} + +void ScDocShell::SetSolverSaveData( std::unique_ptr pData ) +{ + m_pSolverSaveData = std::move(pData); +} + +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& xScAccel, const vector& 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 xContext = ::comphelper::getProcessComponentContext(); + if (!xContext.is()) + return; + + Reference xModuleCfgSupplier( + theModuleUIConfigurationManagerSupplier::get(xContext) ); + + // Grab the Calc configuration. + Reference xConfigMgr = + xModuleCfgSupplier->getUIConfigurationManager( + "com.sun.star.sheet.SpreadsheetDocument"); + + if (!xConfigMgr.is()) + return; + + // shortcut manager + Reference xScAccel = xConfigMgr->getShortCutManager(); + + if (!xScAccel.is()) + return; + + vector 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::SetIsInUcalc() +{ + m_bUcalcTest = true; +} + +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::TempFile aTmpDir(nullptr, true); + aTmpDir.EnableKillingFile(); + OUString sTmpDir = aTmpDir.GetURL(); + + OUString sExtension(".dbf"); + utl::TempFile aTempInput(u"", true, &sExtension, &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 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 000000000..04faa6e75 --- /dev/null +++ b/sc/source/ui/docshell/docsh2.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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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(); + + if (!m_bUcalcTest) + { + /* 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 aLocales = aAsian.GetStartEndCharLocales(); + if (aLocales.hasElements()) + { + std::shared_ptr 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 000000000..edab7a530 --- /dev/null +++ b/sc/source/ui/docshell/docsh3.cxx @@ -0,0 +1,1331 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "docshimp.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// 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; + 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 = 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 (nCol2MaxCol()) ++nCol2; + if (nRow1>0) --nRow1; + if (nRow2MaxRow()) ++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)); + } + + 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 = comphelper::getFromUnoTunnel(this->GetModel()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel); + } +} + +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 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->GetFont(aDefFont, SC_AUTOCOL_BLACK, 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->GetFont(aDefFont, SC_AUTOCOL_BLACK, 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 + // + // 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 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(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(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(pAction)-> + GetFromRange().MakeRange( GetDocument() ); + if ( aRange.Contains( rPos ) ) + { + pFound = pAction; + } + } + } + pAction = pAction->GetNext(); + } + + return const_cast(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 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 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; + SfxObjectShell* 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 xDPS( + GetModel(), uno::UNO_QUERY_THROW); + uno::Reference xDocProps( + xDPS->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(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(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(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(pSourceAction)->GetNewString( m_pDocument.get() ); + ScMatrixMode eMatrix = ScMatrixMode::NONE; + const ScCellValue& rCell = static_cast(pSourceAction)->GetNewCell(); + if (rCell.meType == CELLTYPE_FORMULA) + eMatrix = rCell.mpFormula->GetMatrixFlag(); + switch ( eMatrix ) + { + case ScMatrixMode::NONE : + pViewSh->EnterData( aPos.Col(), aPos.Row(), aPos.Tab(), aValue ); + break; + case ScMatrixMode::Formula : + { + SCCOL nCols; + SCROW nRows; + rCell.mpFormula->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(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(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(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(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 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 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 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 000000000..f64ecaeaa --- /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 + +#include + +#include + +using namespace ::com::sun::star; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "docshimp.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +void ScDocShell::SetInitialLinkUpdate( const SfxMedium* pMed ) +{ + if (pMed) + { + const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem( pMed->GetItemSet(), + 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_NOARG( ScDocShell, ReloadAllLinksHdl, weld::Button&, void ) +{ + 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(pColItem)->GetValue() - 1; + SCROW nRow = static_cast(pRowItem)->GetValue() - 1; + SCTAB nTab = static_cast(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(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(pItem)->GetValue(); + uno::Sequence aProperties; + if ( aAny >>= aProperties ) + aDesc.initializeFrom( aProperties ); + } + + OUString sTarget; + if ( pReqArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET ) + sTarget = static_cast(pItem)->GetValue(); + + bool bIsNewArea = true; // Default sal_True (no inquiry) + if ( pReqArgs->GetItemState( FN_PARAM_2, true, &pItem ) == SfxItemState::SET ) + bIsNewArea = static_cast(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 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(pItem)->GetValue(); + bColInit = true; + } + if( pReqArgs->HasItem( FN_PARAM_2, &pItem ) ) + { + bRowHeaders = static_cast(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 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( 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( 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(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", "", 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)); + } + } + 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 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(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 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 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(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 pFilter = ScDocShell::Factory().GetFilterContainer()->GetFilter4FilterName( aFilterName ); + auto pSet = std::make_shared( 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 ); + ErrCode nErr = pOtherDocSh->GetErrorCode(); + if (nErr) + ErrorHandler::HandleError( nErr ); // also warnings + + if ( !pOtherDocSh->GetError() ) // 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( 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(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(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 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(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 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( 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 xWarn(Application::CreateMessageDialog(pWin, + VclMessageType::Warning, VclButtonsType::Ok, + aMessage)); + xWarn->run(); + } + else + { + std::unique_ptr 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. + uno::Reference xStorable2( + GetModel(), uno::UNO_QUERY_THROW); + xStorable2->store(); + + ScTabView* pTabView = pViewData->GetView(); + if ( pTabView ) + { + pTabView->UpdateLayerLocks(); + } + } + else + { + xCloseable->close( true ); + } + } + } + else + { + xCloseable->close( true ); + std::unique_ptr 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( 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(nSlot); + if ( pItem ) + aLangText = pItem->GetValue(); + + if ( !aLangText.isEmpty() ) + { + LanguageType eLang, eLatin, eCjk, eCtl; + static const OUStringLiteral aSelectionLangPrefix(u"Current_"); + static const OUStringLiteral aParagraphLangPrefix(u"Paragraph_"); + static const OUStringLiteral aDocLangPrefix(u"Default_"); + + 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 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(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(FN_PARAM_1); + if (pItem2) + sApplyText = pItem2->GetValue(); + + static const OUStringLiteral sSpellingRule(u"Spelling_"); + 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(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 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; nTabSetCalcNotification(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; nTabSetStreamValid(nTab, false); + + PostPaintGridAll(); + auto end = std::chrono::steady_clock::now(); + SAL_INFO("sc.timing", "ScDocShell::DoHardRecalc(): took " << std::chrono::duration_cast(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(&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; nTabGetPageStyle(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(&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( + 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 oRepeatCol = m_pDocument->GetRepeatColRange( nTab ); + std::optional 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(nNewScale) ) + nNewScale = static_cast(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; nTabMAXTAB; 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 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 pDlg(pFact->CreateScStyleDlg(GetActiveDialogParent(), *pStyleSheet, true)); + + auto pRequest = std::make_shared(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( 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 pDlg(pFact->CreateScHFEditDlg( + GetActiveDialogParent(), + rStyleSet, + aStr, + nResId)); + auto xRequest = std::make_shared(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 ) +{ + + 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(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(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