Bug Summary

File:home/maarten/src/libreoffice/core/sc/source/ui/view/viewfun2.cxx
Warning:line 498, column 18
Although the value stored to 'eSum' is used in the enclosing expression, the value is never actually read from 'eSum'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name viewfun2.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /usr/include/libxml2 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D SC_DLLIMPLEMENTATION -D SC_INFO_OSVERSION="LINUX" -D SYSTEM_LIBXML -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/liborcus/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/external/clew/source/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/sc/source/core/inc -I /home/maarten/src/libreoffice/core/sc/source/filter/inc -I /home/maarten/src/libreoffice/core/sc/source/ui/inc -I /home/maarten/src/libreoffice/core/sc/inc -I /home/maarten/src/libreoffice/core/workdir/SdiTarget/sc/sdi -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/oovbaapi/normal -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/sc/source/ui/view/viewfun2.cxx
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source eCode Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <scitems.hxx>
21
22#include <sfx2/app.hxx>
23#include <editeng/borderline.hxx>
24#include <editeng/boxitem.hxx>
25#include <editeng/fontitem.hxx>
26#include <editeng/lineitem.hxx>
27#include <editeng/scripttypeitem.hxx>
28#include <svl/srchitem.hxx>
29#include <sfx2/linkmgr.hxx>
30#include <sfx2/dispatch.hxx>
31#include <sfx2/docfilt.hxx>
32#include <sfx2/docfile.hxx>
33#include <sfx2/objitem.hxx>
34#include <sfx2/viewfrm.hxx>
35#include <svl/stritem.hxx>
36#include <svl/zforlist.hxx>
37#include <svx/srchdlg.hxx>
38#include <svx/svdview.hxx>
39#include <vcl/svapp.hxx>
40#include <vcl/weld.hxx>
41#include <vcl/waitobj.hxx>
42
43#include <viewfunc.hxx>
44#include <vcl/uitest/logger.hxx>
45#include <vcl/uitest/eventdescription.hxx>
46
47#include <sc.hrc>
48#include <globstr.hrc>
49#include <scresid.hxx>
50
51#include <attrib.hxx>
52#include <autoform.hxx>
53#include <formulacell.hxx>
54#include <cellmergeoption.hxx>
55#include <compiler.hxx>
56#include <docfunc.hxx>
57#include <docpool.hxx>
58#include <docsh.hxx>
59#include <docoptio.hxx>
60#include <global.hxx>
61#include <patattr.hxx>
62#include <printfun.hxx>
63#include <refundo.hxx>
64#include <table.hxx>
65#include <tablink.hxx>
66#include <tabvwsh.hxx>
67#include <uiitems.hxx>
68#include <undoblk.hxx>
69#include <undotab.hxx>
70#include <sizedev.hxx>
71#include <editable.hxx>
72#include <docuno.hxx>
73#include <charthelper.hxx>
74#include <tabbgcolor.hxx>
75#include <clipparam.hxx>
76#include <prnsave.hxx>
77#include <searchresults.hxx>
78#include <tokenarray.hxx>
79#include <rowheightcontext.hxx>
80#include <LibreOfficeKit/LibreOfficeKitEnums.h>
81#include <comphelper/lok.hxx>
82#include <mergecellsdialog.hxx>
83#include <sheetevents.hxx>
84#include <columnspanset.hxx>
85
86#include <vector>
87#include <memory>
88#include <boost/property_tree/json_parser.hpp>
89
90#include <officecfg/Office/Calc.hxx>
91
92using namespace com::sun::star;
93using ::editeng::SvxBorderLine;
94
95namespace {
96
97void collectUIInformation(const std::map<OUString, OUString>& aParameters, const OUString& rAction)
98{
99 EventDescription aDescription;
100 aDescription.aID = "grid_window";
101 aDescription.aAction = rAction;
102 aDescription.aParameters = aParameters;
103 aDescription.aParent = "MainWindow";
104 aDescription.aKeyWord = "ScGridWinUIObject";
105
106 UITestLogger::getInstance().logEvent(aDescription);
107}
108}
109
110using ::std::vector;
111using ::std::unique_ptr;
112
113bool ScViewFunc::AdjustBlockHeight( bool bPaint, ScMarkData* pMarkData )
114{
115 ScDocShell* pDocSh = GetViewData().GetDocShell();
116 if (!pMarkData)
117 pMarkData = &GetViewData().GetMarkData();
118
119 ScDocument& rDoc = pDocSh->GetDocument();
120 std::vector<sc::ColRowSpan> aMarkedRows = pMarkData->GetMarkedRowSpans();
121
122 if (aMarkedRows.empty())
123 {
124 SCROW nCurRow = GetViewData().GetCurY();
125 aMarkedRows.emplace_back(nCurRow, nCurRow);
126 }
127
128 if (comphelper::LibreOfficeKit::isActive())
129 {
130 SCCOLROW nStart = aMarkedRows[0].mnStart;
131 OnLOKSetWidthOrHeight(nStart, /*width: */ false);
132 }
133
134 double nPPTX = GetViewData().GetPPTX();
135 double nPPTY = GetViewData().GetPPTY();
136 Fraction aZoomX = GetViewData().GetZoomX();
137 Fraction aZoomY = GetViewData().GetZoomY();
138
139 ScSizeDeviceProvider aProv(pDocSh);
140 if (aProv.IsPrinter())
141 {
142 nPPTX = aProv.GetPPTX();
143 nPPTY = aProv.GetPPTY();
144 aZoomX = aZoomY = Fraction( 1, 1 );
145 }
146
147 sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
148 bool bAnyChanged = false;
149 for (const SCTAB& nTab : *pMarkData)
150 {
151 bool bChanged = false;
152 SCROW nPaintY = 0;
153 for (const auto& rRow : aMarkedRows)
154 {
155 SCROW nStartNo = rRow.mnStart;
156 SCROW nEndNo = rRow.mnEnd;
157 ScAddress aTopLeft(0, nStartNo, nTab);
158 rDoc.UpdateScriptTypes(aTopLeft, MAXCOLCOUNT, nEndNo-nStartNo+1);
159 if (rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab))
160 {
161 if (!bChanged)
162 nPaintY = nStartNo;
163 bAnyChanged = bChanged = true;
164 }
165 }
166 // tdf#76183: recalculate objects' positions
167 if (bChanged)
168 rDoc.SetDrawPageSize(nTab);
169 if ( bPaint && bChanged )
170 pDocSh->PostPaint( 0, nPaintY, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
171 PaintPartFlags::Grid | PaintPartFlags::Left );
172 }
173
174 if ( bPaint && bAnyChanged )
175 pDocSh->UpdateOle(GetViewData());
176
177 if (comphelper::LibreOfficeKit::isActive())
178 {
179 SCTAB nTab = GetViewData().GetTabNo();
180 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
181 GetViewData().GetViewShell(),
182 false /* bColumns */, true /* bRows */,
183 true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
184 false /* bGroups */, nTab);
185 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, nTab);
186 }
187
188 return bAnyChanged;
189}
190
191bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow )
192{
193 if (comphelper::LibreOfficeKit::isActive())
194 {
195 OnLOKSetWidthOrHeight(nStartRow, /*width: */ false);
196 }
197
198 ScDocShell* pDocSh = GetViewData().GetDocShell();
199 ScDocument& rDoc = pDocSh->GetDocument();
200 SCTAB nTab = GetViewData().GetTabNo();
201 double nPPTX = GetViewData().GetPPTX();
202 double nPPTY = GetViewData().GetPPTY();
203 Fraction aZoomX = GetViewData().GetZoomX();
204 Fraction aZoomY = GetViewData().GetZoomY();
205 sal_uInt16 nOldPixel = 0;
206 if (nStartRow == nEndRow)
207 nOldPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
208
209 ScSizeDeviceProvider aProv(pDocSh);
210 if (aProv.IsPrinter())
211 {
212 nPPTX = aProv.GetPPTX();
213 nPPTY = aProv.GetPPTY();
214 aZoomX = aZoomY = Fraction( 1, 1 );
215 }
216 sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
217 bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab);
218
219 // tdf#76183: recalculate objects' positions
220 if (bChanged)
221 rDoc.SetDrawPageSize(nTab);
222
223 if (bChanged && ( nStartRow == nEndRow ))
224 {
225 sal_uInt16 nNewPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
226 if ( nNewPixel == nOldPixel )
227 bChanged = false;
228 }
229
230 if ( bChanged )
231 pDocSh->PostPaint( 0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
232 PaintPartFlags::Grid | PaintPartFlags::Left );
233
234 if (comphelper::LibreOfficeKit::isActive())
235 {
236 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
237 GetViewData().GetViewShell(),
238 false /* bColumns */, true /* bRows */,
239 true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
240 false /* bGroups */, nTab);
241 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
242 }
243
244 return bChanged;
245}
246
247namespace {
248
249enum ScAutoSum
250{
251 ScAutoSumNone = 0,
252 ScAutoSumData,
253 ScAutoSumSum,
254 ScAutoSumAverage,
255 ScAutoSumMax,
256 ScAutoSumMin,
257 ScAutoSumCount
258};
259
260}
261
262static ScAutoSum lcl_IsAutoSumData( ScDocument& rDoc, SCCOL nCol, SCROW nRow,
263 SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
264{
265 ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
266 if (aCell.hasNumeric())
267 {
268 if (aCell.meType == CELLTYPE_FORMULA)
269 {
270 ScAutoSum val = ScAutoSumNone;
271 ScTokenArray* pCode = aCell.mpFormula->GetCode();
272 if ( pCode )
273 {
274 switch( pCode->GetOuterFuncOpCode() )
275 {
276 case ocSum : val = ScAutoSumSum;
277 break;
278 case ocAverage : val = ScAutoSumAverage;
279 break;
280 case ocMax : val = ScAutoSumMax;
281 break;
282 case ocMin : val = ScAutoSumMin;
283 break;
284 case ocCount : val = ScAutoSumCount;
285 break;
286 default :
287 break;
288 }
289 if ( pCode->GetAdjacentExtendOfOuterFuncRefs( nExtend,
290 ScAddress( nCol, nRow, nTab ), eDir ) )
291 return val;
292 }
293 }
294 return ScAutoSumData;
295 }
296 return ScAutoSumNone;
297}
298
299#define SC_AUTOSUM_MAXCOUNT 20
300
301static ScAutoSum lcl_SeekAutoSumData( ScDocument& rDoc, SCCOL& nCol, SCROW& nRow,
302 SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
303{
304 sal_uInt16 nCount = 0;
305 while (nCount < SC_AUTOSUM_MAXCOUNT)
306 {
307 if ( eDir == DIR_TOP )
308 {
309 if (nRow > 0)
310 --nRow;
311 else
312 return ScAutoSumNone;
313 }
314 else
315 {
316 if (nCol > 0)
317 --nCol;
318 else
319 return ScAutoSumNone;
320 }
321 ScAutoSum eSum;
322 if ( (eSum = lcl_IsAutoSumData(
323 rDoc, nCol, nRow, nTab, eDir, nExtend )) != ScAutoSumNone )
324 return eSum;
325 ++nCount;
326 }
327 return ScAutoSumNone;
328}
329
330#undef SC_AUTOSUM_MAXCOUNT
331
332static bool lcl_FindNextSumEntryInColumn( ScDocument& rDoc, SCCOL nCol, SCROW& nRow,
333 SCTAB nTab, SCCOLROW& nExtend, SCROW nMinRow )
334{
335 const SCROW nTmp = nRow;
336 ScAutoSum eSkip = ScAutoSumNone;
337 for (;;)
338 {
339 eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend );
340 if (eSkip != ScAutoSumData || nRow <= nMinRow )
341 break;
342 --nRow;
343 }
344 return eSkip >= ScAutoSumSum && nRow < nTmp;
345}
346
347static bool lcl_FindNextSumEntryInRow( ScDocument& rDoc, SCCOL& nCol, SCROW nRow,
348 SCTAB nTab, SCCOLROW& nExtend, SCCOL nMinCol )
349{
350 const SCCOL nTmp = nCol;
351 ScAutoSum eSkip = ScAutoSumNone;
352 for (;;)
353 {
354 eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend );
355 if (eSkip != ScAutoSumData || nCol <= nMinCol )
356 break;
357 --nCol;
358 }
359 return eSkip >= ScAutoSumSum && nCol < nTmp;
360}
361
362static ScAutoSum lcl_GetAutoSumForColumnRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
363{
364 const ScAddress aStart = rRange.aStart;
365 const ScAddress aEnd = rRange.aEnd;
366 if ( aStart.Col() != aEnd.Col() )
367 {
368 return ScAutoSumNone;
369 }
370
371 const SCTAB nTab = aEnd.Tab();
372 const SCCOL nCol = aEnd.Col();
373 SCROW nEndRow = aEnd.Row();
374 SCROW nStartRow = nEndRow;
375 SCCOLROW nExtend = 0;
376 ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nCol, nEndRow, nTab, DIR_TOP, nExtend /*out*/ );
377
378 if ( eSum >= ScAutoSumSum )
379 {
380 bool bContinue = false;
381 do
382 {
383 rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
384 nEndRow = static_cast< SCROW >( nExtend );
385 bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, aStart.Row() );
386 if ( bContinue )
387 {
388 nStartRow = nEndRow;
389 }
390 } while ( bContinue );
391 }
392 else
393 {
394 while ( nStartRow > aStart.Row() )
395 {
396 eSum = lcl_IsAutoSumData( rDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ );
397 if (eSum >= ScAutoSumSum )
398 break;
399 --nStartRow;
400 }
401 rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
402 if (eSum == ScAutoSumNone)
403 eSum = ScAutoSumData;
404 }
405
406 return eSum;
407}
408
409static ScAutoSum lcl_GetAutoSumForRowRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
410{
411 const ScAddress aStart = rRange.aStart;
412 const ScAddress aEnd = rRange.aEnd;
413 if ( aStart.Row() != aEnd.Row() )
414 {
415 return ScAutoSumNone;
416 }
417
418 const SCTAB nTab = aEnd.Tab();
419 const SCROW nRow = aEnd.Row();
420 SCCOL nEndCol = aEnd.Col();
421 SCCOL nStartCol = nEndCol;
422 SCCOLROW nExtend = 0;
423 ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nEndCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
424
425 if ( eSum >= ScAutoSumSum )
426 {
427 bool bContinue = false;
428 do
429 {
430 rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
431 nEndCol = static_cast< SCCOL >( nExtend );
432 bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, aStart.Col() );
433 if ( bContinue )
434 {
435 nStartCol = nEndCol;
436 }
437 } while ( bContinue );
438 }
439 else
440 {
441 while ( nStartCol > aStart.Col() )
442 {
443 eSum = lcl_IsAutoSumData( rDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
444 if (eSum >= ScAutoSumSum )
445 break;
446 --nStartCol;
447 }
448 rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
449 if (eSum == ScAutoSumNone)
450 eSum = ScAutoSumData;
451 }
452
453 return eSum;
454}
455
456static sal_Int8 GetSubTotal( const OpCode eCode )
457{
458 sal_Int8 val;
459 switch ( eCode )
460 {
461 case ocSum : val = 9;
462 break;
463 case ocAverage : val = 1;
464 break;
465 case ocMax : val = 4;
466 break;
467 case ocMin : val = 5;
468 break;
469 case ocCount : val = 2;
470 break;
471 default : val = 9;
472 }
473
474 return val;
475}
476
477bool ScViewFunc::GetAutoSumArea( ScRangeList& rRangeList )
478{
479 ScDocument& rDoc = GetViewData().GetDocument();
480 SCTAB nTab = GetViewData().GetTabNo();
481
482 SCCOL nCol = GetViewData().GetCurX();
483 SCROW nRow = GetViewData().GetCurY();
484
485 SCCOL nStartCol = nCol;
486 SCROW nStartRow = nRow;
487 SCCOL nEndCol = nCol;
488 SCROW nEndRow = nRow;
489 SCCOL nSeekCol = nCol;
490 SCROW nSeekRow = nRow;
491 SCCOLROW nExtend; // will become valid via reference for ScAutoSumSum
492
493 bool bCol = false;
494 bool bRow = false;
495
496 ScAutoSum eSum;
497 if ( nRow != 0
498 && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
Although the value stored to 'eSum' is used in the enclosing expression, the value is never actually read from 'eSum'
499 DIR_TOP, nExtend /*out*/ )) == ScAutoSumData )
500 && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
501 DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
502 )
503 {
504 bRow = true;
505 nSeekRow = nRow - 1;
506 }
507 else if ( nCol != 0 && (eSum = lcl_IsAutoSumData( rDoc, nCol-1, nRow, nTab,
508 DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
509 {
510 bCol = true;
511 nSeekCol = nCol - 1;
512 }
513 else if ( (eSum = lcl_SeekAutoSumData( rDoc, nCol, nSeekRow, nTab, DIR_TOP, nExtend /*out*/ )) != ScAutoSumNone )
514 bRow = true;
515 else if (( eSum = lcl_SeekAutoSumData( rDoc, nSeekCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) != ScAutoSumNone )
516 bCol = true;
517
518 if ( bCol || bRow )
519 {
520 if ( bRow )
521 {
522 nStartRow = nSeekRow; // nSeekRow might be adjusted via reference
523 if ( eSum >= ScAutoSumSum && eSum <= ScAutoSumCount )
524 nEndRow = nStartRow; // only sum sums
525 else
526 nEndRow = nRow - 1; // maybe extend data area at bottom
527 }
528 else
529 {
530 nStartCol = nSeekCol; // nSeekCol might be adjusted via reference
531 if ( eSum >= ScAutoSumSum )
532 nEndCol = nStartCol; // only sum sums
533 else
534 nEndCol = nCol - 1; // maybe extend data area to the right
535 }
536 bool bContinue = false;
537 do
538 {
539 if ( eSum == ScAutoSumData )
540 {
541 if ( bRow )
542 {
543 while ( nStartRow != 0 && lcl_IsAutoSumData( rDoc, nCol,
544 nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) == eSum )
545 --nStartRow;
546 }
547 else
548 {
549 while ( nStartCol != 0 && lcl_IsAutoSumData( rDoc, nStartCol-1,
550 nRow, nTab, DIR_LEFT, nExtend /*out*/ ) == eSum )
551 --nStartCol;
552 }
553 }
554 rRangeList.push_back(
555 ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ) );
556 if ( eSum >= ScAutoSumSum )
557 {
558 if ( bRow )
559 {
560 nEndRow = static_cast< SCROW >( nExtend );
561 bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, 0 );
562 if ( bContinue )
563 {
564 nStartRow = nEndRow;
565 }
566 }
567 else
568 {
569 nEndCol = static_cast< SCCOL >( nExtend );
570 bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, 0 );
571 if ( bContinue )
572 {
573 nStartCol = nEndCol;
574 }
575 }
576 }
577 } while ( bContinue );
578 return true;
579 }
580 return false;
581}
582
583void ScViewFunc::EnterAutoSum(const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode)
584{
585 OUString aFormula = GetAutoSumFormula( rRangeList, bSubTotal, rAddr , eCode);
586 EnterBlock( aFormula, nullptr );
587}
588
589bool ScViewFunc::AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue , const OpCode eCode)
590{
591 ScDocument& rDoc = GetViewData().GetDocument();
592 const SCTAB nTab = rRange.aStart.Tab();
593 SCCOL nStartCol = rRange.aStart.Col();
594 SCROW nStartRow = rRange.aStart.Row();
595 const SCCOL nEndCol = rRange.aEnd.Col();
596 const SCROW nEndRow = rRange.aEnd.Row();
597 SCCOLROW nExtend = 0; // out parameter for lcl_IsAutoSumData
598
599 // ignore rows at the top of the given range which don't contain autosum data
600 bool bRowData = false;
601 for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
602 {
603 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
604 {
605 if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) != ScAutoSumNone )
606 {
607 bRowData = true;
608 break;
609 }
610 }
611 if ( bRowData )
612 {
613 nStartRow = nRow;
614 break;
615 }
616 }
617 if ( !bRowData )
618 {
619 return false;
620 }
621
622 // ignore columns at the left of the given range which don't contain autosum data
623 bool bColData = false;
624 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
625 {
626 for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
627 {
628 if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) != ScAutoSumNone )
629 {
630 bColData = true;
631 break;
632 }
633 }
634 if ( bColData )
635 {
636 nStartCol = nCol;
637 break;
638 }
639 }
640 if ( !bColData )
641 {
642 return false;
643 }
644
645 const bool bEndRowEmpty = rDoc.IsBlockEmpty( nTab, nStartCol, nEndRow, nEndCol, nEndRow );
646 const bool bEndColEmpty = rDoc.IsBlockEmpty( nTab, nEndCol, nStartRow, nEndCol, nEndRow );
647 bool bRow = ( nStartRow != nEndRow ) && ( bEndRowEmpty || !bEndColEmpty );
648 bool bCol = ( nStartCol != nEndCol ) && ( bEndColEmpty || nStartRow == nEndRow );
649
650 // find an empty row for entering the result
651 SCROW nInsRow = nEndRow;
652 if ( bRow && !bEndRowEmpty )
653 {
654 if ( nInsRow < rDoc.MaxRow() )
655 {
656 ++nInsRow;
657 while ( !rDoc.IsBlockEmpty( nTab, nStartCol, nInsRow, nEndCol, nInsRow ) )
658 {
659 if ( nInsRow < rDoc.MaxRow() )
660 {
661 ++nInsRow;
662 }
663 else
664 {
665 bRow = false;
666 break;
667 }
668 }
669 }
670 else
671 {
672 bRow = false;
673 }
674 }
675
676 // find an empty column for entering the result
677 SCCOL nInsCol = nEndCol;
678 if ( bCol && !bEndColEmpty )
679 {
680 if ( nInsCol < rDoc.MaxCol() )
681 {
682 ++nInsCol;
683 while ( !rDoc.IsBlockEmpty( nTab, nInsCol, nStartRow, nInsCol, nEndRow ) )
684 {
685 if ( nInsCol < rDoc.MaxCol() )
686 {
687 ++nInsCol;
688 }
689 else
690 {
691 bCol = false;
692 break;
693 }
694 }
695 }
696 else
697 {
698 bCol = false;
699 }
700 }
701
702 if ( !bRow && !bCol )
703 {
704 return false;
705 }
706
707 SCCOL nMarkEndCol = nEndCol;
708 SCROW nMarkEndRow = nEndRow;
709 ScAutoSum eSum = ScAutoSumNone;
710 SCROW nColSums = 0;
711 SCCOL nRowSums = 0;
712 SCROW nColSumsStartRow = 0;
713 SCCOL nRowSumsStartCol = 0;
714
715 if ( bRow )
716 {
717 // calculate the row sums for all columns of the given range
718
719 SCROW nSumEndRow = nEndRow;
720
721 if ( bEndRowEmpty )
722 {
723 // the last row of the given range is empty;
724 // don't take into account for calculating the autosum
725 --nSumEndRow;
726 }
727 else
728 {
729 // increase mark range
730 ++nMarkEndRow;
731 }
732
733 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
734 {
735 if ( !rDoc.IsBlockEmpty( nTab, nCol, nStartRow, nCol, nSumEndRow ) )
736 {
737 ScRangeList aRangeList;
738 // Include the originally selected start row.
739 const ScRange aRange( nCol, rRange.aStart.Row(), nTab, nCol, nSumEndRow, nTab );
740 if ( (eSum = lcl_GetAutoSumForColumnRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
741 {
742 if (++nRowSums == 1)
743 nRowSumsStartCol = aRangeList[0].aStart.Col();
744 const OUString aFormula = GetAutoSumFormula(
745 aRangeList, bSubTotal, ScAddress(nCol, nInsRow, nTab), eCode);
746 EnterData( nCol, nInsRow, nTab, aFormula );
747 }
748 }
749 }
750 }
751
752 if ( bCol )
753 {
754 // calculate the column sums for all rows of the given range
755
756 SCCOL nSumEndCol = nEndCol;
757
758 if ( bEndColEmpty )
759 {
760 // the last column of the given range is empty;
761 // don't take into account for calculating the autosum
762 --nSumEndCol;
763 }
764 else
765 {
766 // increase mark range
767 ++nMarkEndCol;
768 }
769
770 for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
771 {
772 if ( !rDoc.IsBlockEmpty( nTab, nStartCol, nRow, nSumEndCol, nRow ) )
773 {
774 ScRangeList aRangeList;
775 // Include the originally selected start column.
776 const ScRange aRange( rRange.aStart.Col(), nRow, nTab, nSumEndCol, nRow, nTab );
777 if ( (eSum = lcl_GetAutoSumForRowRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
778 {
779 if (++nColSums == 1)
780 nColSumsStartRow = aRangeList[0].aStart.Row();
781 const OUString aFormula = GetAutoSumFormula( aRangeList, bSubTotal, ScAddress(nInsCol, nRow, nTab), eCode );
782 EnterData( nInsCol, nRow, nTab, aFormula );
783 }
784 }
785 }
786 }
787
788 // Set new mark range and cursor position.
789 // For sum of sums (and data until sum) mark the actual resulting range if
790 // there is only one, or the data range if more than one. Otherwise use the
791 // original selection. All extended by end column/row where the sum is put.
792 const ScRange aMarkRange(
793 (eSum >= ScAutoSumSum ?
794 (nRowSums == 1 ? nRowSumsStartCol : nStartCol) :
795 rRange.aStart.Col()),
796 (eSum >= ScAutoSumSum ?
797 (nColSums == 1 ? nColSumsStartRow : nStartRow) :
798 rRange.aStart.Row()),
799 nTab, nMarkEndCol, nMarkEndRow, nTab );
800 MarkRange( aMarkRange, false, bContinue );
801 if ( bSetCursor )
802 {
803 SetCursor( nMarkEndCol, nMarkEndRow );
804 }
805
806 return true;
807}
808
809OUString ScViewFunc::GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr , const OpCode eCode)
810{
811 ScViewData& rViewData = GetViewData();
812 ScDocument& rDoc = rViewData.GetDocument();
813 std::unique_ptr<ScTokenArray> pArray(new ScTokenArray(rDoc));
814
815 pArray->AddOpCode(bSubTotal ? ocSubTotal : eCode);
816 pArray->AddOpCode(ocOpen);
817
818 if (bSubTotal)
819 {
820 pArray->AddDouble( GetSubTotal( eCode ) );
821 pArray->AddOpCode(ocSep);
822 }
823
824 if(!rRangeList.empty())
825 {
826 ScRangeList aRangeList = rRangeList;
827 size_t ListSize = aRangeList.size();
828 for ( size_t i = 0; i < ListSize; ++i )
829 {
830 const ScRange & r = aRangeList[i];
831 if (i != 0)
832 pArray->AddOpCode(ocSep);
833 ScComplexRefData aRef;
834 aRef.InitRangeRel(rDoc, r, rAddr);
835 pArray->AddDoubleReference(aRef);
836 }
837 }
838
839 pArray->AddOpCode(ocClose);
840
841 ScCompiler aComp(rDoc, rAddr, *pArray, rDoc.GetGrammar());
842 OUStringBuffer aBuf;
843 aComp.CreateStringFromTokenArray(aBuf);
844 OUString aFormula = aBuf.makeStringAndClear();
845 aBuf.append('=');
846 aBuf.append(aFormula);
847 return aBuf.makeStringAndClear();
848}
849
850void ScViewFunc::EnterBlock( const OUString& rString, const EditTextObject* pData )
851{
852 // test for multi selection
853
854 SCCOL nCol = GetViewData().GetCurX();
855 SCROW nRow = GetViewData().GetCurY();
856 SCTAB nTab = GetViewData().GetTabNo();
857 ScMarkData& rMark = GetViewData().GetMarkData();
858 if ( rMark.IsMultiMarked() )
859 {
860 rMark.MarkToSimple();
861 if ( rMark.IsMultiMarked() )
862 { // "Insert into multi selection not possible"
863 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0reinterpret_cast<char const *>("STR_MSSG_PASTEFROMCLIP_0"
"\004" u8"Insert into multiple selection not possible")
);
864
865 // insert into single cell
866 if ( pData )
867 EnterData(nCol, nRow, nTab, *pData);
868 else
869 EnterData( nCol, nRow, nTab, rString );
870 return;
871 }
872 }
873
874 if (GetViewData().SelectionForbidsCellFill())
875 {
876 PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
877 return;
878 }
879
880 ScDocument& rDoc = GetViewData().GetDocument();
881 OUString aNewStr = rString;
882 if ( pData )
883 {
884 const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
885 ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
886 aEngine.SetTextCurrentDefaults(*pData);
887
888 ScEditAttrTester aTester( &aEngine );
889 if (!aTester.NeedsObject())
890 {
891 aNewStr = aEngine.GetText();
892 pData = nullptr;
893 }
894 }
895
896 // Insert via PasteFromClip
897
898 WaitObject aWait( GetFrameWin() );
899
900 ScAddress aPos( nCol, nRow, nTab );
901
902 ScDocumentUniquePtr pInsDoc(new ScDocument( SCDOCMODE_CLIP ));
903 pInsDoc->ResetClip( &rDoc, nTab );
904
905 if (aNewStr[0] == '=') // Formula ?
906 {
907 // SetString not possible, because in Clipboard-Documents nothing will be compiled!
908 pInsDoc->SetFormulaCell(aPos, new ScFormulaCell(rDoc, aPos, aNewStr));
909 }
910 else if ( pData )
911 {
912 // A copy of pData will be stored.
913 pInsDoc->SetEditText(aPos, *pData, rDoc.GetEditPool());
914 }
915 else
916 pInsDoc->SetString( nCol, nRow, nTab, aNewStr );
917
918 pInsDoc->SetClipArea( ScRange(aPos) );
919 // insert Block, with Undo etc.
920 if ( !PasteFromClip( InsertDeleteFlags::CONTENTS, pInsDoc.get(), ScPasteFunc::NONE, false, false,
921 false, INS_NONE, InsertDeleteFlags::ATTRIB ) )
922 return;
923
924 const SfxUInt32Item* pItem = pInsDoc->GetAttr(
925 nCol, nRow, nTab, ATTR_VALUE_FORMAT );
926 if ( pItem )
927 { // set number format if incompatible
928 // MarkData was already MarkToSimple'ed in PasteFromClip
929 ScRange aRange;
930 rMark.GetMarkArea( aRange );
931 std::unique_ptr<ScPatternAttr> pPattern(new ScPatternAttr( rDoc.GetPool() ));
932 pPattern->GetItemSet().Put( *pItem );
933 SvNumFormatType nNewType = rDoc.GetFormatTable()->GetType( pItem->GetValue() );
934 rDoc.ApplyPatternIfNumberformatIncompatible( aRange, rMark,
935 *pPattern, nNewType );
936 }
937}
938
939// manual page break
940
941void ScViewFunc::InsertPageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
942 bool bSetModified )
943{
944 SCTAB nTab = GetViewData().GetTabNo();
945 ScAddress aCursor;
946 if (pPos)
947 aCursor = *pPos;
948 else
949 aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
950
951 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
952 InsertPageBreak( bColumn, aCursor, bRecord, bSetModified );
953
954 if ( bSuccess && bSetModified )
955 UpdatePageBreakData( true ); // for PageBreak-Mode
956}
957
958void ScViewFunc::DeletePageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
959 bool bSetModified )
960{
961 SCTAB nTab = GetViewData().GetTabNo();
962 ScAddress aCursor;
963 if (pPos)
964 aCursor = *pPos;
965 else
966 aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
967
968 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
969 RemovePageBreak( bColumn, aCursor, bRecord, bSetModified );
970
971 if ( bSuccess && bSetModified )
972 UpdatePageBreakData( true ); // for PageBreak-Mode
973}
974
975void ScViewFunc::RemoveManualBreaks()
976{
977 ScDocShell* pDocSh = GetViewData().GetDocShell();
978 ScDocument& rDoc = pDocSh->GetDocument();
979 SCTAB nTab = GetViewData().GetTabNo();
980 bool bUndo(rDoc.IsUndoEnabled());
981
982 if (bUndo)
983 {
984 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
985 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
986 rDoc.CopyToDocument( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
987 pDocSh->GetUndoManager()->AddUndoAction(
988 std::make_unique<ScUndoRemoveBreaks>( pDocSh, nTab, std::move(pUndoDoc) ) );
989 }
990
991 rDoc.RemoveManualBreaks(nTab);
992 rDoc.UpdatePageBreaks(nTab);
993
994 UpdatePageBreakData( true );
995 pDocSh->SetDocumentModified();
996 pDocSh->PostPaint( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
997}
998
999void ScViewFunc::SetPrintZoom(sal_uInt16 nScale)
1000{
1001 ScDocShell* pDocSh = GetViewData().GetDocShell();
1002 SCTAB nTab = GetViewData().GetTabNo();
1003 pDocSh->SetPrintZoom( nTab, nScale, 0/*nPages*/ );
1004}
1005
1006void ScViewFunc::AdjustPrintZoom()
1007{
1008 ScRange aRange;
1009 if ( GetViewData().GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
1010 GetViewData().GetMarkData().GetMultiMarkArea( aRange );
1011 GetViewData().GetDocShell()->AdjustPrintZoom( aRange );
1012}
1013
1014void ScViewFunc::SetPrintRanges( bool bEntireSheet, const OUString* pPrint,
1015 const OUString* pRepCol, const OUString* pRepRow,
1016 bool bAddPrint )
1017{
1018 // on all selected tables
1019
1020 ScDocShell* pDocSh = GetViewData().GetDocShell();
1021 ScDocument& rDoc = pDocSh->GetDocument();
1022 ScMarkData& rMark = GetViewData().GetMarkData();
1023 bool bUndo (rDoc.IsUndoEnabled());
1024
1025 std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
1026
1027 ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0);
1028
1029 for (const SCTAB& nTab : rMark)
1030 {
1031 ScRange aRange( 0,0,nTab );
1032
1033 // print ranges
1034
1035 if( !bAddPrint )
1036 rDoc.ClearPrintRanges( nTab );
1037
1038 if( bEntireSheet )
1039 {
1040 rDoc.SetPrintEntireSheet( nTab );
1041 }
1042 else if ( pPrint )
1043 {
1044 if ( !pPrint->isEmpty() )
1045 {
1046 const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
1047 sal_Int32 nPos = 0;
1048 do
1049 {
1050 const OUString aToken = pPrint->getToken(0, sep, nPos);
1051 if ( aRange.ParseAny( aToken, rDoc, aDetails ) & ScRefFlags::VALID )
1052 rDoc.AddPrintRange( nTab, aRange );
1053 }
1054 while (nPos >= 0);
1055 }
1056 }
1057 else // NULL = use selection (print range is always set), use empty string to delete all ranges
1058 {
1059 if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
1060 {
1061 rDoc.AddPrintRange( nTab, aRange );
1062 }
1063 else if ( rMark.IsMultiMarked() )
1064 {
1065 rMark.MarkToMulti();
1066 ScRangeListRef pList( new ScRangeList );
1067 rMark.FillRangeListWithMarks( pList.get(), false );
1068 for (size_t i = 0, n = pList->size(); i < n; ++i)
1069 {
1070 const ScRange & rR = (*pList)[i];
1071 rDoc.AddPrintRange(nTab, rR);
1072 }
1073 }
1074 }
1075
1076 // repeat columns
1077
1078 if ( pRepCol )
1079 {
1080 if ( pRepCol->isEmpty() )
1081 rDoc.SetRepeatColRange( nTab, nullptr );
1082 else
1083 if ( aRange.ParseAny( *pRepCol, rDoc, aDetails ) & ScRefFlags::VALID )
1084 rDoc.SetRepeatColRange( nTab, std::unique_ptr<ScRange>(new ScRange(aRange)) );
1085 }
1086
1087 // repeat rows
1088
1089 if ( pRepRow )
1090 {
1091 if ( pRepRow->isEmpty() )
1092 rDoc.SetRepeatRowRange( nTab, nullptr );
1093 else
1094 if ( aRange.ParseAny( *pRepRow, rDoc, aDetails ) & ScRefFlags::VALID )
1095 rDoc.SetRepeatRowRange( nTab, std::unique_ptr<ScRange>(new ScRange(aRange)) );
1096 }
1097 }
1098
1099 // undo (for all tables)
1100 if (bUndo)
1101 {
1102 SCTAB nCurTab = GetViewData().GetTabNo();
1103 std::unique_ptr<ScPrintRangeSaver> pNewRanges = rDoc.CreatePrintRangeSaver();
1104 pDocSh->GetUndoManager()->AddUndoAction(
1105 std::make_unique<ScUndoPrintRange>( pDocSh, nCurTab, std::move(pOldRanges), std::move(pNewRanges) ) );
1106 }
1107 else
1108 pOldRanges.reset();
1109
1110 // update page breaks
1111
1112 for (const auto& rTab : rMark)
1113 ScPrintFunc( pDocSh, pDocSh->GetPrinter(), rTab ).UpdatePages();
1114
1115 SfxBindings& rBindings = GetViewData().GetBindings();
1116 rBindings.Invalidate( SID_DELETE_PRINTAREA((((26000 + 521) + 50))+32) );
1117
1118 pDocSh->SetDocumentModified();
1119}
1120
1121// Merge cells
1122
1123bool ScViewFunc::TestMergeCells() // pre-test (for menu)
1124{
1125 // simple test: true if there's a selection but no multi selection and not filtered
1126
1127 const ScMarkData& rMark = GetViewData().GetMarkData();
1128 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1129 {
1130 ScRange aRange;
1131 bool bMergeable = ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE );
1132 bMergeable = bMergeable && ( aRange.aStart.Col() != aRange.aEnd.Col() ||
1133 aRange.aStart.Row() != aRange.aEnd.Row() );
1134 return bMergeable;
1135 }
1136 else
1137 return false;
1138}
1139
1140bool ScViewFunc::MergeCells( bool bApi, bool& rDoContents, bool bCenter )
1141{
1142 // Editable- and Being-Nested- test must be at the beginning (in DocFunc too),
1143 // so that the Contents-QueryBox won't appear
1144 ScEditableTester aTester( this );
1145 if (!aTester.IsEditable())
1146 {
1147 ErrorMessage(aTester.GetMessageId());
1148 return false;
1149 }
1150
1151 ScMarkData& rMark = GetViewData().GetMarkData();
1152 rMark.MarkToSimple();
1153 if (!rMark.IsMarked())
1154 {
1155 ErrorMessage(STR_NOMULTISELECTreinterpret_cast<char const *>("STR_NOMULTISELECT" "\004"
u8"This function cannot be used with multiple selections.")
);
1156 return false;
1157 }
1158
1159 ScDocShell* pDocSh = GetViewData().GetDocShell();
1160 ScDocument& rDoc = pDocSh->GetDocument();
1161
1162 ScRange aMarkRange;
1163 rMark.GetMarkArea( aMarkRange );
1164 SCCOL nStartCol = aMarkRange.aStart.Col();
1165 SCROW nStartRow = aMarkRange.aStart.Row();
1166 SCTAB nStartTab = aMarkRange.aStart.Tab();
1167 SCCOL nEndCol = aMarkRange.aEnd.Col();
1168 SCROW nEndRow = aMarkRange.aEnd.Row();
1169 SCTAB nEndTab = aMarkRange.aEnd.Tab();
1170 if ( nStartCol == nEndCol && nStartRow == nEndRow )
1171 {
1172 // nothing to do
1173 return true;
1174 }
1175
1176 if ( rDoc.HasAttrib( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
1177 HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1178 { // "Don't nest merging !"
1179 ErrorMessage(STR_MSSG_MERGECELLS_0reinterpret_cast<char const *>("STR_MSSG_MERGECELLS_0" "\004"
u8"Cell merge not possible if cells already merged")
);
1180 return false;
1181 }
1182
1183 // Check for the contents of all selected tables.
1184 bool bAskDialog = false;
1185 ScCellMergeOption aMergeOption(nStartCol, nStartRow, nEndCol, nEndRow, bCenter);
1186 for (const SCTAB& i : rMark)
1187 {
1188 aMergeOption.maTabs.insert(i);
1189
1190 sc::MultiDataCellState aState = rDoc.HasMultipleDataCells(aMergeOption.getSingleRange(i));
1191 switch (aState.meState)
1192 {
1193 case sc::MultiDataCellState::HasMultipleCells:
1194 {
1195 // this range contains multiple data cells.
1196 bAskDialog = true;
1197 break;
1198 }
1199 case sc::MultiDataCellState::HasOneCell:
1200 {
1201 // this range contains only one data cell.
1202 if (nStartCol != aState.mnCol1 || nStartRow != aState.mnRow1)
1203 rDoContents = true; // move the value to the top-left.
1204 break;
1205 }
1206 default:
1207 ;
1208 }
1209 }
1210
1211 bool bOk = true;
1212 bool bEmptyMergedCells = officecfg::Office::Calc::Compatibility::MergeCells::EmptyMergedCells::get();
1213
1214 if (bAskDialog)
1215 {
1216 bool bShowDialog = officecfg::Office::Calc::Compatibility::MergeCells::ShowDialog::get();
1217 if (!bApi && bShowDialog)
1218 {
1219 ScMergeCellsDialog aBox(GetViewData().GetDialogParent());
1220 sal_uInt16 nRetVal = aBox.run();
1221
1222 if ( nRetVal == RET_OK )
1223 {
1224 switch (aBox.GetMergeCellsOption())
1225 {
1226 case MoveContentHiddenCells:
1227 rDoContents = true;
1228 break;
1229 case KeepContentHiddenCells:
1230 bEmptyMergedCells = false;
1231 break;
1232 case EmptyContentHiddenCells:
1233 bEmptyMergedCells = true;
1234 break;
1235 default:
1236 assert(!"Unknown option for merge cells.")(static_cast <bool> (!"Unknown option for merge cells."
) ? void (0) : __assert_fail ("!\"Unknown option for merge cells.\""
, "/home/maarten/src/libreoffice/core/sc/source/ui/view/viewfun2.cxx"
, 1236, __extension__ __PRETTY_FUNCTION__))
;
1237 break;
1238 }
1239 }
1240 else if ( nRetVal == RET_CANCEL )
1241 bOk = false;
1242 }
1243 }
1244
1245 if (bOk)
1246 {
1247 bOk = pDocSh->GetDocFunc().MergeCells( aMergeOption, rDoContents, true/*bRecord*/, bApi, bEmptyMergedCells );
1248
1249 if (bOk)
1250 {
1251 SetCursor( nStartCol, nStartRow );
1252 //DoneBlockMode( sal_False);
1253 Unmark();
1254
1255 pDocSh->UpdateOle(GetViewData());
1256 UpdateInputLine();
1257
1258 OUString aStartAddress = aMarkRange.aStart.GetColRowString();
1259 OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
1260
1261 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "MERGE_CELLS");
1262 }
1263 }
1264
1265 return bOk;
1266}
1267
1268bool ScViewFunc::TestRemoveMerge()
1269{
1270 bool bMerged = false;
1271 ScRange aRange;
1272 if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
1273 {
1274 ScDocument& rDoc = GetViewData().GetDocument();
1275 if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) )
1276 bMerged = true;
1277 }
1278 return bMerged;
1279}
1280
1281static bool lcl_extendMergeRange(ScCellMergeOption& rOption, const ScRange& rRange)
1282{
1283 bool bExtended = false;
1284 if (rOption.mnStartCol > rRange.aStart.Col())
1285 {
1286 rOption.mnStartCol = rRange.aStart.Col();
1287 bExtended = true;
1288 }
1289 if (rOption.mnStartRow > rRange.aStart.Row())
1290 {
1291 rOption.mnStartRow = rRange.aStart.Row();
1292 bExtended = true;
1293 }
1294 if (rOption.mnEndCol < rRange.aEnd.Col())
1295 {
1296 rOption.mnEndCol = rRange.aEnd.Col();
1297 bExtended = true;
1298 }
1299 if (rOption.mnEndRow < rRange.aEnd.Row())
1300 {
1301 rOption.mnEndRow = rRange.aEnd.Row();
1302 bExtended = true;
1303 }
1304 return bExtended;
1305}
1306
1307bool ScViewFunc::RemoveMerge()
1308{
1309 ScRange aRange;
1310 ScEditableTester aTester( this );
1311 if (!aTester.IsEditable())
1312 {
1313 ErrorMessage(aTester.GetMessageId());
1314 return false;
1315 }
1316 else if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
1317 {
1318 ScDocument& rDoc = GetViewData().GetDocument();
1319 ScRange aExtended( aRange );
1320 rDoc.ExtendMerge( aExtended );
1321 ScDocShell* pDocSh = GetViewData().GetDocShell();
1322 const ScMarkData& rMark = GetViewData().GetMarkData();
1323 ScCellMergeOption aOption(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row());
1324 bool bExtended = false;
1325 do
1326 {
1327 bExtended = false;
1328 for (const SCTAB& i : rMark)
1329 {
1330 aOption.maTabs.insert(i);
1331 aExtended.aStart.SetTab(i);
1332 aExtended.aEnd.SetTab(i);
1333 rDoc.ExtendMerge(aExtended);
1334 rDoc.ExtendOverlapped(aExtended);
1335
1336 // Expand the current range to be inclusive of all merged
1337 // areas on all sheets.
1338 bExtended = lcl_extendMergeRange(aOption, aExtended);
1339 }
1340 }
1341 while (bExtended);
1342
1343 bool bOk = pDocSh->GetDocFunc().UnmergeCells(aOption, true/*bRecord*/, nullptr);
1344 aExtended = aOption.getFirstSingleRange();
1345 MarkRange( aExtended );
1346
1347 if (bOk)
1348 pDocSh->UpdateOle(GetViewData());
1349 }
1350
1351 OUString aCellLocation = aRange.aStart.GetColRowString();
1352 collectUIInformation({{"CELL", aCellLocation}}, "UNMERGE_CELL");
1353
1354 return true; //! bOk ??
1355}
1356
1357void ScViewFunc::FillSimple( FillDir eDir )
1358{
1359 ScRange aRange;
1360 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
1361 {
1362 ScDocShell* pDocSh = GetViewData().GetDocShell();
1363 const ScMarkData& rMark = GetViewData().GetMarkData();
1364 bool bSuccess = pDocSh->GetDocFunc().FillSimple( aRange, &rMark, eDir, false );
1365 if (bSuccess)
1366 {
1367 pDocSh->UpdateOle(GetViewData());
1368 UpdateScrollBars();
1369
1370 auto& rDoc = pDocSh->GetDocument();
1371 bool bDoAutoSpell = rDoc.GetDocOptions().IsAutoSpell();
1372 if ( bDoAutoSpell )
1373 {
1374 // Copy AutoSpellData from above(left/right/below) if no selection.
1375 switch (eDir)
1376 {
1377 case FILL_TO_BOTTOM:
1378 if (aRange.aStart.Row() > 0 && aRange.aStart.Row() == aRange.aEnd.Row())
1379 aRange.aStart.IncRow(-1);
1380 break;
1381 case FILL_TO_TOP:
1382 if (aRange.aEnd.Row() < rDoc.MaxRow() && aRange.aStart.Row() == aRange.aEnd.Row())
1383 aRange.aEnd.IncRow(1);
1384 break;
1385 case FILL_TO_RIGHT:
1386 if (aRange.aStart.Col() > 0 && aRange.aStart.Col() == aRange.aEnd.Col())
1387 aRange.aStart.IncCol(-1);
1388 break;
1389 case FILL_TO_LEFT:
1390 if (aRange.aEnd.Col() < rDoc.MaxCol() && aRange.aStart.Col() == aRange.aEnd.Col())
1391 aRange.aEnd.IncCol(1);
1392 break;
1393 }
1394 CopyAutoSpellData(eDir, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(),
1395 ::std::numeric_limits<sal_uLong>::max());
1396 }
1397
1398 // Invalidate cell slots and update input line with new content.
1399 CellContentChanged();
1400 }
1401 }
1402 else
1403 ErrorMessage(STR_NOMULTISELECTreinterpret_cast<char const *>("STR_NOMULTISELECT" "\004"
u8"This function cannot be used with multiple selections.")
);
1404}
1405
1406void ScViewFunc::FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
1407 double fStart, double fStep, double fMax )
1408{
1409 ScRange aRange;
1410 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
1411 {
1412 ScDocShell* pDocSh = GetViewData().GetDocShell();
1413 const ScMarkData& rMark = GetViewData().GetMarkData();
1414 bool bSuccess = pDocSh->GetDocFunc().
1415 FillSeries( aRange, &rMark, eDir, eCmd, eDateCmd,
1416 fStart, fStep, fMax, false );
1417 if (bSuccess)
1418 {
1419 pDocSh->UpdateOle(GetViewData());
1420 UpdateScrollBars();
1421
1422 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange);
1423 }
1424 }
1425 else
1426 ErrorMessage(STR_NOMULTISELECTreinterpret_cast<char const *>("STR_NOMULTISELECT" "\004"
u8"This function cannot be used with multiple selections.")
);
1427}
1428
1429void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
1430 SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
1431{
1432 SCTAB nTab = GetViewData().GetTabNo();
1433 ScRange aRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab );
1434 ScRange aSourceRange( aRange );
1435 ScDocShell* pDocSh = GetViewData().GetDocShell();
1436 const ScMarkData& rMark = GetViewData().GetMarkData();
1437 bool bSuccess = pDocSh->GetDocFunc().
1438 FillAuto( aRange, &rMark, eDir, nCount, false );
1439 if (!bSuccess)
1440 return;
1441
1442 MarkRange( aRange, false ); // aRange was modified in FillAuto
1443 pDocSh->UpdateOle(GetViewData());
1444 UpdateScrollBars();
1445
1446 bool bDoAutoSpell = pDocSh->GetDocument().GetDocOptions().IsAutoSpell();
1447 if ( bDoAutoSpell )
1448 CopyAutoSpellData(eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount);
1449
1450 ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh);
1451 if (!pModelObj)
1452 return;
1453
1454 ScRangeList aChangeRanges;
1455 ScRange aChangeRange( aRange );
1456 switch (eDir)
1457 {
1458 case FILL_TO_BOTTOM:
1459 aChangeRange.aStart.SetRow( aSourceRange.aEnd.Row() + 1 );
1460 break;
1461 case FILL_TO_TOP:
1462 aChangeRange.aEnd.SetRow( aSourceRange.aStart.Row() - 1 );
1463 break;
1464 case FILL_TO_RIGHT:
1465 aChangeRange.aStart.SetCol( aSourceRange.aEnd.Col() + 1 );
1466 break;
1467 case FILL_TO_LEFT:
1468 aChangeRange.aEnd.SetCol( aSourceRange.aStart.Col() - 1 );
1469 break;
1470 default:
1471 break;
1472 }
1473 aChangeRanges.push_back( aChangeRange );
1474 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
1475}
1476
1477void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
1478 SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
1479{
1480 ScGridWindow* pWin = GetActiveWin();
1481 if ( pWin->InsideVisibleRange(nStartCol, nStartRow) && pWin->InsideVisibleRange(nEndCol, nEndRow) )
1482 {
1483 if ( nCount == ::std::numeric_limits<sal_uLong>::max() )
1484 {
1485 switch( eDir )
1486 {
1487 case FILL_TO_BOTTOM:
1488 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1489 {
1490 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nStartRow);
1491 if ( !pRanges )
1492 continue;
1493 for ( SCROW nRowItr = nStartRow + 1; nRowItr <= nEndRow; ++nRowItr )
1494 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1495 }
1496 break;
1497 case FILL_TO_TOP:
1498 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1499 {
1500 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nEndRow);
1501 if ( !pRanges )
1502 continue;
1503 for ( SCROW nRowItr = nEndRow - 1; nRowItr >= nStartRow; --nRowItr )
1504 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1505 }
1506 break;
1507 case FILL_TO_RIGHT:
1508 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1509 {
1510 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nStartCol, nRowItr);
1511 if ( !pRanges )
1512 continue;
1513 for ( SCCOL nColItr = nStartCol + 1; nColItr <= nEndCol; ++nColItr )
1514 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1515 }
1516 break;
1517 case FILL_TO_LEFT:
1518 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1519 {
1520 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nEndCol, nRowItr);
1521 if ( !pRanges )
1522 continue;
1523 for ( SCCOL nColItr = nEndCol - 1; nColItr >= nStartCol; --nColItr )
1524 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1525 }
1526 break;
1527 }
1528 return;
1529 }
1530
1531 typedef const std::vector<editeng::MisspellRanges>* MisspellRangesType;
1532 SCROW nRowRepeatSize = nEndRow - nStartRow + 1;
1533 SCCOL nColRepeatSize = nEndCol - nStartCol + 1;
1534 SCROW nTillRow = 0;
1535 SCCOL nTillCol = 0;
1536 std::vector<std::vector<MisspellRangesType>> aSourceSpellRanges(nRowRepeatSize, std::vector<MisspellRangesType>(nColRepeatSize));
1537
1538 for ( SCROW nRowIdx = 0; nRowIdx < nRowRepeatSize; ++nRowIdx )
1539 for ( SCCOL nColIdx = 0; nColIdx < nColRepeatSize; ++nColIdx )
1540 aSourceSpellRanges[nRowIdx][nColIdx] = pWin->GetAutoSpellData( nStartCol + nColIdx, nStartRow + nRowIdx );
1541
1542 switch( eDir )
1543 {
1544 case FILL_TO_BOTTOM:
1545 nTillRow = nEndRow + nCount;
1546 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1547 {
1548 for ( SCROW nRowItr = nEndRow + 1; nRowItr <= nTillRow; ++nRowItr )
1549 {
1550 size_t nSourceRowIdx = ( nRowItr - nEndRow - 1 ) % nRowRepeatSize;
1551 MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
1552 if ( !pRanges )
1553 continue;
1554 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1555 }
1556 }
1557 break;
1558
1559 case FILL_TO_TOP:
1560 nTillRow = nStartRow - nCount;
1561 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1562 {
1563 for ( SCROW nRowItr = nStartRow - 1; nRowItr >= nTillRow; --nRowItr )
1564 {
1565 size_t nSourceRowIdx = nRowRepeatSize - 1 - ( ( nStartRow - 1 - nRowItr ) % nRowRepeatSize );
1566 MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
1567 if ( !pRanges )
1568 continue;
1569 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1570 }
1571 }
1572 break;
1573
1574 case FILL_TO_RIGHT:
1575 nTillCol = nEndCol + nCount;
1576 for ( SCCOL nColItr = nEndCol + 1; nColItr <= nTillCol; ++nColItr )
1577 {
1578 size_t nSourceColIdx = ( nColItr - nEndCol - 1 ) % nColRepeatSize;
1579 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1580 {
1581 MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
1582 if ( !pRanges )
1583 continue;
1584 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1585 }
1586 }
1587 break;
1588
1589 case FILL_TO_LEFT:
1590 nTillCol = nStartCol - nCount;
1591 for ( SCCOL nColItr = nStartCol - 1; nColItr >= nTillCol; --nColItr )
1592 {
1593 size_t nSourceColIdx = nColRepeatSize - 1 - ( ( nStartCol - 1 - nColItr ) % nColRepeatSize );
1594 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1595 {
1596 MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
1597 if ( !pRanges )
1598 continue;
1599 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1600 }
1601 }
1602 break;
1603 }
1604 }
1605 else
1606 pWin->ResetAutoSpell();
1607
1608}
1609
1610void ScViewFunc::FillTab( InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bAsLink )
1611{
1612 //! allow source sheet to be protected
1613 ScEditableTester aTester( this );
1614 if (!aTester.IsEditable())
1615 {
1616 ErrorMessage(aTester.GetMessageId());
1617 return;
1618 }
1619
1620 ScDocShell* pDocSh = GetViewData().GetDocShell();
1621 ScDocument& rDoc = pDocSh->GetDocument();
1622 ScMarkData& rMark = GetViewData().GetMarkData();
1623 SCTAB nTab = GetViewData().GetTabNo();
1624 bool bUndo(rDoc.IsUndoEnabled());
1625
1626 ScRange aMarkRange;
1627 rMark.MarkToSimple();
1628 bool bMulti = rMark.IsMultiMarked();
1629 if (bMulti)
1630 rMark.GetMultiMarkArea( aMarkRange );
1631 else if (rMark.IsMarked())
1632 rMark.GetMarkArea( aMarkRange );
1633 else
1634 aMarkRange = ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
1635
1636 ScDocumentUniquePtr pUndoDoc;
1637
1638 if (bUndo)
1639 {
1640 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1641 pUndoDoc->InitUndo( rDoc, nTab, nTab );
1642
1643 for (const SCTAB& i : rMark)
1644 if (i != nTab )
1645 {
1646 pUndoDoc->AddUndoTab( i, i );
1647 aMarkRange.aStart.SetTab( i );
1648 aMarkRange.aEnd.SetTab( i );
1649 rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc );
1650 }
1651 }
1652
1653 if (bMulti)
1654 rDoc.FillTabMarked( nTab, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
1655 else
1656 {
1657 aMarkRange.aStart.SetTab( nTab );
1658 aMarkRange.aEnd.SetTab( nTab );
1659 rDoc.FillTab( aMarkRange, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
1660 }
1661
1662 if (bUndo)
1663 { //! for ChangeTrack not until the end
1664 pDocSh->GetUndoManager()->AddUndoAction(
1665 std::make_unique<ScUndoFillTable>( pDocSh, rMark,
1666 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nTab,
1667 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab,
1668 std::move(pUndoDoc), bMulti, nTab, nFlags, nFunction, bSkipEmpty, bAsLink ) );
1669 }
1670
1671 pDocSh->PostPaintGridAll();
1672 pDocSh->PostDataChanged();
1673}
1674
1675/** Downward fill of selected cell(s) by double-clicking cross-hair cursor
1676
1677 Either, extends a current selection if non-empty cells exist immediately
1678 below the selection, overwriting cells below the selection up to the
1679 minimum row of already filled cells.
1680
1681 Or, extends a current selection down to the last non-empty cell of an
1682 adjacent column when the lower-right corner of the selection is
1683 double-clicked. It uses a left-adjoining non-empty column as a guide if
1684 such is available, otherwise a right-adjoining non-empty column is used.
1685
1686 @return No return value
1687
1688 @see #i12313#
1689*/
1690void ScViewFunc::FillCrossDblClick()
1691{
1692 ScRange aRange;
1693 GetViewData().GetSimpleArea( aRange );
1694 aRange.PutInOrder();
1695
1696 SCTAB nTab = GetViewData().GetCurPos().Tab();
1697 SCCOL nStartX = aRange.aStart.Col();
1698 SCROW nStartY = aRange.aStart.Row();
1699 SCCOL nEndX = aRange.aEnd.Col();
1700 SCROW nEndY = aRange.aEnd.Row();
1701
1702 ScDocument& rDoc = GetViewData().GetDocument();
1703
1704 if (nEndY >= rDoc.MaxRow())
1705 // Nothing to fill.
1706 return;
1707
1708 // Make sure the selection is not empty
1709 if ( rDoc.IsBlockEmpty( nTab, nStartX, nStartY, nEndX, nEndY ) )
1710 return;
1711
1712 // If there is data in all columns immediately below the selection then
1713 // switch to overwriting fill.
1714 SCROW nOverWriteEndRow = rDoc.MaxRow();
1715 for (SCCOL nCol = nStartX; nCol <= nEndX; ++nCol)
1716 {
1717 if (rDoc.HasData( nCol, nEndY + 1, nTab))
1718 {
1719 // Determine the shortest data column to end the fill.
1720 SCROW nY = nEndY + 1;
1721 // FindAreaPos() returns the start row of the next data block if
1722 // the current row is the last row of a data block and an empty
1723 // cell follows. Somewhat unexpected behaviour...
1724 // So check beforehand if there is one non-empty cell following.
1725 if (rDoc.HasData( nCol, nY + 1, nTab))
1726 {
1727 rDoc.FindAreaPos( nCol, nY, nTab, SC_MOVE_DOWN);
1728 if (nOverWriteEndRow > nY)
1729 nOverWriteEndRow = nY;
1730 }
1731 else
1732 {
1733 nOverWriteEndRow = nY;
1734 }
1735 }
1736 else
1737 {
1738 nOverWriteEndRow = 0;
1739 break; // for
1740 }
1741 }
1742
1743 if (nOverWriteEndRow > nEndY)
1744 {
1745 FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nOverWriteEndRow - nEndY);
1746 return;
1747 }
1748
1749 // Non-overwriting fill follows.
1750
1751 const bool bDataLeft = (nStartX > 0);
1752 if (!bDataLeft && nEndX >= rDoc.MaxCol())
1753 // Absolutely no data left or right of selection.
1754 return;
1755
1756 // Check that there is
1757 // 1) data immediately left (preferred) or right of start (row) of selection
1758 // 2) data there below
1759 // 3) no data immediately below selection
1760
1761 SCCOL nMovX = (bDataLeft ? nStartX - 1 : nEndX + 1);
1762 SCROW nMovY = nStartY;
1763 bool bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
1764 if (!bDataFound && bDataLeft && nEndX < rDoc.MaxCol())
1765 {
1766 nMovX = nEndX + 1; // check right
1767 bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
1768 }
1769
1770 if (!(bDataFound && rDoc.IsBlockEmpty( nTab, nStartX, nEndY + 1, nEndX, nEndY + 1, true)))
1771 return;
1772
1773 // Get end of data left or right.
1774 rDoc.FindAreaPos( nMovX, nMovY, nTab, SC_MOVE_DOWN);
1775 // Find minimum end row of below empty area and data right.
1776 for (SCCOL nX = nStartX; nX <= nEndX; ++nX)
1777 {
1778 SCROW nY = nEndY + 1;
1779 // Get next row with data in this column.
1780 rDoc.FindAreaPos( nX, nY, nTab, SC_MOVE_DOWN);
1781 if (nMovY == rDoc.MaxRow() && nY == rDoc.MaxRow())
1782 {
1783 // FindAreaPos() returns MAXROW also if there is no data at all
1784 // from the start, so check if that contains data if the nearby
1785 // (left or right) data ends there and increment if no data
1786 // here, pretending the next data would be thereafter so nMovY
1787 // will not be decremented.
1788 if (!rDoc.HasData( nX, nY, nTab))
1789 ++nY;
1790 }
1791 if (nMovY > nY - 1)
1792 nMovY = nY - 1;
1793 }
1794
1795 if (nMovY > nEndY)
1796 {
1797 FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nMovY - nEndY);
1798 }
1799}
1800
1801void ScViewFunc::ConvertFormulaToValue()
1802{
1803 ScRange aRange;
1804 GetViewData().GetSimpleArea(aRange);
1805 aRange.PutInOrder();
1806
1807 ScDocShell* pDocSh = GetViewData().GetDocShell();
1808 pDocSh->GetDocFunc().ConvertFormulaToValue(aRange, true);
1809 pDocSh->PostPaint(aRange, PaintPartFlags::Grid);
1810}
1811
1812void ScViewFunc::TransliterateText( TransliterationFlags nType )
1813{
1814 ScMarkData aFuncMark = GetViewData().GetMarkData();
1815 if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
1816 {
1817 // no selection -> use cursor position
1818
1819 ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
1820 aFuncMark.SetMarkArea( ScRange( aCursor ) );
1821 }
1822
1823 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
1824 TransliterateText( aFuncMark, nType, false );
1825 if (bSuccess)
1826 {
1827 GetViewData().GetViewShell()->UpdateInputHandler();
1828 }
1829}
1830
1831// AutoFormat
1832
1833ScAutoFormatData* ScViewFunc::CreateAutoFormatData()
1834{
1835 ScAutoFormatData* pData = nullptr;
1836 SCCOL nStartCol;
1837 SCROW nStartRow;
1838 SCTAB nStartTab;
1839 SCCOL nEndCol;
1840 SCROW nEndRow;
1841 SCTAB nEndTab;
1842 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
1843 {
1844 if ( nEndCol-nStartCol >= 3 && nEndRow-nStartRow >= 3 )
1845 {
1846 ScDocument& rDoc = GetViewData().GetDocument();
1847 pData = new ScAutoFormatData;
1848 rDoc.GetAutoFormatData( nStartTab, nStartCol,nStartRow,nEndCol,nEndRow, *pData );
1849 }
1850 }
1851 return pData;
1852}
1853
1854void ScViewFunc::AutoFormat( sal_uInt16 nFormatNo )
1855{
1856 ScRange aRange;
1857 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
1858 {
1859 ScDocShell* pDocSh = GetViewData().GetDocShell();
1860 ScMarkData& rMark = GetViewData().GetMarkData();
1861
1862 bool bSuccess = pDocSh->GetDocFunc().AutoFormat( aRange, &rMark, nFormatNo, false );
1863 if (bSuccess)
1864 pDocSh->UpdateOle(GetViewData());
1865 }
1866 else
1867 ErrorMessage(STR_NOMULTISELECTreinterpret_cast<char const *>("STR_NOMULTISELECT" "\004"
u8"This function cannot be used with multiple selections.")
);
1868}
1869
1870// Search & Replace
1871
1872bool ScViewFunc::SearchAndReplace( const SvxSearchItem* pSearchItem,
1873 bool bAddUndo, bool bIsApi )
1874{
1875 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
1876 ScDocShell* pDocSh = GetViewData().GetDocShell();
1877 ScDocument& rDoc = pDocSh->GetDocument();
1878 ScMarkData& rMark = GetViewData().GetMarkData();
1879 if (bAddUndo && !rDoc.IsUndoEnabled())
1880 bAddUndo = false;
1881
1882 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() && (pSearchItem->HasStartPoint()) )
1883 {
1884 // No selection -> but we have a start point (top left corner of the
1885 // current view), start searching from there, not from the current
1886 // cursor position.
1887 SCCOL nPosX;
1888 SCROW nPosY;
1889
1890 int nPixelX = pSearchItem->GetStartPointX() * GetViewData().GetPPTX();
1891 int nPixelY = pSearchItem->GetStartPointY() * GetViewData().GetPPTY();
1892
1893 GetViewData().GetPosFromPixel(nPixelX, nPixelY, GetViewData().GetActivePart(), nPosX, nPosY);
1894
1895 AlignToCursor( nPosX, nPosY, SC_FOLLOW_JUMP );
1896 SetCursor( nPosX, nPosY, true );
1897 }
1898
1899 SCCOL nCol, nOldCol;
1900 SCROW nRow, nOldRow;
1901 SCTAB nTab, nOldTab;
1902 nCol = nOldCol = GetViewData().GetCurX();
1903 nRow = nOldRow = GetViewData().GetCurY();
1904 nTab = nOldTab = GetViewData().GetTabNo();
1905
1906 SvxSearchCmd nCommand = pSearchItem->GetCommand();
1907 bool bAllTables = pSearchItem->IsAllTables();
1908 std::set<SCTAB> aOldSelectedTables;
1909 SCTAB nLastTab = rDoc.GetTableCount() - 1;
1910 SCTAB nStartTab, nEndTab;
1911 if ( bAllTables )
1912 {
1913 nStartTab = 0;
1914 nEndTab = nLastTab;
1915 std::set<SCTAB> aTmp(rMark.begin(), rMark.end());
1916 aOldSelectedTables.swap(aTmp);
1917 }
1918 else
1919 { //! at least one is always selected
1920 nStartTab = rMark.GetFirstSelected();
1921 nEndTab = rMark.GetLastSelected();
1922 }
1923
1924 if ( nCommand == SvxSearchCmd::FIND
1925 || nCommand == SvxSearchCmd::FIND_ALL)
1926 bAddUndo = false;
1927
1928 //! account for bAttrib during Undo !!!
1929
1930 ScDocumentUniquePtr pUndoDoc;
1931 std::unique_ptr<ScMarkData> pUndoMark;
1932 OUString aUndoStr;
1933 if (bAddUndo)
1934 {
1935 pUndoMark.reset(new ScMarkData(rMark)); // Mark is being modified
1936 if ( nCommand == SvxSearchCmd::REPLACE_ALL )
1937 {
1938 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
1939 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
1940 }
1941 }
1942
1943 if ( bAllTables )
1944 { //! select all, after pUndoMark has been created
1945 for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
1946 {
1947 rMark.SelectTable( j, true );
1948 }
1949 }
1950
1951 DoneBlockMode(true); // don't delete mark
1952 InitOwnBlockMode();
1953
1954 // If search starts at the beginning don't ask again whether it shall start at the beginning
1955 bool bFirst = true;
1956 if ( nCol == 0 && nRow == 0 && nTab == nStartTab && !pSearchItem->GetBackward() )
1957 bFirst = false;
1958
1959 bool bFound = false;
1960 while (true)
1961 {
1962 GetFrameWin()->EnterWait();
1963 ScRangeList aMatchedRanges;
1964 if (rDoc.SearchAndReplace(*pSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, pUndoDoc.get()))
1965 {
1966 bFound = true;
1967 if (bAddUndo)
1968 {
1969 GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
1970 std::make_unique<ScUndoReplace>( GetViewData().GetDocShell(), *pUndoMark,
1971 nCol, nRow, nTab,
1972 aUndoStr, std::move(pUndoDoc), pSearchItem ) );
1973 }
1974
1975 if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
1976 {
1977 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
1978 bool bShow = GetViewData().GetViewShell()->GetViewData().GetOptions().GetOption( VOPT_SUMMARY );
1979
1980 if (bShow && pViewFrm && !comphelper::LibreOfficeKit::isActive())
1981 {
1982 pViewFrm->ShowChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
1983 SfxChildWindow* pWnd = pViewFrm->GetChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
1984 if (pWnd)
1985 {
1986 sc::SearchResultsDlg* pDlg = static_cast<sc::SearchResultsDlg*>(pWnd->GetController().get());
1987 if (pDlg)
1988 pDlg->FillResults(rDoc, aMatchedRanges,
1989 pSearchItem->GetCellType() == SvxSearchCellType::NOTE);
1990 }
1991 }
1992
1993 rMark.ResetMark();
1994 for (size_t i = 0, n = aMatchedRanges.size(); i < n; ++i)
1995 {
1996 const ScRange& r = aMatchedRanges[i];
1997 if (r.aStart.Tab() == nTab)
1998 rMark.SetMultiMarkArea(r);
1999 }
2000 }
2001
2002 break; // break 'while (TRUE)'
2003 }
2004 else if ( bFirst && (nCommand == SvxSearchCmd::FIND ||
2005 nCommand == SvxSearchCmd::REPLACE) )
2006 {
2007 bFirst = false;
2008 GetFrameWin()->LeaveWait();
2009 if (!bIsApi)
2010 {
2011 if ( nStartTab == nEndTab )
2012 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::EndSheet);
2013 else
2014 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
2015
2016 rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow );
2017 if (pSearchItem->GetBackward())
2018 nTab = nEndTab;
2019 else
2020 nTab = nStartTab;
2021 }
2022 else
2023 {
2024 break; // break 'while (TRUE)'
2025 }
2026 }
2027 else // nothing found
2028 {
2029 if ( nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL )
2030 {
2031 pDocSh->PostPaintGridAll(); // Mark
2032 }
2033
2034 GetFrameWin()->LeaveWait();
2035 if (!bIsApi)
2036 {
2037 GetViewData().GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, pSearchItem->GetSearchString().toUtf8().getStr());
2038 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
2039 }
2040
2041 break; // break 'while (TRUE)'
2042 }
2043 } // of while true
2044
2045 if (!aOldSelectedTables.empty())
2046 {
2047 // restore originally selected table
2048 for (SCTAB i = 0; i <= nEndTab; ++i)
2049 rMark.SelectTable(i, false);
2050
2051 for (const auto& rTab : aOldSelectedTables)
2052 rMark.SelectTable(rTab, true);
2053
2054 if ( bFound )
2055 { // if a table is selected as a "match" it remains selected.
2056 rMark.SelectTable( nTab, true );
2057 // It's a swap if only one table was selected before
2058 //! otherwise now one table more might be selected
2059 if ( aOldSelectedTables.size() == 1 && nTab != nOldTab )
2060 rMark.SelectTable( nOldTab, false );
2061 }
2062 }
2063
2064 // Avoid LOK selection notifications before we have all the results.
2065 GetViewData().GetViewShell()->setTiledSearching(true);
2066 MarkDataChanged();
2067 GetViewData().GetViewShell()->setTiledSearching(false);
2068
2069 if ( bFound )
2070 {
2071 if ( nTab != GetViewData().GetTabNo() )
2072 SetTabNo( nTab );
2073
2074 // if nothing is marked, DoneBlockMode, then marking can start
2075 // directly from this place via Shift-Cursor
2076 if (!rMark.IsMarked() && !rMark.IsMultiMarked())
2077 DoneBlockMode(true);
2078
2079 AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
2080 SetCursor( nCol, nRow, true );
2081
2082 if (comphelper::LibreOfficeKit::isActive())
2083 {
2084 Point aCurPos = GetViewData().GetScrPos(nCol, nRow, GetViewData().GetActivePart());
2085
2086 // just update the cell selection
2087 ScGridWindow* pGridWindow = GetViewData().GetActiveWin();
2088 // Don't move cell selection handles for find-all: selection of all but the first result would be lost.
2089 if (pGridWindow && nCommand == SvxSearchCmd::FIND)
2090 {
2091 // move the cell selection handles
2092 pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_RESET, aCurPos.X(), aCurPos.Y());
2093 pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_START, aCurPos.X(), aCurPos.Y());
2094 pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_END, aCurPos.X(), aCurPos.Y());
2095 }
2096
2097 if (pGridWindow)
2098 {
2099 std::vector<tools::Rectangle> aLogicRects;
2100 pGridWindow->GetCellSelection(aLogicRects);
2101
2102 boost::property_tree::ptree aTree;
2103 aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr());
2104 aTree.put("highlightAll", nCommand == SvxSearchCmd::FIND_ALL);
2105
2106 boost::property_tree::ptree aSelections;
2107 for (const tools::Rectangle& rLogicRect : aLogicRects)
2108 {
2109 boost::property_tree::ptree aSelection;
2110 aSelection.put("part", OString::number(nTab).getStr());
2111 aSelection.put("rectangles", rLogicRect.toString().getStr());
2112 aSelections.push_back(std::make_pair("", aSelection));
2113 }
2114 aTree.add_child("searchResultSelection", aSelections);
2115
2116 std::stringstream aStream;
2117 boost::property_tree::write_json(aStream, aTree);
2118 OString aPayload = aStream.str().c_str();
2119 SfxViewShell* pViewShell = GetViewData().GetViewShell();
2120 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr());
2121
2122 // Trigger LOK_CALLBACK_TEXT_SELECTION now.
2123 MarkDataChanged();
2124 }
2125 }
2126
2127 if ( nCommand == SvxSearchCmd::REPLACE
2128 || nCommand == SvxSearchCmd::REPLACE_ALL )
2129 {
2130 if ( nCommand == SvxSearchCmd::REPLACE )
2131 {
2132 pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid );
2133
2134 // jump to next cell if we replaced everything in the cell
2135 // where the cursor was positioned (but avoid switching tabs)
2136 if ( nCol == nOldCol && nRow == nOldRow && nTab == nOldTab )
2137 {
2138 SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
2139 aSearchItem.SetCommand(SvxSearchCmd::FIND);
2140 aSearchItem.SetWhich(SID_SEARCH_ITEMTypedWhichId<SvxSearchItem>(10000 + 291));
2141
2142 ScRangeList aMatchedRanges;
2143 ScTable::UpdateSearchItemAddressForReplace( aSearchItem, nCol, nRow );
2144 if ( rDoc.SearchAndReplace( aSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr ) &&
2145 ( nTab == nOldTab ) &&
2146 ( nCol != nOldCol || nRow != nOldRow ) )
2147 {
2148 AlignToCursor(nCol, nRow, SC_FOLLOW_JUMP);
2149 SetCursor( nCol, nRow, true );
2150 }
2151 }
2152 }
2153 else
2154 pDocSh->PostPaintGridAll();
2155 pDocSh->SetDocumentModified();
2156 }
2157 else if ( nCommand == SvxSearchCmd::FIND_ALL )
2158 pDocSh->PostPaintGridAll(); // mark
2159 GetFrameWin()->LeaveWait();
2160 }
2161 return bFound;
2162}
2163
2164// Goal Seek
2165
2166void ScViewFunc::Solve( const ScSolveParam& rParam )
2167{
2168 ScDocument& rDoc = GetViewData().GetDocument();
2169
2170 SCCOL nDestCol = rParam.aRefVariableCell.Col();
2171 SCROW nDestRow = rParam.aRefVariableCell.Row();
2172 SCTAB nDestTab = rParam.aRefVariableCell.Tab();
2173
2174 ScEditableTester aTester( rDoc, nDestTab, nDestCol,nDestRow, nDestCol,nDestRow );
2175 if (!aTester.IsEditable())
2176 {
2177 ErrorMessage(aTester.GetMessageId());
2178 return;
2179 }
2180
2181 OUString aTargetValStr;
2182 if ( rParam.pStrTargetVal )
2183 aTargetValStr = *rParam.pStrTargetVal;
2184
2185 OUString aMsgStr;
2186 OUString aResStr;
2187 double nSolveResult;
2188
2189 GetFrameWin()->EnterWait();
2190
2191 bool bExact =
2192 rDoc.Solver(
2193 rParam.aRefFormulaCell.Col(),
2194 rParam.aRefFormulaCell.Row(),
2195 rParam.aRefFormulaCell.Tab(),
2196 nDestCol, nDestRow, nDestTab,
2197 aTargetValStr,
2198 nSolveResult );
2199
2200 GetFrameWin()->LeaveWait();
2201
2202 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
2203 sal_uLong nFormat = 0;
2204 const ScPatternAttr* pPattern = rDoc.GetPattern( nDestCol, nDestRow, nDestTab );
2205 if ( pPattern )
2206 nFormat = pPattern->GetNumberFormat( pFormatter );
2207 const Color* p;
2208 pFormatter->GetOutputString( nSolveResult, nFormat, aResStr, &p );
2209
2210 if ( bExact )
2211 {
2212 aMsgStr += ScResId( STR_MSSG_SOLVE_0reinterpret_cast<char const *>("STR_MSSG_SOLVE_0" "\004"
u8"Goal Seek succeeded. Result: ")
) +
2213 aResStr +
2214 ScResId( STR_MSSG_SOLVE_1reinterpret_cast<char const *>("STR_MSSG_SOLVE_1" "\004"
u8"\n\nInsert the result into the variable cell?")
);
2215 }
2216 else
2217 {
2218 aMsgStr = ScResId( STR_MSSG_SOLVE_2reinterpret_cast<char const *>("STR_MSSG_SOLVE_2" "\004"
u8"Goal Seek failed.\n\n")
) +
2219 ScResId( STR_MSSG_SOLVE_3reinterpret_cast<char const *>("STR_MSSG_SOLVE_3" "\004"
u8"Insert the closest value (")
) +
2220 aResStr +
2221 ScResId( STR_MSSG_SOLVE_4reinterpret_cast<char const *>("STR_MSSG_SOLVE_4" "\004"
u8") into the variable cell anyway?")
);
2222 }
2223
2224 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
2225 VclMessageType::Question, VclButtonsType::YesNo, aMsgStr));
2226 xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0reinterpret_cast<char const *>("STR_MSSG_DOSUBTOTALS_0"
"\004" u8"%PRODUCTNAME Calc")
));
2227 xBox->set_default_response(RET_NO);
2228 if (xBox->run() == RET_YES)
2229 EnterValue( nDestCol, nDestRow, nDestTab, nSolveResult );
2230
2231 GetViewData().GetViewShell()->UpdateInputHandler( true );
2232}
2233
2234// multi operation
2235
2236void ScViewFunc::TabOp( const ScTabOpParam& rParam, bool bRecord )
2237{
2238 ScRange aRange;
2239 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
2240 {
2241 ScDocShell* pDocSh = GetViewData().GetDocShell();
2242 ScMarkData& rMark = GetViewData().GetMarkData();
2243 pDocSh->GetDocFunc().TabOp( aRange, &rMark, rParam, bRecord, false );
2244 }
2245 else
2246 ErrorMessage(STR_NOMULTISELECTreinterpret_cast<char const *>("STR_NOMULTISELECT" "\004"
u8"This function cannot be used with multiple selections.")
);
2247}
2248
2249void ScViewFunc::MakeScenario( const OUString& rName, const OUString& rComment,
2250 const Color& rColor, ScScenarioFlags nFlags )
2251{
2252 ScDocShell* pDocSh = GetViewData().GetDocShell();
2253 ScMarkData& rMark = GetViewData().GetMarkData();
2254 SCTAB nTab = GetViewData().GetTabNo();
2255
2256 SCTAB nNewTab = pDocSh->MakeScenario( nTab, rName, rComment, rColor, nFlags, rMark );
2257 if (nFlags & ScScenarioFlags::CopyAll)
2258 SetTabNo( nNewTab, true ); // ScScenarioFlags::CopyAll -> visible
2259 else
2260 {
2261 SfxBindings& rBindings = GetViewData().GetBindings();
2262 rBindings.Invalidate( SID_STATUS_DOCPOS((26000 + 100) + 14) ); // Statusbar
2263 rBindings.Invalidate( SID_ROWCOL_SELCOUNT((26000 + 100) + 38) ); // Statusbar
2264 rBindings.Invalidate( SID_TABLES_COUNT((((26000 + 521) + 50))+83) );
2265 rBindings.Invalidate( SID_SELECT_SCENARIO((((((((((((((((((26000 + 200) + 20)) + 20)) + 20)) + 25)) + 22
)) + 20)) + 29)) + 20))+13)
);
2266 rBindings.Invalidate( FID_TABLE_SHOW((((((((((26000 + 200) + 20)) + 20)) + 20)) + 25)) + 10) );
2267 }
2268}
2269
2270void ScViewFunc::ExtendScenario()
2271{
2272 ScEditableTester aTester( this );
2273 if (!aTester.IsEditable())
2274 {
2275 ErrorMessage(aTester.GetMessageId());
2276 return;
2277 }
2278
2279 // Undo: apply attributes
2280
2281 ScDocument& rDoc = GetViewData().GetDocument();
2282 ScPatternAttr aPattern( rDoc.GetPool() );
2283 aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) );
2284 aPattern.GetItemSet().Put( ScProtectionAttr( true ) );
2285 ApplySelectionPattern(aPattern);
2286}
2287
2288void ScViewFunc::UseScenario( const OUString& rName )
2289{
2290 ScDocShell* pDocSh = GetViewData().GetDocShell();
2291 SCTAB nTab = GetViewData().GetTabNo();
2292
2293 DoneBlockMode();
2294 InitOwnBlockMode();
2295 pDocSh->UseScenario( nTab, rName );
2296}
2297
2298// Insert table
2299
2300bool ScViewFunc::InsertTable( const OUString& rName, SCTAB nTab, bool bRecord )
2301{
2302 // Order Table/Name is inverted for DocFunc
2303 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
2304 InsertTable( nTab, rName, bRecord, false );
2305 if (bSuccess)
2306 SetTabNo( nTab, true );
2307
2308 return bSuccess;
2309}
2310
2311// Insert tables
2312
2313void ScViewFunc::InsertTables(std::vector<OUString>& aNames, SCTAB nTab,
2314 SCTAB nCount, bool bRecord )
2315{
2316 ScDocShell* pDocSh = GetViewData().GetDocShell();
2317 ScDocument& rDoc = pDocSh->GetDocument();
2318 if (bRecord && !rDoc.IsUndoEnabled())
2319 bRecord = false;
2320
2321 WaitObject aWait( GetFrameWin() );
2322
2323 if (bRecord)
2324 {
2325 rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
2326 }
2327
2328 bool bFlag=false;
2329
2330 if(aNames.empty())
2331 {
2332 rDoc.CreateValidTabNames(aNames, nCount);
2333 }
2334 if (rDoc.InsertTabs(nTab, aNames))
2335 {
2336 pDocSh->Broadcast( ScTablesHint( SC_TABS_INSERTED6, nTab, nCount ) );
2337 bFlag = true;
2338 }
2339
2340 if (!bFlag)
2341 return;
2342
2343 if (bRecord)
2344 pDocSh->GetUndoManager()->AddUndoAction(
2345 std::make_unique<ScUndoInsertTables>( pDocSh, nTab, aNames));
2346
2347 // Update views
2348
2349 SetTabNo( nTab, true );
2350 pDocSh->PostPaintExtras();
2351 pDocSh->SetDocumentModified();
2352 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2353}
2354
2355bool ScViewFunc::AppendTable( const OUString& rName, bool bRecord )
2356{
2357 ScDocShell* pDocSh = GetViewData().GetDocShell();
2358 ScDocument& rDoc = pDocSh->GetDocument();
2359 if (bRecord && !rDoc.IsUndoEnabled())
2360 bRecord = false;
2361
2362 WaitObject aWait( GetFrameWin() );
2363
2364 if (bRecord)
2365 rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
2366
2367 if (rDoc.InsertTab( SC_TAB_APPEND, rName ))
2368 {
2369 SCTAB nTab = rDoc.GetTableCount()-1;
2370 if (bRecord)
2371 pDocSh->GetUndoManager()->AddUndoAction(
2372 std::make_unique<ScUndoInsertTab>( pDocSh, nTab, true, rName));
2373 GetViewData().InsertTab( nTab );
2374 SetTabNo( nTab, true );
2375 pDocSh->PostPaintExtras();
2376 pDocSh->SetDocumentModified();
2377 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2378 return true;
2379 }
2380 else
2381 {
2382 return false;
2383 }
2384}
2385
2386void ScViewFunc::DeleteTable( SCTAB nTab, bool bRecord )
2387{
2388 ScDocShell* pDocSh = GetViewData().GetDocShell();
2389 ScDocument& rDoc = pDocSh->GetDocument();
2390
2391 bool bSuccess = pDocSh->GetDocFunc().DeleteTable( nTab, bRecord );
2392 if (bSuccess)
2393 {
2394 SCTAB nNewTab = nTab;
2395 if ( nNewTab >= rDoc.GetTableCount() )
2396 --nNewTab;
2397 SetTabNo( nNewTab, true );
2398 }
2399}
2400
2401//only use this method for undo for now, all sheets must be connected
2402//this method doesn't support undo for now, merge it when it with the other method later
2403void ScViewFunc::DeleteTables( const SCTAB nTab, SCTAB nSheets )
2404{
2405 ScDocShell* pDocSh = GetViewData().GetDocShell();
2406 ScDocument& rDoc = pDocSh->GetDocument();
2407 bool bVbaEnabled = rDoc.IsInVBAMode();
2408 SCTAB nNewTab = nTab;
2409 WaitObject aWait( GetFrameWin() );
2410
2411 while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
2412 --nNewTab;
2413
2414 if (!rDoc.DeleteTabs(nTab, nSheets))
2415 return;
2416
2417 if( bVbaEnabled )
2418 {
2419 for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
2420 {
2421 OUString sCodeName;
2422 bool bHasCodeName = rDoc.GetCodeName( nTab + aTab, sCodeName );
2423 if ( bHasCodeName )
2424 VBA_DeleteModule( *pDocSh, sCodeName );
2425 }
2426 }
2427
2428 pDocSh->Broadcast( ScTablesHint( SC_TABS_DELETED7, nTab, nSheets ) );
2429 if ( nNewTab >= rDoc.GetTableCount() )
2430 nNewTab = rDoc.GetTableCount() - 1;
2431 SetTabNo( nNewTab, true );
2432
2433 pDocSh->PostPaintExtras();
2434 pDocSh->SetDocumentModified();
2435
2436 SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
2437 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2438 pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
2439 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2440}
2441
2442bool ScViewFunc::DeleteTables(const vector<SCTAB> &TheTabs, bool bRecord )
2443{
2444 ScDocShell* pDocSh = GetViewData().GetDocShell();
2445 ScDocument& rDoc = pDocSh->GetDocument();
2446 bool bVbaEnabled = rDoc.IsInVBAMode();
2447 SCTAB nNewTab = TheTabs.front();
2448 WaitObject aWait( GetFrameWin() );
2449 if (bRecord && !rDoc.IsUndoEnabled())
2450 bRecord = false;
2451 if ( bVbaEnabled )
2452 bRecord = false;
2453
2454 while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
2455 --nNewTab;
2456
2457 bool bWasLinked = false;
2458 ScDocumentUniquePtr pUndoDoc;
2459 std::unique_ptr<ScRefUndoData> pUndoData;
2460 if (bRecord)
2461 {
2462 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2463 SCTAB nCount = rDoc.GetTableCount();
2464
2465 OUString aOldName;
2466 for(size_t i=0; i<TheTabs.size(); ++i)
2467 {
2468 SCTAB nTab = TheTabs[i];
2469 if (i==0)
2470 pUndoDoc->InitUndo( rDoc, nTab,nTab, true,true ); // incl. column/fow flags
2471 else
2472 pUndoDoc->AddUndoTab( nTab,nTab, true,true ); // incl. column/fow flags
2473
2474 rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
2475 rDoc.GetName( nTab, aOldName );
2476 pUndoDoc->RenameTab( nTab, aOldName );
2477 if (rDoc.IsLinked(nTab))
2478 {
2479 bWasLinked = true;
2480 pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
2481 rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
2482 rDoc.GetLinkTab(nTab),
2483 rDoc.GetLinkRefreshDelay(nTab) );
2484 }
2485 if ( rDoc.IsScenario(nTab) )
2486 {
2487 pUndoDoc->SetScenario( nTab, true );
2488 OUString aComment;
2489 Color aColor;
2490 ScScenarioFlags nScenFlags;
2491 rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
2492 pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
2493 bool bActive = rDoc.IsActiveScenario( nTab );
2494 pUndoDoc->SetActiveScenario( nTab, bActive );
2495 }
2496 pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
2497 pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
2498 auto pSheetEvents = rDoc.GetSheetEvents( nTab );
2499 pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
2500 pUndoDoc->SetLayoutRTL( nTab, rDoc.IsLayoutRTL( nTab ) );
2501
2502 if ( rDoc.IsTabProtected( nTab ) )
2503 pUndoDoc->SetTabProtection(nTab, rDoc.GetTabProtection(nTab));
2504
2505 // Drawing-Layer is responsible for its Undo !!!
2506 // pUndoDoc->TransferDrawPage(rDoc, nTab,nTab);
2507 }
2508
2509 pUndoDoc->AddUndoTab( 0, nCount-1 ); // all Tabs for references
2510
2511 rDoc.BeginDrawUndo(); // DeleteTab creates a SdrUndoDelPage
2512
2513 pUndoData.reset(new ScRefUndoData( &rDoc ));
2514 }
2515
2516 bool bDelDone = false;
2517
2518 for(int i=TheTabs.size()-1; i>=0; --i)
2519 {
2520 OUString sCodeName;
2521 bool bHasCodeName = rDoc.GetCodeName( TheTabs[i], sCodeName );
2522 if (rDoc.DeleteTab(TheTabs[i]))
2523 {
2524 bDelDone = true;
2525 if( bVbaEnabled && bHasCodeName )
2526 {
2527 VBA_DeleteModule( *pDocSh, sCodeName );
2528 }
2529 pDocSh->Broadcast( ScTablesHint( SC_TAB_DELETED2, TheTabs[i] ) );
2530 }
2531 }
2532 if (bRecord)
2533 {
2534 pDocSh->GetUndoManager()->AddUndoAction(
2535 std::make_unique<ScUndoDeleteTab>( GetViewData().GetDocShell(), TheTabs,
2536 std::move(pUndoDoc), std::move(pUndoData) ));
2537 }
2538
2539 if (bDelDone)
2540 {
2541 if ( nNewTab >= rDoc.GetTableCount() )
2542 nNewTab = rDoc.GetTableCount() - 1;
2543
2544 SetTabNo( nNewTab, true );
2545
2546 if (bWasLinked)
2547 {
2548 pDocSh->UpdateLinks(); // update Link-Manager
2549 GetViewData().GetBindings().Invalidate(SID_LINKS((26000) + 60));
2550 }
2551
2552 pDocSh->PostPaintExtras();
2553 pDocSh->SetDocumentModified();
2554
2555 SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
2556 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2557 pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
2558 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2559 }
2560 return bDelDone;
2561}
2562
2563bool ScViewFunc::RenameTable( const OUString& rName, SCTAB nTab )
2564{
2565 // order Table/Name is inverted for DocFunc
2566 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
2567 RenameTable( nTab, rName, true, false );
2568 if (bSuccess)
2569 {
2570 // the table name might be part of a formula
2571 GetViewData().GetViewShell()->UpdateInputHandler();
2572 }
2573 return bSuccess;
2574}
2575
2576bool ScViewFunc::SetTabBgColor( const Color& rColor, SCTAB nTab )
2577{
2578 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( nTab, rColor, true, false );
2579 if (bSuccess)
2580 {
2581 GetViewData().GetViewShell()->UpdateInputHandler();
2582 }
2583 return bSuccess;
2584}
2585
2586bool ScViewFunc::SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList )
2587{
2588 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( rUndoSetTabBgColorInfoList, false );
2589 if (bSuccess)
2590 {
2591 GetViewData().GetViewShell()->UpdateInputHandler();
2592 }
2593 return bSuccess;
2594}
2595
2596void ScViewFunc::InsertAreaLink( const OUString& rFile,
2597 const OUString& rFilter, const OUString& rOptions,
2598 const OUString& rSource )
2599{
2600 ScDocShell* pDocSh = GetViewData().GetDocShell();
2601 SCCOL nPosX = GetViewData().GetCurX();
2602 SCROW nPosY = GetViewData().GetCurY();
2603 SCTAB nTab = GetViewData().GetTabNo();
2604 ScAddress aPos( nPosX, nPosY, nTab );
2605
2606 pDocSh->GetDocFunc().InsertAreaLink( rFile, rFilter, rOptions, rSource, aPos, 0/*nRefresh*/, false, false );
2607}
2608
2609void ScViewFunc::InsertTableLink( const OUString& rFile,
2610 const OUString& rFilter, const OUString& rOptions,
2611 const OUString& rTabName )
2612{
2613 OUString aFilterName = rFilter;
2614 OUString aOpt = rOptions;
2615 ScDocumentLoader aLoader( rFile, aFilterName, aOpt );
2616 if (aLoader.IsError())
2617 return;
2618
2619 ScDocShell* pSrcSh = aLoader.GetDocShell();
2620 ScDocument& rSrcDoc = pSrcSh->GetDocument();
2621 SCTAB nTab = MAXTAB+1;
2622 if (rTabName.isEmpty()) // no name given -> first table
2623 nTab = 0;
2624 else
2625 {
2626 OUString aTemp;
2627 SCTAB nCount = rSrcDoc.GetTableCount();
2628 for (SCTAB i=0; i<nCount; i++)
2629 {
2630 rSrcDoc.GetName( i, aTemp );
2631 if ( aTemp == rTabName )
2632 nTab = i;
2633 }
2634 }
2635
2636 if ( nTab <= MAXTAB )
2637 ImportTables( pSrcSh, 1, &nTab, true,
2638 GetViewData().GetTabNo() );
2639}
2640
2641// Copy/link tables from another document
2642
2643void ScViewFunc::ImportTables( ScDocShell* pSrcShell,
2644 SCTAB nCount, const SCTAB* pSrcTabs, bool bLink,SCTAB nTab )
2645{
2646 ScDocument& rSrcDoc = pSrcShell->GetDocument();
2647
2648 ScDocShell* pDocSh = GetViewData().GetDocShell();
2649 ScDocument& rDoc = pDocSh->GetDocument();
2650 bool bUndo(rDoc.IsUndoEnabled());
2651
2652 bool bError = false;
2653 bool bRefs = false;
2654 bool bName = false;
2655
2656 if (rSrcDoc.GetDrawLayer())
2657 pDocSh->MakeDrawLayer();
2658
2659 if (bUndo)
2660 rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
2661
2662 SCTAB nInsCount = 0;
2663 SCTAB i;
2664 for( i=0; i<nCount; i++ )
2665 { // insert sheets first and update all references
2666 OUString aName;
2667 rSrcDoc.GetName( pSrcTabs[i], aName );
2668 rDoc.CreateValidTabName( aName );
2669 if ( !rDoc.InsertTab( nTab+i, aName ) )
2670 {
2671 bError = true; // total error
2672 break; // for
2673 }
2674 ++nInsCount;
2675 }
2676 for (i=0; i<nCount && !bError; i++)
2677 {
2678 SCTAB nSrcTab = pSrcTabs[i];
2679 SCTAB nDestTab1=nTab+i;
2680 sal_uLong nErrVal = pDocSh->TransferTab( *pSrcShell, nSrcTab, nDestTab1,
2681 false, false ); // no insert
2682
2683 switch (nErrVal)
2684 {
2685 case 0: // internal error or full of errors
2686 bError = true;
2687 break;
2688 case 2:
2689 bRefs = true;
2690 break;
2691 case 3:
2692 bName = true;
2693 break;
2694 case 4:
2695 bRefs = bName = true;
2696 break;
2697 }
2698
2699 }
2700
2701 if (bLink)
2702 {
2703 sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
2704
2705 SfxMedium* pMed = pSrcShell->GetMedium();
2706 OUString aFileName = pMed->GetName();
2707 OUString aFilterName;
2708 if (pMed->GetFilter())
2709 aFilterName = pMed->GetFilter()->GetFilterName();
2710 OUString aOptions = ScDocumentLoader::GetOptions(*pMed);
2711
2712 bool bWasThere = rDoc.HasLink( aFileName, aFilterName, aOptions );
2713
2714 sal_uLong nRefresh = 0;
2715 OUString aTabStr;
2716 for (i=0; i<nInsCount; i++)
2717 {
2718 rSrcDoc.GetName( pSrcTabs[i], aTabStr );
2719 rDoc.SetLink( nTab+i, ScLinkMode::NORMAL,
2720 aFileName, aFilterName, aOptions, aTabStr, nRefresh );
2721 }
2722
2723 if (!bWasThere) // Insert link only once per source document
2724 {
2725 ScTableLink* pLink = new ScTableLink( pDocSh, aFileName, aFilterName, aOptions, nRefresh );
2726 pLink->SetInCreate( true );
2727 pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilterName );
2728 pLink->Update();
2729 pLink->SetInCreate( false );
2730
2731 SfxBindings& rBindings = GetViewData().GetBindings();
2732 rBindings.Invalidate( SID_LINKS((26000) + 60) );
2733 }
2734 }
2735
2736 if (bUndo)
2737 {
2738 pDocSh->GetUndoManager()->AddUndoAction(
2739 std::make_unique<ScUndoImportTab>( pDocSh, nTab, nCount ) );
2740 }
2741
2742 for (i=0; i<nInsCount; i++)
2743 GetViewData().InsertTab(nTab);
2744 SetTabNo(nTab,true);
2745 pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
2746 PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras );
2747
2748 SfxApplication* pSfxApp = SfxGetpApp();
2749 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2750 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
2751
2752 pDocSh->PostPaintExtras();
2753 pDocSh->PostPaintGridAll();
2754 pDocSh->SetDocumentModified();
2755
2756 if (bRefs)
2757 ErrorMessage(STR_ABSREFLOSTreinterpret_cast<char const *>("STR_ABSREFLOST" "\004" u8"The new table contains absolute references to other tables which may be incorrect!"
)
);
2758 if (bName)
2759 ErrorMessage(STR_NAMECONFLICTreinterpret_cast<char const *>("STR_NAMECONFLICT" "\004"
u8"Due to identical names, an existing range name in the destination document has been altered!"
)
);
2760}
2761
2762// Move/Copy table to another document
2763
2764void ScViewFunc::MoveTable(
2765 sal_uInt16 nDestDocNo, SCTAB nDestTab, bool bCopy, const OUString* pNewTabName )
2766{
2767 ScDocument& rDoc = GetViewData().GetDocument();
2768 ScDocShell* pDocShell = GetViewData().GetDocShell();
2769 ScDocShell* pDestShell = nullptr;
2770 ScTabViewShell* pDestViewSh = nullptr;
2771 bool bUndo (rDoc.IsUndoEnabled());
2772 bool bRename = pNewTabName && !pNewTabName->isEmpty();
2773
2774 bool bNewDoc = (nDestDocNo == SC_DOC_NEW0xFFFF);
2775 if ( bNewDoc )
2776 {
2777 nDestTab = 0; // firstly insert
2778
2779 // execute without SfxCallMode::RECORD, because already contained in move command
2780
2781 SfxStringItem aItem( SID_FILE_NAME(5000 + 507), "private:factory/" STRING_SCAPP"scalc" );
2782 SfxStringItem aTarget( SID_TARGETNAME(5000 + 560), "_blank" );
2783
2784 const SfxPoolItem* pRetItem = GetViewData().GetDispatcher().ExecuteList(
2785 SID_OPENDOC(5000 + 501), SfxCallMode::API|SfxCallMode::SYNCHRON,
2786 { &aItem, &aTarget });
2787 if ( pRetItem )
2788 {
2789 if ( auto pObjectItem = dynamic_cast<const SfxObjectItem*>(pRetItem) )
2790 pDestShell = dynamic_cast<ScDocShell*>( pObjectItem->GetShell() );
2791 else if ( auto pViewFrameItem = dynamic_cast<const SfxViewFrameItem*>( pRetItem) )
2792 {
2793 SfxViewFrame* pFrm = pViewFrameItem->GetFrame();
2794 if (pFrm)
2795 pDestShell = dynamic_cast<ScDocShell*>( pFrm->GetObjectShell() );
2796 }
2797 if (pDestShell)
2798 pDestViewSh = pDestShell->GetBestViewShell();
2799 }
2800 }
2801 else
2802 pDestShell = ScDocShell::GetShellByNum( nDestDocNo );
2803
2804 if (!pDestShell)
2805 {
2806 OSL_FAIL("Destination document not found !!!")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat
((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/ui/view/viewfun2.cxx"
":" "2806" ": "), "%s", "Destination document not found !!!"
); } } while (false)
;
2807 return;
2808 }
2809
2810 ScMarkData& rMark = GetViewData().GetMarkData();
2811 if (bRename && rMark.GetSelectCount() != 1)
2812 {
2813 // Custom sheet name is provided, but more than one sheet is selected.
2814 // We don't support this scenario at the moment.
2815 return;
2816 }
2817
2818 ScDocument& rDestDoc = pDestShell->GetDocument();
2819
2820 if (&rDestDoc != &rDoc)
2821 {
2822 if (bNewDoc)
2823 {
2824 while (rDestDoc.GetTableCount() > 1)
2825 rDestDoc.DeleteTab(0);
2826 rDestDoc.RenameTab( 0, "______42_____" );
2827 }
2828
2829 SCTAB nTabCount = rDoc.GetTableCount();
2830 SCTAB nTabSelCount = rMark.GetSelectCount();
2831
2832 vector<SCTAB> TheTabs;
2833
2834 for(SCTAB i=0; i<nTabCount; ++i)
2835 {
2836 if(rMark.GetTableSelect(i))
2837 {
2838 OUString aTabName;
2839 rDoc.GetName( i, aTabName);
2840 TheTabs.push_back(i);
2841 for(SCTAB j=i+1;j<nTabCount;j++)
2842 {
2843 if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
2844 {
2845 rDoc.GetName( j, aTabName);
2846 TheTabs.push_back(j);
2847 i=j;
2848 }
2849 else break;
2850 }
2851 }
2852 }
2853
2854 GetFrameWin()->EnterWait();
2855
2856 if (rDoc.GetDrawLayer())
2857 pDestShell->MakeDrawLayer();
2858
2859 if (!bNewDoc && bUndo)
2860 rDestDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
2861
2862 sal_uLong nErrVal =1;
2863 if(nDestTab==SC_TAB_APPEND)
2864 nDestTab=rDestDoc.GetTableCount();
2865 SCTAB nDestTab1=nDestTab;
2866 ScClipParam aParam;
2867 for( size_t j=0; j<TheTabs.size(); ++j, ++nDestTab1 )
2868 { // insert sheets first and update all references
2869 OUString aName;
2870 if (bRename)
2871 aName = *pNewTabName;
2872 else
2873 rDoc.GetName( TheTabs[j], aName );
2874
2875 rDestDoc.CreateValidTabName( aName );
2876 if ( !rDestDoc.InsertTab( nDestTab1, aName ) )
2877 {
2878 nErrVal = 0; // total error
2879 break; // for
2880 }
2881 ScRange aRange( 0, 0, TheTabs[j], rDoc.MaxCol(), rDoc.MaxRow(), TheTabs[j] );
2882 aParam.maRanges.push_back(aRange);
2883 }
2884 rDoc.SetClipParam(aParam);
2885 if ( nErrVal > 0 )
2886 {
2887 nDestTab1 = nDestTab;
2888 for(SCTAB nTab : TheTabs)
2889 {
2890 nErrVal = pDestShell->TransferTab( *pDocShell, nTab, nDestTab1, false, false );
2891 nDestTab1++;
2892 }
2893 }
2894 if (!bNewDoc && bUndo)
2895 {
2896 OUString sName;
2897 rDestDoc.GetName(nDestTab, sName);
2898 pDestShell->GetUndoManager()->AddUndoAction(
2899 std::make_unique<ScUndoImportTab>( pDestShell, nDestTab,
2900 static_cast<SCTAB>(TheTabs.size())));
2901
2902 }
2903 else
2904 {
2905 pDestShell->GetUndoManager()->Clear();
2906 }
2907
2908 GetFrameWin()->LeaveWait();
2909 switch (nErrVal)
2910 {
2911 case 0: // internal error or full of errors
2912 {
2913 ErrorMessage(STR_TABINSERT_ERRORreinterpret_cast<char const *>("STR_TABINSERT_ERROR" "\004"
u8"The table could not be inserted.")
);
2914 return;
2915 }
2916 case 2:
2917 ErrorMessage(STR_ABSREFLOSTreinterpret_cast<char const *>("STR_ABSREFLOST" "\004" u8"The new table contains absolute references to other tables which may be incorrect!"
)
);
2918 break;
2919 case 3:
2920 ErrorMessage(STR_NAMECONFLICTreinterpret_cast<char const *>("STR_NAMECONFLICT" "\004"
u8"Due to identical names, an existing range name in the destination document has been altered!"
)
);
2921 break;
2922 case 4:
2923 {
2924 ErrorMessage(STR_ABSREFLOSTreinterpret_cast<char const *>("STR_ABSREFLOST" "\004" u8"The new table contains absolute references to other tables which may be incorrect!"
)
);
2925 ErrorMessage(STR_NAMECONFLICTreinterpret_cast<char const *>("STR_NAMECONFLICT" "\004"
u8"Due to identical names, an existing range name in the destination document has been altered!"
)
);
2926 }
2927 break;
2928 default:
2929 break;
2930 }
2931
2932 if (!bCopy)
2933 {
2934 if(nTabCount!=nTabSelCount)
2935 DeleteTables(TheTabs); // incl. Paint & Undo
2936 else
2937 ErrorMessage(STR_TABREMOVE_ERRORreinterpret_cast<char const *>("STR_TABREMOVE_ERROR" "\004"
u8"The sheets could not be deleted.")
);
2938 }
2939
2940 if (bNewDoc)
2941 {
2942 // ChartListenerCollection must be updated before DeleteTab
2943 if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
2944 rDestDoc.UpdateChartListenerCollection();
2945
2946 SCTAB nNumTabsInserted = static_cast<SCTAB>(TheTabs.size());
2947 pDestShell->Broadcast( ScTablesHint( SC_TABS_INSERTED6, 0, nNumTabsInserted ) );
2948
2949 rDestDoc.DeleteTab( nNumTabsInserted ); // old first table
2950 pDestShell->Broadcast( ScTablesHint( SC_TAB_DELETED2, nNumTabsInserted ) );
2951
2952 if (pDestViewSh)
2953 {
2954 // Make sure to clear the cached page view after sheet
2955 // deletion, which still points to the sdr page belonging to
2956 // the deleted sheet.
2957 SdrView* pSdrView = pDestViewSh->GetScDrawView();
2958 if (pSdrView)
2959 pSdrView->ClearPageView();
2960
2961 pDestViewSh->TabChanged(); // pages on the drawing layer
2962 }
2963 pDestShell->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
2964 PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left |
2965 PaintPartFlags::Extras | PaintPartFlags::Size );
2966 // PaintPartFlags::Size for outline
2967 }
2968 else
2969 {
2970 pDestShell->Broadcast( ScTablesHint( SC_TAB_INSERTED1, nDestTab ) );
2971 pDestShell->PostPaintExtras();
2972 pDestShell->PostPaintGridAll();
2973 }
2974
2975 TheTabs.clear();
2976
2977 pDestShell->SetDocumentModified();
2978 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2979 }
2980 else
2981 {
2982 // Move or copy within the same document.
2983 SCTAB nTabCount = rDoc.GetTableCount();
2984
2985 unique_ptr< vector<SCTAB> > pSrcTabs(new vector<SCTAB>);
2986 unique_ptr< vector<SCTAB> > pDestTabs(new vector<SCTAB>);
2987 unique_ptr< vector<OUString> > pTabNames(new vector<OUString>);
2988 unique_ptr< vector<OUString> > pDestNames;
2989 pSrcTabs->reserve(nTabCount);
2990 pDestTabs->reserve(nTabCount);
2991 pTabNames->reserve(nTabCount);
2992 OUString aDestName;
2993
2994 for(SCTAB i=0;i<nTabCount;i++)
2995 {
2996 if(rMark.GetTableSelect(i))
2997 {
2998 OUString aTabName;
2999 rDoc.GetName( i, aTabName);
3000 pTabNames->push_back(aTabName);
3001
3002 for(SCTAB j=i+1;j<nTabCount;j++)
3003 {
3004 if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
3005 {
3006 rDoc.GetName( j, aTabName);
3007 pTabNames->push_back(aTabName);
3008 i=j;
3009 }
3010 else break;
3011 }
3012 }
3013 }
3014
3015 if (bCopy && bUndo)
3016 rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
3017
3018 rDoc.GetName( nDestTab, aDestName);
3019 SCTAB nDestTab1=nDestTab;
3020 SCTAB nMovTab=0;
3021 for (size_t j = 0, n = pTabNames->size(); j < n; ++j)
3022 {
3023 nTabCount = rDoc.GetTableCount();
3024 const OUString& rStr = (*pTabNames)[j];
3025 if(!rDoc.GetTable(rStr,nMovTab))
3026 {
3027 nMovTab=nTabCount;
3028 }
3029 if(!rDoc.GetTable(aDestName,nDestTab1))
3030 {
3031 nDestTab1=nTabCount;
3032 }
3033 pDocShell->MoveTable( nMovTab, nDestTab1, bCopy, false ); // Undo is here
3034
3035 if(bCopy && rDoc.IsScenario(nMovTab))
3036 {
3037 OUString aComment;
3038 Color aColor;
3039 ScScenarioFlags nFlags;
3040
3041 rDoc.GetScenarioData(nMovTab, aComment,aColor, nFlags);
3042 rDoc.SetScenario(nDestTab1,true);
3043 rDoc.SetScenarioData(nDestTab1,aComment,aColor,nFlags);
3044 bool bActive = rDoc.IsActiveScenario(nMovTab );
3045 rDoc.SetActiveScenario( nDestTab1, bActive );
3046 bool bVisible=rDoc.IsVisible(nMovTab);
3047 rDoc.SetVisible(nDestTab1,bVisible );
3048 }
3049
3050 pSrcTabs->push_back(nMovTab);
3051
3052 if(!bCopy)
3053 {
3054 if(!rDoc.GetTable(rStr,nDestTab1))
3055 {
3056 nDestTab1=nTabCount;
3057 }
3058 }
3059
3060 pDestTabs->push_back(nDestTab1);
3061 }
3062
3063 // Rename must be done after all sheets have been moved.
3064 if (bRename)
3065 {
3066 pDestNames.reset(new vector<OUString>);
3067 size_t n = pDestTabs->size();
3068 pDestNames->reserve(n);
3069 for (size_t j = 0; j < n; ++j)
3070 {
3071 SCTAB nRenameTab = (*pDestTabs)[j];
3072 OUString aTabName = *pNewTabName;
3073 rDoc.CreateValidTabName( aTabName );
3074 pDestNames->push_back(aTabName);
3075 rDoc.RenameTab(nRenameTab, aTabName);
3076 }
3077 }
3078 else
3079 // No need to keep this around when we are not renaming.
3080 pTabNames.reset();
3081
3082 SCTAB nTab = GetViewData().GetTabNo();
3083
3084 if (bUndo)
3085 {
3086 if (bCopy)
3087 {
3088 pDocShell->GetUndoManager()->AddUndoAction(
3089 std::make_unique<ScUndoCopyTab>(
3090 pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pDestNames)));
3091 }
3092 else
3093 {
3094 pDocShell->GetUndoManager()->AddUndoAction(
3095 std::make_unique<ScUndoMoveTab>(
3096 pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pTabNames), std::move(pDestNames)));
3097 }
3098 }
3099
3100 SCTAB nNewTab = nDestTab;
3101 if (nNewTab == SC_TAB_APPEND)
3102 nNewTab = rDoc.GetTableCount()-1;
3103 else if (!bCopy && nTab<nDestTab)
3104 nNewTab--;
3105
3106 SetTabNo( nNewTab, true );
3107
3108 //#i29848# adjust references to data on the copied sheet
3109 if( bCopy )
3110 ScChartHelper::AdjustRangesOfChartsOnDestinationPage( rDoc, rDestDoc, nTab, nNewTab );
3111 }
3112}
3113
3114void ScViewFunc::ShowTable( const std::vector<OUString>& rNames )
3115{
3116 ScDocShell* pDocSh = GetViewData().GetDocShell();
3117 ScDocument& rDoc = pDocSh->GetDocument();
3118 bool bUndo(rDoc.IsUndoEnabled());
3119
3120 std::vector<SCTAB> undoTabs;
3121 SCTAB nPos = 0;
3122
3123 bool bFound(false);
3124
3125 for (const OUString& aName : rNames)
3126 {
3127 if (rDoc.GetTable(aName, nPos))
3128 {
3129 rDoc.SetVisible( nPos, true );
3130 SetTabNo( nPos, true );
3131 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3132 if (!bFound)
3133 bFound = true;
3134 if (bUndo)
3135 undoTabs.push_back(nPos);
3136 }
3137 }
3138 if (bFound)
3139 {
3140 if (bUndo)
3141 {
3142 pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, undoTabs, true ) );
3143 }
3144 pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
3145 pDocSh->SetDocumentModified();
3146 }
3147}
3148
3149void ScViewFunc::HideTable( const ScMarkData& rMark )
3150{
3151 ScDocShell* pDocSh = GetViewData().GetDocShell();
3152 ScDocument& rDoc = pDocSh->GetDocument();
3153 bool bUndo(rDoc.IsUndoEnabled());
3154 SCTAB nVisible = 0;
3155 SCTAB nTabCount = rDoc.GetTableCount();
3156
3157 SCTAB nTabSelCount = rMark.GetSelectCount();
3158
3159 // check to make sure we won't hide all sheets. we need at least one visible at all times.
3160 for ( SCTAB i=0; i < nTabCount && nVisible <= nTabSelCount ; i++ )
3161 if (rDoc.IsVisible(i))
3162 ++nVisible;
3163
3164 if (nVisible <= nTabSelCount)
3165 return;
3166
3167 std::vector<SCTAB> undoTabs;
3168
3169 // need to take a copy of selectedtabs since it is modified in the loop
3170 const ScMarkData::MarkedTabsType selectedTabs = rMark.GetSelectedTabs();
3171 for (const SCTAB& nTab : selectedTabs)
3172 {
3173 if (rDoc.IsVisible( nTab ))
3174 {
3175 rDoc.SetVisible( nTab, false );
3176 // Update views
3177 pDocSh->Broadcast( ScTablesHint( SC_TAB_HIDDEN5, nTab ) );
3178 SetTabNo( nTab, true );
3179 // Store for undo
3180 if (bUndo)
3181 undoTabs.push_back(nTab);
3182 }
3183 }
3184 if (bUndo)
3185 {
3186 pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, undoTabs, false ) );
3187 }
3188
3189 // Update views
3190 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3191 pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
3192 pDocSh->SetDocumentModified();
3193}
3194
3195void ScViewFunc::InsertSpecialChar( const OUString& rStr, const vcl::Font& rFont )
3196{
3197 ScEditableTester aTester( this );
3198 if (!aTester.IsEditable())
3199 {
3200 ErrorMessage(aTester.GetMessageId());
3201 return;
3202 }
3203
3204 const sal_Unicode* pChar = rStr.getStr();
3205 ScTabViewShell* pViewShell = GetViewData().GetViewShell();
3206 SvxFontItem aFontItem( rFont.GetFamilyType(),
3207 rFont.GetFamilyName(),
3208 rFont.GetStyleName(),
3209 rFont.GetPitch(),
3210 rFont.GetCharSet(),
3211 ATTR_FONT );
3212
3213 // if string contains WEAK characters, set all fonts
3214 SvtScriptType nScript;
3215 ScDocument& rDoc = GetViewData().GetDocument();
3216 if ( rDoc.HasStringWeakCharacters( rStr ) )
3217 nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
3218 else
3219 nScript = rDoc.GetStringScriptType( rStr );
3220
3221 SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT( 10000 + 7 ), pViewShell->GetPool() );
3222 aSetItem.PutItemForScriptType( nScript, aFontItem );
3223 ApplyUserItemSet( aSetItem.GetItemSet() );
3224
3225 while ( *pChar )
3226 pViewShell->TabKeyInput( KeyEvent( *(pChar++), vcl::KeyCode() ) );
3227}
3228
3229void ScViewFunc::UpdateLineAttrs( SvxBorderLine& rLine,
3230 const SvxBorderLine* pDestLine,
3231 const SvxBorderLine* pSrcLine,
3232 bool bColor )
3233{
3234 if ( !(pSrcLine && pDestLine) )
3235 return;
3236
3237 if ( bColor )
3238 {
3239 rLine.SetColor ( pSrcLine->GetColor() );
3240 rLine.SetBorderLineStyle(pDestLine->GetBorderLineStyle());
3241 rLine.SetWidth ( pDestLine->GetWidth() );
3242 }
3243 else
3244 {
3245 rLine.SetColor ( pDestLine->GetColor() );
3246 rLine.SetBorderLineStyle(pSrcLine->GetBorderLineStyle());
3247 rLine.SetWidth ( pSrcLine->GetWidth() );
3248 }
3249}
3250
3251#define SET_LINE_ATTRIBUTES(LINE,BOXLINE) \
3252 pBoxLine = aBoxItem.Get##LINE(); \
3253 if ( pBoxLine ) \
3254 { \
3255 if ( pLine ) \
3256 { \
3257 UpdateLineAttrs( aLine, pBoxLine, pLine, bColorOnly ); \
3258 aBoxItem.SetLine( &aLine, BOXLINE ); \
3259 } \
3260 else \
3261 aBoxItem.SetLine( nullptr, BOXLINE ); \
3262 }
3263
3264void ScViewFunc::SetSelectionFrameLines( const SvxBorderLine* pLine,
3265 bool bColorOnly )
3266{
3267 // Not editable only due to a matrix? Attribute is ok anyhow.
3268 bool bOnlyNotBecauseOfMatrix;
3269 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
3270 {
3271 ErrorMessage(STR_PROTECTIONERRreinterpret_cast<char const *>("STR_PROTECTIONERR" "\004"
u8"Protected cells can not be modified.")
);
3272 return;
3273 }
3274
3275 ScDocument& rDoc = GetViewData().GetDocument();
3276 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
3277 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
3278 ScDocShell* pDocSh = GetViewData().GetDocShell();
3279 const ScPatternAttr* pSelAttrs = GetSelectionPattern();
3280 const SfxItemSet& rSelItemSet = pSelAttrs->GetItemSet();
3281
3282 const SfxPoolItem* pBorderAttr = nullptr;
3283 SfxItemState eItemState = rSelItemSet.GetItemState( ATTR_BORDER, true, &pBorderAttr );
3284
3285 const SfxPoolItem* pTLBRItem = nullptr;
3286 SfxItemState eTLBRState = rSelItemSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem );
3287
3288 const SfxPoolItem* pBLTRItem = nullptr;
3289 SfxItemState eBLTRState = rSelItemSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem );
3290
3291 // any of the lines visible?
3292 if( !((eItemState != SfxItemState::DEFAULT) || (eTLBRState != SfxItemState::DEFAULT) || (eBLTRState != SfxItemState::DEFAULT)) )
3293 return;
3294
3295 // none of the lines don't care?
3296 if( (eItemState != SfxItemState::DONTCARE) && (eTLBRState != SfxItemState::DONTCARE) && (eBLTRState != SfxItemState::DONTCARE) )
3297 {
3298 std::unique_ptr<SfxItemSet> pOldSet(new SfxItemSet(
3299 *rDoc.GetPool(),
3300 svl::Items<ATTR_PATTERN_START,
3301 ATTR_PATTERN_END>{} ));
3302 std::unique_ptr<SfxItemSet> pNewSet(new SfxItemSet(
3303 *rDoc.GetPool(),
3304 svl::Items<ATTR_PATTERN_START,
3305 ATTR_PATTERN_END>{} ));
3306
3307 SvxBorderLine aLine;
3308
3309 if( pBorderAttr )
3310 {
3311 const SvxBorderLine* pBoxLine = nullptr;
3312 SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem*>(pBorderAttr) );
3313 SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
3314
3315 // here pBoxLine is used
3316 SET_LINE_ATTRIBUTES(Top,SvxBoxItemLine::TOP)
3317 SET_LINE_ATTRIBUTES(Bottom,SvxBoxItemLine::BOTTOM)
3318 SET_LINE_ATTRIBUTES(Left,SvxBoxItemLine::LEFT)
3319 SET_LINE_ATTRIBUTES(Right,SvxBoxItemLine::RIGHT)
3320
3321 aBoxInfoItem.SetLine( aBoxItem.GetTop(), SvxBoxInfoItemLine::HORI );
3322 aBoxInfoItem.SetLine( aBoxItem.GetLeft(), SvxBoxInfoItemLine::VERT );
3323 aBoxInfoItem.ResetFlags(); // set Lines to Valid
3324
3325 pOldSet->Put( *pBorderAttr );
3326 pNewSet->Put( aBoxItem );
3327 pNewSet->Put( aBoxInfoItem );
3328 }
3329
3330 if( pTLBRItem && static_cast<const SvxLineItem*>(pTLBRItem)->GetLine() )
3331 {
3332 SvxLineItem aTLBRItem( *static_cast<const SvxLineItem*>(pTLBRItem) );
3333 UpdateLineAttrs( aLine, aTLBRItem.GetLine(), pLine, bColorOnly );
3334 aTLBRItem.SetLine( &aLine );
3335 pOldSet->Put( *pTLBRItem );
3336 pNewSet->Put( aTLBRItem );
3337 }
3338
3339 if( pBLTRItem && static_cast<const SvxLineItem*>(pBLTRItem)->GetLine() )
3340 {
3341 SvxLineItem aBLTRItem( *static_cast<const SvxLineItem*>(pBLTRItem) );
3342 UpdateLineAttrs( aLine, aBLTRItem.GetLine(), pLine, bColorOnly );
3343 aBLTRItem.SetLine( &aLine );
3344 pOldSet->Put( *pBLTRItem );
3345 pNewSet->Put( aBLTRItem );
3346 }
3347
3348 ApplyAttributes( pNewSet.get(), pOldSet.get() );
3349 }
3350 else // if ( eItemState == SfxItemState::DONTCARE )
3351 {
3352 aFuncMark.MarkToMulti();
3353 rDoc.ApplySelectionLineStyle( aFuncMark, pLine, bColorOnly );
3354 }
3355
3356 ScRange aMarkRange;
3357 aFuncMark.GetMultiMarkArea( aMarkRange );
3358 SCCOL nStartCol = aMarkRange.aStart.Col();
3359 SCROW nStartRow = aMarkRange.aStart.Row();
3360 SCTAB nStartTab = aMarkRange.aStart.Tab();
3361 SCCOL nEndCol = aMarkRange.aEnd.Col();
3362 SCROW nEndRow = aMarkRange.aEnd.Row();
3363 SCTAB nEndTab = aMarkRange.aEnd.Tab();
3364 pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
3365 nEndCol, nEndRow, nEndTab,
3366 PaintPartFlags::Grid, SC_PF_LINES1 | SC_PF_TESTMERGE2 );
3367
3368 pDocSh->UpdateOle(GetViewData());
3369 pDocSh->SetDocumentModified();
3370}
3371
3372#undef SET_LINE_ATTRIBUTES
3373
3374void ScViewFunc::SetValidation( const ScValidationData& rNew )
3375{
3376 ScDocument& rDoc = GetViewData().GetDocument();
3377 sal_uLong nIndex = rDoc.AddValidationEntry(rNew); // for it there is no Undo
3378 SfxUInt32Item aItem( ATTR_VALIDDATA, nIndex );
3379
3380 ApplyAttr( aItem ); // with Paint and Undo...
3381}
3382
3383/* vim:set shiftwidth=4 softtabstop=4 expandtab: */