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