tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / viewfun2.cxx
blob1e8a19021c0840dfe9d1195cda5d2ba32a60dbbb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <scitems.hxx>
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/scripttypeitem.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>
45 #include <viewfunc.hxx>
46 #include <vcl/uitest/logger.hxx>
47 #include <vcl/uitest/eventdescription.hxx>
49 #include <sc.hrc>
50 #include <globstr.hrc>
51 #include <scresid.hxx>
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>
87 #include <vector>
88 #include <memory>
89 #include <boost/property_tree/json_parser.hpp>
90 #include <tools/json_writer.hxx>
92 #include <officecfg/Office/Calc.hxx>
93 #include <sfx2/lokhelper.hxx>
95 using namespace com::sun::star;
96 using ::editeng::SvxBorderLine;
98 namespace {
100 void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
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";
109 UITestLogger::getInstance().logEvent(aDescription);
113 using ::std::vector;
114 using ::std::unique_ptr;
116 bool ScViewFunc::AdjustBlockHeight( bool bPaint, ScMarkData* pMarkData )
118 ScDocShell* pDocSh = GetViewData().GetDocShell();
119 if (!pMarkData)
120 pMarkData = &GetViewData().GetMarkData();
122 ScDocument& rDoc = pDocSh->GetDocument();
123 std::vector<sc::ColRowSpan> aMarkedRows = pMarkData->GetMarkedRowSpans();
125 if (aMarkedRows.empty())
127 SCROW nCurRow = GetViewData().GetCurY();
128 aMarkedRows.emplace_back(nCurRow, nCurRow);
131 if (comphelper::LibreOfficeKit::isActive())
133 SCCOLROW nStart = aMarkedRows[0].mnStart;
134 OnLOKSetWidthOrHeight(nStart, /*width: */ false);
137 double nPPTX = GetViewData().GetPPTX();
138 double nPPTY = GetViewData().GetPPTY();
139 Fraction aZoomX = GetViewData().GetZoomX();
140 Fraction aZoomY = GetViewData().GetZoomY();
142 ScSizeDeviceProvider aProv(pDocSh);
143 if (aProv.IsPrinter())
145 nPPTX = aProv.GetPPTX();
146 nPPTY = aProv.GetPPTY();
147 aZoomX = aZoomY = Fraction( 1, 1 );
150 sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
151 bool bAnyChanged = false;
152 for (const SCTAB& nTab : *pMarkData)
154 bool bChanged = false;
155 SCROW nPaintY = 0;
156 for (const auto& rRow : aMarkedRows)
158 SCROW nStartNo = rRow.mnStart;
159 SCROW nEndNo = rRow.mnEnd;
160 ScAddress aTopLeft(0, nStartNo, nTab);
161 rDoc.UpdateScriptTypes(aTopLeft, rDoc.GetSheetLimits().GetMaxColCount(), nEndNo-nStartNo+1);
162 if (rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true))
164 if (!bChanged)
165 nPaintY = nStartNo;
166 bAnyChanged = bChanged = true;
169 // tdf#76183: recalculate objects' positions
170 if (bChanged)
171 rDoc.SetDrawPageSize(nTab);
172 if ( bPaint && bChanged )
173 pDocSh->PostPaint( 0, nPaintY, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
174 PaintPartFlags::Grid | PaintPartFlags::Left );
177 if ( bPaint && bAnyChanged )
178 pDocSh->UpdateOle(GetViewData());
180 if (comphelper::LibreOfficeKit::isActive())
182 SCTAB nTab = GetViewData().GetTabNo();
183 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
184 GetViewData().GetViewShell(),
185 false /* bColumns */, true /* bRows */,
186 true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
187 false /* bGroups */, nTab);
188 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, nTab);
191 return bAnyChanged;
194 bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, bool bApi )
196 if (comphelper::LibreOfficeKit::isActive())
198 OnLOKSetWidthOrHeight(nStartRow, /*width: */ false);
201 ScDocShell* pDocSh = GetViewData().GetDocShell();
202 ScDocument& rDoc = pDocSh->GetDocument();
203 SCTAB nTab = GetViewData().GetTabNo();
204 double nPPTX = GetViewData().GetPPTX();
205 double nPPTY = GetViewData().GetPPTY();
206 Fraction aZoomX = GetViewData().GetZoomX();
207 Fraction aZoomY = GetViewData().GetZoomY();
208 sal_uInt16 nOldPixel = 0;
209 if (nStartRow == nEndRow)
210 nOldPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
212 ScSizeDeviceProvider aProv(pDocSh);
213 if (aProv.IsPrinter())
215 nPPTX = aProv.GetPPTX();
216 nPPTY = aProv.GetPPTY();
217 aZoomX = aZoomY = Fraction( 1, 1 );
219 sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
220 bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
222 // tdf#76183: recalculate objects' positions
223 if (bChanged)
224 rDoc.SetDrawPageSize(nTab);
226 if (bChanged && ( nStartRow == nEndRow ))
228 sal_uInt16 nNewPixel = static_cast<sal_uInt16>(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
229 if ( nNewPixel == nOldPixel )
230 bChanged = false;
233 if ( bChanged )
234 pDocSh->PostPaint( 0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab,
235 PaintPartFlags::Grid | PaintPartFlags::Left );
237 if (comphelper::LibreOfficeKit::isActive())
239 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
240 GetViewData().GetViewShell(),
241 false /* bColumns */, true /* bRows */,
242 true /* bSizes*/, false /* bHidden */, false /* bFiltered */,
243 false /* bGroups */, nTab);
244 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
247 return bChanged;
250 namespace {
252 enum ScAutoSum
254 ScAutoSumNone = 0,
255 ScAutoSumData,
256 ScAutoSumSum,
257 ScAutoSumAverage,
258 ScAutoSumMax,
259 ScAutoSumMin,
260 ScAutoSumCount,
261 ScAutoSumCountA,
262 ScAutoSumProduct,
263 ScAutoSumStDev,
264 ScAutoSumStDevP,
265 ScAutoSumVar,
266 ScAutoSumVarP,
267 ScAutoSumEnd
272 static ScAutoSum lcl_IsAutoSumData( ScDocument& rDoc, SCCOL nCol, SCROW nRow,
273 SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
275 ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab));
276 if (aCell.hasNumeric())
278 if (aCell.getType() == CELLTYPE_FORMULA)
280 ScAutoSum val = ScAutoSumNone;
281 ScTokenArray* pCode = aCell.getFormula()->GetCode();
282 if ( pCode )
284 switch( pCode->GetOuterFuncOpCode() )
286 case ocSum : val = ScAutoSumSum;
287 break;
288 case ocAverage : val = ScAutoSumAverage;
289 break;
290 case ocMax : val = ScAutoSumMax;
291 break;
292 case ocMin : val = ScAutoSumMin;
293 break;
294 case ocCount : val = ScAutoSumCount;
295 break;
296 case ocCount2 : val = ScAutoSumCountA;
297 break;
298 case ocProduct : val = ScAutoSumProduct;
299 break;
300 case ocStDev : val = ScAutoSumStDev;
301 break;
302 case ocStDevP : val = ScAutoSumStDevP;
303 break;
304 case ocVar : val = ScAutoSumVar;
305 break;
306 case ocVarP : val = ScAutoSumVarP;
307 break;
308 default :
309 break;
311 if ( pCode->GetAdjacentExtendOfOuterFuncRefs( nExtend,
312 ScAddress( nCol, nRow, nTab ), eDir ) )
313 return val;
316 return ScAutoSumData;
318 return ScAutoSumNone;
321 #define SC_AUTOSUM_MAXCOUNT 20
323 static ScAutoSum lcl_SeekAutoSumData( ScDocument& rDoc, SCCOL& nCol, SCROW& nRow,
324 SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend )
326 sal_uInt16 nCount = 0;
327 while (nCount < SC_AUTOSUM_MAXCOUNT)
329 if ( eDir == DIR_TOP )
331 if (nRow > 0)
332 --nRow;
333 else
334 return ScAutoSumNone;
336 else
338 if (nCol > 0)
339 --nCol;
340 else
341 return ScAutoSumNone;
343 ScAutoSum eSum;
344 if ( (eSum = lcl_IsAutoSumData(
345 rDoc, nCol, nRow, nTab, eDir, nExtend )) != ScAutoSumNone )
346 return eSum;
347 ++nCount;
349 return ScAutoSumNone;
352 #undef SC_AUTOSUM_MAXCOUNT
354 static bool lcl_FindNextSumEntryInColumn( ScDocument& rDoc, SCCOL nCol, SCROW& nRow,
355 SCTAB nTab, SCCOLROW& nExtend, SCROW nMinRow )
357 const SCROW nTmp = nRow;
358 ScAutoSum eSkip = ScAutoSumNone;
359 for (;;)
361 eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend );
362 if (eSkip != ScAutoSumData || nRow <= nMinRow )
363 break;
364 --nRow;
366 return eSkip >= ScAutoSumSum && nRow < nTmp;
369 static bool lcl_FindNextSumEntryInRow( ScDocument& rDoc, SCCOL& nCol, SCROW nRow,
370 SCTAB nTab, SCCOLROW& nExtend, SCCOL nMinCol )
372 const SCCOL nTmp = nCol;
373 ScAutoSum eSkip = ScAutoSumNone;
374 for (;;)
376 eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend );
377 if (eSkip != ScAutoSumData || nCol <= nMinCol )
378 break;
379 --nCol;
381 return eSkip >= ScAutoSumSum && nCol < nTmp;
384 static ScAutoSum lcl_GetAutoSumForColumnRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
386 const ScAddress aStart = rRange.aStart;
387 const ScAddress aEnd = rRange.aEnd;
388 if ( aStart.Col() != aEnd.Col() )
390 return ScAutoSumNone;
393 const SCTAB nTab = aEnd.Tab();
394 const SCCOL nCol = aEnd.Col();
395 SCROW nEndRow = aEnd.Row();
396 SCROW nStartRow = nEndRow;
397 SCCOLROW nExtend = 0;
398 ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nCol, nEndRow, nTab, DIR_TOP, nExtend /*out*/ );
400 if ( eSum >= ScAutoSumSum )
402 bool bContinue = false;
405 rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
406 nEndRow = static_cast< SCROW >( nExtend );
407 bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, aStart.Row() );
408 if ( bContinue )
410 nStartRow = nEndRow;
412 } while ( bContinue );
414 else
416 while ( nStartRow > aStart.Row() )
418 eSum = lcl_IsAutoSumData( rDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ );
419 if (eSum >= ScAutoSumSum )
420 break;
421 --nStartRow;
423 rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) );
424 if (eSum == ScAutoSumNone)
425 eSum = ScAutoSumData;
428 return eSum;
431 static ScAutoSum lcl_GetAutoSumForRowRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange )
433 const ScAddress aStart = rRange.aStart;
434 const ScAddress aEnd = rRange.aEnd;
435 if ( aStart.Row() != aEnd.Row() )
437 return ScAutoSumNone;
440 const SCTAB nTab = aEnd.Tab();
441 const SCROW nRow = aEnd.Row();
442 SCCOL nEndCol = aEnd.Col();
443 SCCOL nStartCol = nEndCol;
444 SCCOLROW nExtend = 0;
445 ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nEndCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
447 if ( eSum >= ScAutoSumSum )
449 bool bContinue = false;
452 rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
453 nEndCol = static_cast< SCCOL >( nExtend );
454 bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, aStart.Col() );
455 if ( bContinue )
457 nStartCol = nEndCol;
459 } while ( bContinue );
461 else
463 while ( nStartCol > aStart.Col() )
465 eSum = lcl_IsAutoSumData( rDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ );
466 if (eSum >= ScAutoSumSum )
467 break;
468 --nStartCol;
470 rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) );
471 if (eSum == ScAutoSumNone)
472 eSum = ScAutoSumData;
475 return eSum;
478 static sal_Int8 GetSubTotal( const OpCode eCode )
480 sal_Int8 val;
481 switch ( eCode )
483 case ocSum : val = 9;
484 break;
485 case ocAverage : val = 1;
486 break;
487 case ocMax : val = 4;
488 break;
489 case ocMin : val = 5;
490 break;
491 case ocCount : val = 2;
492 break;
493 case ocCount2 : val = 3;
494 break;
495 case ocProduct : val = 6;
496 break;
497 case ocStDev : val = 7;
498 break;
499 case ocStDevP : val = 8;
500 break;
501 case ocVar : val = 10;
502 break;
503 case ocVarP : val = 11;
504 break;
505 default : val = 9;
508 return val;
511 bool ScViewFunc::GetAutoSumArea( ScRangeList& rRangeList )
513 ScDocument& rDoc = GetViewData().GetDocument();
514 SCTAB nTab = GetViewData().GetTabNo();
516 SCCOL nCol = GetViewData().GetCurX();
517 SCROW nRow = GetViewData().GetCurY();
519 SCCOL nStartCol = nCol;
520 SCROW nStartRow = nRow;
521 SCCOL nEndCol = nCol;
522 SCROW nEndRow = nRow;
523 SCCOL nSeekCol = nCol;
524 SCROW nSeekRow = nRow;
525 SCCOLROW nExtend; // will become valid via reference for ScAutoSumSum
527 bool bCol = false;
528 bool bRow = false;
530 ScAutoSum eSum;
531 if ( nRow != 0
532 && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
533 DIR_TOP, nExtend /*out*/ )) == ScAutoSumData )
534 && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab,
535 DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
538 bRow = true;
539 nSeekRow = nRow - 1;
541 else if ( nCol != 0 && (eSum = lcl_IsAutoSumData( rDoc, nCol-1, nRow, nTab,
542 DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData )
544 bCol = true;
545 nSeekCol = nCol - 1;
547 else if ( (eSum = lcl_SeekAutoSumData( rDoc, nCol, nSeekRow, nTab, DIR_TOP, nExtend /*out*/ )) != ScAutoSumNone )
548 bRow = true;
549 else if (( eSum = lcl_SeekAutoSumData( rDoc, nSeekCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) != ScAutoSumNone )
550 bCol = true;
552 if ( bCol || bRow )
554 if ( bRow )
556 nStartRow = nSeekRow; // nSeekRow might be adjusted via reference
557 if ( eSum >= ScAutoSumSum && eSum < ScAutoSumEnd )
558 nEndRow = nStartRow; // only sum sums
559 else
560 nEndRow = nRow - 1; // maybe extend data area at bottom
562 else
564 nStartCol = nSeekCol; // nSeekCol might be adjusted via reference
565 if ( eSum >= ScAutoSumSum )
566 nEndCol = nStartCol; // only sum sums
567 else
568 nEndCol = nCol - 1; // maybe extend data area to the right
570 bool bContinue = false;
573 if ( eSum == ScAutoSumData )
575 if ( bRow )
577 while ( nStartRow != 0 && lcl_IsAutoSumData( rDoc, nCol,
578 nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) == eSum )
579 --nStartRow;
581 else
583 while ( nStartCol != 0 && lcl_IsAutoSumData( rDoc, nStartCol-1,
584 nRow, nTab, DIR_LEFT, nExtend /*out*/ ) == eSum )
585 --nStartCol;
588 rRangeList.push_back(
589 ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ) );
590 if ( eSum >= ScAutoSumSum )
592 if ( bRow )
594 nEndRow = static_cast< SCROW >( nExtend );
595 bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, 0 );
596 if ( bContinue )
598 nStartRow = nEndRow;
601 else
603 nEndCol = static_cast< SCCOL >( nExtend );
604 bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, 0 );
605 if ( bContinue )
607 nStartCol = nEndCol;
611 } while ( bContinue );
612 return true;
614 return false;
617 void ScViewFunc::EnterAutoSum(const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode)
619 OUString aFormula = GetAutoSumFormula( rRangeList, bSubTotal, rAddr , eCode);
620 EnterBlock( aFormula, nullptr );
623 bool ScViewFunc::AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue , const OpCode eCode)
625 ScDocument& rDoc = GetViewData().GetDocument();
626 const SCTAB nTab = rRange.aStart.Tab();
627 SCCOL nStartCol = rRange.aStart.Col();
628 SCROW nStartRow = rRange.aStart.Row();
629 const SCCOL nEndCol = rRange.aEnd.Col();
630 const SCROW nEndRow = rRange.aEnd.Row();
631 SCCOLROW nExtend = 0; // out parameter for lcl_IsAutoSumData
633 // ignore rows at the top of the given range which don't contain autosum data
634 bool bRowData = false;
635 for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
637 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
639 if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) != ScAutoSumNone )
641 bRowData = true;
642 break;
645 if ( bRowData )
647 nStartRow = nRow;
648 break;
651 if ( !bRowData )
653 return false;
656 // ignore columns at the left of the given range which don't contain autosum data
657 bool bColData = false;
658 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
660 for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
662 if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) != ScAutoSumNone )
664 bColData = true;
665 break;
668 if ( bColData )
670 nStartCol = nCol;
671 break;
674 if ( !bColData )
676 return false;
679 const bool bEndRowEmpty = rDoc.IsBlockEmpty( nStartCol, nEndRow, nEndCol, nEndRow, nTab );
680 const bool bEndColEmpty = rDoc.IsBlockEmpty( nEndCol, nStartRow, nEndCol, nEndRow, nTab );
681 bool bRow = ( nStartRow != nEndRow ) && ( bEndRowEmpty || !bEndColEmpty );
682 bool bCol = ( nStartCol != nEndCol ) && ( bEndColEmpty || nStartRow == nEndRow );
684 // find an empty row for entering the result
685 SCROW nInsRow = nEndRow;
686 if ( bRow && !bEndRowEmpty )
688 if ( nInsRow < rDoc.MaxRow() )
690 ++nInsRow;
691 while ( !rDoc.IsBlockEmpty( nStartCol, nInsRow, nEndCol, nInsRow, nTab ) )
693 if ( nInsRow < rDoc.MaxRow() )
695 ++nInsRow;
697 else
699 bRow = false;
700 break;
704 else
706 bRow = false;
710 // find an empty column for entering the result
711 SCCOL nInsCol = nEndCol;
712 if ( bCol && !bEndColEmpty )
714 if ( nInsCol < rDoc.MaxCol() )
716 ++nInsCol;
717 while ( !rDoc.IsBlockEmpty( nInsCol, nStartRow, nInsCol, nEndRow, nTab ) )
719 if ( nInsCol < rDoc.MaxCol() )
721 ++nInsCol;
723 else
725 bCol = false;
726 break;
730 else
732 bCol = false;
736 if ( !bRow && !bCol )
738 return false;
741 SCCOL nMarkEndCol = nEndCol;
742 SCROW nMarkEndRow = nEndRow;
743 ScAutoSum eSum = ScAutoSumNone;
744 SCROW nColSums = 0;
745 SCCOL nRowSums = 0;
746 SCROW nColSumsStartRow = 0;
747 SCCOL nRowSumsStartCol = 0;
749 if ( bRow )
751 // calculate the row sums for all columns of the given range
753 SCROW nSumEndRow = nEndRow;
755 if ( bEndRowEmpty )
757 // the last row of the given range is empty;
758 // don't take into account for calculating the autosum
759 --nSumEndRow;
761 else
763 // increase mark range
764 ++nMarkEndRow;
767 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
769 if ( !rDoc.IsBlockEmpty( nCol, nStartRow, nCol, nSumEndRow, nTab ) )
771 ScRangeList aRangeList;
772 // Include the originally selected start row.
773 const ScRange aRange( nCol, rRange.aStart.Row(), nTab, nCol, nSumEndRow, nTab );
774 if ( (eSum = lcl_GetAutoSumForColumnRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
776 if (++nRowSums == 1)
777 nRowSumsStartCol = aRangeList[0].aStart.Col();
778 const OUString aFormula = GetAutoSumFormula(
779 aRangeList, bSubTotal, ScAddress(nCol, nInsRow, nTab), eCode);
780 EnterData( nCol, nInsRow, nTab, aFormula );
786 if ( bCol )
788 // calculate the column sums for all rows of the given range
790 SCCOL nSumEndCol = nEndCol;
792 if ( bEndColEmpty )
794 // the last column of the given range is empty;
795 // don't take into account for calculating the autosum
796 --nSumEndCol;
798 else
800 // increase mark range
801 ++nMarkEndCol;
804 for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
806 if ( !rDoc.IsBlockEmpty( nStartCol, nRow, nSumEndCol, nRow, nTab ) )
808 ScRangeList aRangeList;
809 // Include the originally selected start column.
810 const ScRange aRange( rRange.aStart.Col(), nRow, nTab, nSumEndCol, nRow, nTab );
811 if ( (eSum = lcl_GetAutoSumForRowRange( rDoc, aRangeList, aRange )) != ScAutoSumNone )
813 if (++nColSums == 1)
814 nColSumsStartRow = aRangeList[0].aStart.Row();
815 const OUString aFormula = GetAutoSumFormula( aRangeList, bSubTotal, ScAddress(nInsCol, nRow, nTab), eCode );
816 EnterData( nInsCol, nRow, nTab, aFormula );
822 // Set new mark range and cursor position.
823 // For sum of sums (and data until sum) mark the actual resulting range if
824 // there is only one, or the data range if more than one. Otherwise use the
825 // original selection. All extended by end column/row where the sum is put.
826 const ScRange aMarkRange(
827 (eSum >= ScAutoSumSum ?
828 (nRowSums == 1 ? nRowSumsStartCol : nStartCol) :
829 rRange.aStart.Col()),
830 (eSum >= ScAutoSumSum ?
831 (nColSums == 1 ? nColSumsStartRow : nStartRow) :
832 rRange.aStart.Row()),
833 nTab, nMarkEndCol, nMarkEndRow, nTab );
834 MarkRange( aMarkRange, false, bContinue );
835 if ( bSetCursor )
837 SetCursor( nMarkEndCol, nMarkEndRow );
840 return true;
843 OUString ScViewFunc::GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr , const OpCode eCode)
845 ScViewData& rViewData = GetViewData();
846 ScDocument& rDoc = rViewData.GetDocument();
847 ScTokenArray aArray(rDoc);
849 aArray.AddOpCode(bSubTotal ? ocSubTotal : eCode);
850 aArray.AddOpCode(ocOpen);
852 if (bSubTotal)
854 aArray.AddDouble( GetSubTotal( eCode ) );
855 aArray.AddOpCode(ocSep);
858 if(!rRangeList.empty())
860 size_t ListSize = rRangeList.size();
861 for ( size_t i = 0; i < ListSize; ++i )
863 const ScRange & r = rRangeList[i];
864 if (i != 0)
865 aArray.AddOpCode(ocSep);
866 ScComplexRefData aRef;
867 aRef.InitRangeRel(rDoc, r, rAddr);
868 aArray.AddDoubleReference(aRef);
872 aArray.AddOpCode(ocClose);
874 ScCompiler aComp(rDoc, rAddr, aArray, rDoc.GetGrammar());
875 OUStringBuffer aBuf;
876 aComp.CreateStringFromTokenArray(aBuf);
877 aBuf.insert(0, "=");
878 return aBuf.makeStringAndClear();
881 void ScViewFunc::EnterBlock( const OUString& rString, const EditTextObject* pData )
883 // test for multi selection
885 SCCOL nCol = GetViewData().GetCurX();
886 SCROW nRow = GetViewData().GetCurY();
887 SCTAB nTab = GetViewData().GetTabNo();
888 ScMarkData& rMark = GetViewData().GetMarkData();
889 if ( rMark.IsMultiMarked() )
891 rMark.MarkToSimple();
892 if ( rMark.IsMultiMarked() )
893 { // "Insert into multi selection not possible"
894 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0);
896 // insert into single cell
897 if ( pData )
898 EnterData(nCol, nRow, nTab, *pData);
899 else
900 EnterData( nCol, nRow, nTab, rString );
901 return;
905 if (GetViewData().SelectionForbidsCellFill())
907 PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
908 return;
911 ScDocument& rDoc = GetViewData().GetDocument();
912 OUString aNewStr = rString;
913 if ( pData )
915 const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
916 ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
917 aEngine.SetTextCurrentDefaults(*pData);
919 ScEditAttrTester aTester( &aEngine );
920 if (!aTester.NeedsObject())
922 aNewStr = aEngine.GetText();
923 pData = nullptr;
927 // Insert via PasteFromClip
928 weld::WaitObject aWait(GetViewData().GetDialogParent());
930 ScAddress aPos( nCol, nRow, nTab );
932 ScDocumentUniquePtr pInsDoc(new ScDocument( SCDOCMODE_CLIP ));
933 pInsDoc->ResetClip( &rDoc, nTab );
935 if (aNewStr[0] == '=') // Formula ?
937 // SetString not possible, because in Clipboard-Documents nothing will be compiled!
938 pInsDoc->SetFormulaCell(aPos, new ScFormulaCell(rDoc, aPos, aNewStr));
940 else if ( pData )
942 // A copy of pData will be stored.
943 pInsDoc->SetEditText(aPos, *pData, rDoc.GetEditPool());
945 else
946 pInsDoc->SetString( nCol, nRow, nTab, aNewStr );
948 pInsDoc->SetClipArea( ScRange(aPos) );
949 // insert Block, with Undo etc.
950 if ( !PasteFromClip( InsertDeleteFlags::CONTENTS, pInsDoc.get(), ScPasteFunc::NONE, false, false,
951 false, INS_NONE, InsertDeleteFlags::ATTRIB ) )
952 return;
954 const SfxUInt32Item* pItem = pInsDoc->GetAttr(
955 nCol, nRow, nTab, ATTR_VALUE_FORMAT );
956 if ( pItem )
957 { // set number format if incompatible
958 // MarkData was already MarkToSimple'ed in PasteFromClip
959 const ScRange& aRange = rMark.GetMarkArea();
960 ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
961 aPattern.GetItemSet().Put( *pItem );
962 SvNumFormatType nNewType = rDoc.GetFormatTable()->GetType( pItem->GetValue() );
963 rDoc.ApplyPatternIfNumberformatIncompatible( aRange, rMark,
964 aPattern, nNewType );
968 // manual page break
970 void ScViewFunc::InsertPageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
971 bool bSetModified )
973 SCTAB nTab = GetViewData().GetTabNo();
974 ScAddress aCursor;
975 if (pPos)
976 aCursor = *pPos;
977 else
978 aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
980 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
981 InsertPageBreak( bColumn, aCursor, bRecord, bSetModified );
983 if ( bSuccess && bSetModified )
984 UpdatePageBreakData( true ); // for PageBreak-Mode
987 void ScViewFunc::DeletePageBreak( bool bColumn, bool bRecord, const ScAddress* pPos,
988 bool bSetModified )
990 SCTAB nTab = GetViewData().GetTabNo();
991 ScAddress aCursor;
992 if (pPos)
993 aCursor = *pPos;
994 else
995 aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
997 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
998 RemovePageBreak( bColumn, aCursor, bRecord, bSetModified );
1000 if ( bSuccess && bSetModified )
1001 UpdatePageBreakData( true ); // for PageBreak-Mode
1004 void ScViewFunc::RemoveManualBreaks()
1006 ScDocShell* pDocSh = GetViewData().GetDocShell();
1007 ScDocument& rDoc = pDocSh->GetDocument();
1008 SCTAB nTab = GetViewData().GetTabNo();
1009 bool bUndo(rDoc.IsUndoEnabled());
1011 if (bUndo)
1013 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1014 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
1015 rDoc.CopyToDocument( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
1016 pDocSh->GetUndoManager()->AddUndoAction(
1017 std::make_unique<ScUndoRemoveBreaks>( pDocSh, nTab, std::move(pUndoDoc) ) );
1020 rDoc.RemoveManualBreaks(nTab);
1021 rDoc.UpdatePageBreaks(nTab);
1023 UpdatePageBreakData( true );
1024 pDocSh->SetDocumentModified();
1025 pDocSh->PostPaint( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
1028 void ScViewFunc::SetPrintZoom(sal_uInt16 nScale)
1030 ScDocShell* pDocSh = GetViewData().GetDocShell();
1031 SCTAB nTab = GetViewData().GetTabNo();
1032 pDocSh->SetPrintZoom( nTab, nScale, 0/*nPages*/ );
1035 void ScViewFunc::AdjustPrintZoom()
1037 ScRange aRange;
1038 if ( GetViewData().GetSimpleArea( aRange ) != SC_MARK_SIMPLE )
1039 aRange = GetViewData().GetMarkData().GetMultiMarkArea();
1040 GetViewData().GetDocShell()->AdjustPrintZoom( aRange );
1043 void ScViewFunc::SetPrintRanges( bool bEntireSheet, const OUString* pPrint,
1044 const OUString* pRepCol, const OUString* pRepRow,
1045 bool bAddPrint )
1047 // on all selected tables
1049 ScDocShell* pDocSh = GetViewData().GetDocShell();
1050 ScDocument& rDoc = pDocSh->GetDocument();
1051 ScMarkData& rMark = GetViewData().GetMarkData();
1052 bool bUndo (rDoc.IsUndoEnabled());
1054 std::unique_ptr<ScPrintRangeSaver> pOldRanges = rDoc.CreatePrintRangeSaver();
1056 ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0);
1058 for (const SCTAB& nTab : rMark)
1060 ScRange aRange( 0,0,nTab );
1062 // print ranges
1064 if( !bAddPrint )
1066 rDoc.ClearPrintRanges( nTab );
1067 rDoc.ClearPrintNamedRanges(nTab);
1070 if( bEntireSheet )
1072 rDoc.SetPrintEntireSheet( nTab );
1074 else if ( pPrint )
1076 if ( !pPrint->isEmpty() )
1078 const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep);
1079 sal_Int32 nPos = 0;
1082 const OUString aToken = pPrint->getToken(0, sep, nPos);
1083 if ( aRange.ParseAny( aToken, rDoc, aDetails ) & ScRefFlags::VALID )
1084 rDoc.AddPrintRange( nTab, aRange );
1086 while (nPos >= 0);
1089 else // NULL = use selection (print range is always set), use empty string to delete all ranges
1091 if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
1093 rDoc.AddPrintRange( nTab, aRange );
1095 else if ( rMark.IsMultiMarked() )
1097 rMark.MarkToMulti();
1098 ScRangeListRef pList( new ScRangeList );
1099 rMark.FillRangeListWithMarks( pList.get(), false );
1100 for (size_t i = 0, n = pList->size(); i < n; ++i)
1102 const ScRange & rR = (*pList)[i];
1103 rDoc.AddPrintRange(nTab, rR);
1108 // repeat columns
1110 if ( pRepCol )
1112 if ( pRepCol->isEmpty() )
1113 rDoc.SetRepeatColRange( nTab, std::nullopt );
1114 else
1115 if ( aRange.ParseAny( *pRepCol, rDoc, aDetails ) & ScRefFlags::VALID )
1116 rDoc.SetRepeatColRange( nTab, std::move(aRange) );
1119 // repeat rows
1121 if ( pRepRow )
1123 if ( pRepRow->isEmpty() )
1124 rDoc.SetRepeatRowRange( nTab, std::nullopt );
1125 else
1126 if ( aRange.ParseAny( *pRepRow, rDoc, aDetails ) & ScRefFlags::VALID )
1127 rDoc.SetRepeatRowRange( nTab, std::move(aRange) );
1131 // undo (for all tables)
1132 if (bUndo)
1134 SCTAB nCurTab = GetViewData().GetTabNo();
1135 std::unique_ptr<ScPrintRangeSaver> pNewRanges = rDoc.CreatePrintRangeSaver();
1136 if (comphelper::LibreOfficeKit::isActive())
1138 tools::JsonWriter aJsonWriter;
1139 pNewRanges->GetPrintRangesInfo(aJsonWriter);
1141 SfxViewShell* pViewShell = GetViewData().GetViewShell();
1142 const OString message = aJsonWriter.finishAndGetAsOString();
1143 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_PRINT_RANGES, message);
1146 pDocSh->GetUndoManager()->AddUndoAction(
1147 std::make_unique<ScUndoPrintRange>( pDocSh, nCurTab, std::move(pOldRanges), std::move(pNewRanges) ) );
1149 else
1150 pOldRanges.reset();
1152 // update page breaks
1154 for (const auto& rTab : rMark)
1155 ScPrintFunc( pDocSh, pDocSh->GetPrinter(), rTab ).UpdatePages();
1157 SfxBindings& rBindings = GetViewData().GetBindings();
1158 rBindings.Invalidate( SID_DELETE_PRINTAREA );
1160 pDocSh->SetDocumentModified();
1163 // Merge cells
1165 bool ScViewFunc::TestMergeCells() // pre-test (for menu)
1167 // simple test: true if there's a selection but no multi selection and not filtered
1169 const ScMarkData& rMark = GetViewData().GetMarkData();
1170 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1172 ScRange aRange;
1173 bool bMergeable = ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE );
1174 bMergeable = bMergeable && ( aRange.aStart.Col() != aRange.aEnd.Col() ||
1175 aRange.aStart.Row() != aRange.aEnd.Row() );
1176 return bMergeable;
1178 else
1179 return false;
1182 void ScViewFunc::MergeCells( bool bApi, bool bDoContents, bool bCenter,
1183 const sal_uInt16 nSlot )
1185 // Editable- and Being-Nested- test must be at the beginning (in DocFunc too),
1186 // so that the Contents-QueryBox won't appear
1187 ScEditableTester aTester( this );
1188 if (!aTester.IsEditable())
1190 ErrorMessage(aTester.GetMessageId());
1191 return;
1194 ScMarkData& rMark = GetViewData().GetMarkData();
1195 rMark.MarkToSimple();
1196 if (!rMark.IsMarked())
1198 ErrorMessage(STR_NOMULTISELECT);
1199 return;
1202 ScDocShell* pDocSh = GetViewData().GetDocShell();
1203 ScDocument& rDoc = pDocSh->GetDocument();
1205 const ScRange& aMarkRange = rMark.GetMarkArea();
1206 SCCOL nStartCol = aMarkRange.aStart.Col();
1207 SCROW nStartRow = aMarkRange.aStart.Row();
1208 SCTAB nStartTab = aMarkRange.aStart.Tab();
1209 SCCOL nEndCol = aMarkRange.aEnd.Col();
1210 SCROW nEndRow = aMarkRange.aEnd.Row();
1211 SCTAB nEndTab = aMarkRange.aEnd.Tab();
1212 if ( nStartCol == nEndCol && nStartRow == nEndRow )
1214 // nothing to do
1215 return;
1218 if ( rDoc.HasAttrib( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
1219 HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1220 { // "Don't nest merging !"
1221 ErrorMessage(STR_MSSG_MERGECELLS_0);
1222 return;
1225 // Check for the contents of all selected tables.
1226 bool bAskDialog = false;
1227 ScCellMergeOption aMergeOption(nStartCol, nStartRow, nEndCol, nEndRow, bCenter);
1228 for (const SCTAB& i : rMark)
1230 aMergeOption.maTabs.insert(i);
1232 sc::MultiDataCellState aState = rDoc.HasMultipleDataCells(aMergeOption.getSingleRange(i));
1233 switch (aState.meState)
1235 case sc::MultiDataCellState::HasMultipleCells:
1237 // this range contains multiple data cells.
1238 bAskDialog = true;
1239 break;
1241 case sc::MultiDataCellState::HasOneCell:
1243 // this range contains only one data cell.
1244 if (nStartCol != aState.mnCol1 || nStartRow != aState.mnRow1)
1245 bDoContents = true; // move the value to the top-left.
1246 break;
1248 default:
1253 bool bEmptyMergedCells = officecfg::Office::Calc::Compatibility::MergeCells::EmptyMergedCells::get();
1255 auto doMerge = [this, pDocSh, aMergeOption=std::move(aMergeOption),
1256 bApi, nStartCol, nStartRow, aMarkRange]
1257 (bool bNowDoContents, bool bNowEmptyMergedCells)
1259 if (pDocSh->GetDocFunc().MergeCells(aMergeOption, bNowDoContents, true/*bRecord*/,
1260 bApi, bNowEmptyMergedCells))
1262 SetCursor( nStartCol, nStartRow );
1263 // DoneBlockMode( sal_False);
1264 Unmark();
1266 pDocSh->UpdateOle(GetViewData());
1267 UpdateInputLine();
1269 OUString aStartAddress = aMarkRange.aStart.GetColRowString();
1270 OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
1272 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"MERGE_CELLS"_ustr);
1276 if (bAskDialog)
1278 bool bShowDialog = officecfg::Office::Calc::Compatibility::MergeCells::ShowDialog::get();
1279 if (!bApi && bShowDialog)
1281 auto pBox = std::make_shared<ScMergeCellsDialog>(GetViewData().GetDialogParent());
1283 SfxViewShell* pViewShell = GetViewData().GetViewShell();
1285 weld::DialogController::runAsync(pBox, [pBox, bDoContents, bEmptyMergedCells, pViewShell,
1286 nSlot, bApi, doMerge=std::move(doMerge)](sal_Int32 nRetVal) {
1287 if (nRetVal == RET_OK)
1289 bool bRealDoContents = bDoContents;
1290 bool bRealEmptyMergedCells = bEmptyMergedCells;
1291 switch (pBox->GetMergeCellsOption())
1293 case MoveContentHiddenCells:
1294 bRealDoContents = true;
1295 break;
1296 case KeepContentHiddenCells:
1297 bRealEmptyMergedCells = false;
1298 break;
1299 case EmptyContentHiddenCells:
1300 bRealEmptyMergedCells = true;
1301 break;
1302 default:
1303 assert(!"Unknown option for merge cells.");
1304 break;
1307 doMerge(bRealDoContents, bRealEmptyMergedCells);
1309 if (nSlot != 0)
1311 SfxRequest aReq(pViewShell->GetViewFrame(), nSlot);
1312 if (!bApi && bRealDoContents)
1313 aReq.AppendItem(SfxBoolItem(nSlot, bDoContents));
1314 SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings();
1315 rBindings.Invalidate(nSlot);
1316 aReq.Done();
1319 // else cancelled
1322 } else
1323 doMerge(bDoContents, bEmptyMergedCells);
1326 bool ScViewFunc::TestRemoveMerge()
1328 bool bMerged = false;
1329 ScRange aRange;
1330 if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
1332 ScDocument& rDoc = GetViewData().GetDocument();
1333 if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) )
1334 bMerged = true;
1336 return bMerged;
1339 static bool lcl_extendMergeRange(ScCellMergeOption& rOption, const ScRange& rRange)
1341 bool bExtended = false;
1342 if (rOption.mnStartCol > rRange.aStart.Col())
1344 rOption.mnStartCol = rRange.aStart.Col();
1345 bExtended = true;
1347 if (rOption.mnStartRow > rRange.aStart.Row())
1349 rOption.mnStartRow = rRange.aStart.Row();
1350 bExtended = true;
1352 if (rOption.mnEndCol < rRange.aEnd.Col())
1354 rOption.mnEndCol = rRange.aEnd.Col();
1355 bExtended = true;
1357 if (rOption.mnEndRow < rRange.aEnd.Row())
1359 rOption.mnEndRow = rRange.aEnd.Row();
1360 bExtended = true;
1362 return bExtended;
1365 bool ScViewFunc::RemoveMerge()
1367 ScRange aRange;
1368 ScEditableTester aTester( this );
1369 if (!aTester.IsEditable())
1371 ErrorMessage(aTester.GetMessageId());
1372 return false;
1374 else if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE)
1376 ScDocument& rDoc = GetViewData().GetDocument();
1377 ScRange aExtended( aRange );
1378 rDoc.ExtendMerge( aExtended );
1379 ScDocShell* pDocSh = GetViewData().GetDocShell();
1380 const ScMarkData& rMark = GetViewData().GetMarkData();
1381 ScCellMergeOption aOption(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row());
1382 bool bExtended = false;
1385 bExtended = false;
1386 for (const SCTAB& i : rMark)
1388 aOption.maTabs.insert(i);
1389 aExtended.aStart.SetTab(i);
1390 aExtended.aEnd.SetTab(i);
1391 rDoc.ExtendMerge(aExtended);
1392 rDoc.ExtendOverlapped(aExtended);
1394 // Expand the current range to be inclusive of all merged
1395 // areas on all sheets.
1396 bExtended = lcl_extendMergeRange(aOption, aExtended);
1399 while (bExtended);
1401 bool bOk = pDocSh->GetDocFunc().UnmergeCells(aOption, true/*bRecord*/, nullptr);
1402 aExtended = aOption.getFirstSingleRange();
1403 MarkRange( aExtended );
1405 if (bOk)
1406 pDocSh->UpdateOle(GetViewData());
1409 OUString aCellLocation = aRange.aStart.GetColRowString();
1410 collectUIInformation({{"CELL", aCellLocation}}, u"UNMERGE_CELL"_ustr);
1412 return true; //! bOk ??
1415 void ScViewFunc::FillSimple( FillDir eDir )
1417 ScRange aRange;
1418 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
1420 ScDocShell* pDocSh = GetViewData().GetDocShell();
1421 const ScMarkData& rMark = GetViewData().GetMarkData();
1422 bool bSuccess = pDocSh->GetDocFunc().FillSimple( aRange, &rMark, eDir, false );
1423 if (bSuccess)
1425 pDocSh->UpdateOle(GetViewData());
1426 UpdateScrollBars();
1428 auto& rDoc = pDocSh->GetDocument();
1429 const ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
1430 const bool bDoAutoSpell = pTabViewShell && pTabViewShell->IsAutoSpell();
1431 if ( bDoAutoSpell )
1433 // Copy AutoSpellData from above(left/right/below) if no selection.
1434 switch (eDir)
1436 case FILL_TO_BOTTOM:
1437 if (aRange.aStart.Row() > 0 && aRange.aStart.Row() == aRange.aEnd.Row())
1438 aRange.aStart.IncRow(-1);
1439 break;
1440 case FILL_TO_TOP:
1441 if (aRange.aEnd.Row() < rDoc.MaxRow() && aRange.aStart.Row() == aRange.aEnd.Row())
1442 aRange.aEnd.IncRow(1);
1443 break;
1444 case FILL_TO_RIGHT:
1445 if (aRange.aStart.Col() > 0 && aRange.aStart.Col() == aRange.aEnd.Col())
1446 aRange.aStart.IncCol(-1);
1447 break;
1448 case FILL_TO_LEFT:
1449 if (aRange.aEnd.Col() < rDoc.MaxCol() && aRange.aStart.Col() == aRange.aEnd.Col())
1450 aRange.aEnd.IncCol(1);
1451 break;
1453 CopyAutoSpellData(eDir, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(),
1454 ::std::numeric_limits<sal_uLong>::max());
1457 // Invalidate cell slots and update input line with new content.
1458 CellContentChanged();
1461 else
1462 ErrorMessage(STR_NOMULTISELECT);
1465 void ScViewFunc::FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
1466 double fStart, double fStep, double fMax )
1468 ScRange aRange;
1469 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
1471 ScDocShell* pDocSh = GetViewData().GetDocShell();
1472 const ScMarkData& rMark = GetViewData().GetMarkData();
1473 bool bSuccess = pDocSh->GetDocFunc().
1474 FillSeries( aRange, &rMark, eDir, eCmd, eDateCmd,
1475 fStart, fStep, fMax, false );
1476 if (bSuccess)
1478 pDocSh->UpdateOle(GetViewData());
1479 UpdateScrollBars();
1481 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange);
1484 else
1485 ErrorMessage(STR_NOMULTISELECT);
1488 void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
1489 SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
1491 SCTAB nTab = GetViewData().GetTabNo();
1492 ScRange aRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab );
1493 ScRange aSourceRange( aRange );
1494 ScDocShell* pDocSh = GetViewData().GetDocShell();
1495 const ScMarkData& rMark = GetViewData().GetMarkData();
1496 bool bSuccess = pDocSh->GetDocFunc().
1497 FillAuto( aRange, &rMark, eDir, nCount, false );
1498 if (!bSuccess)
1499 return;
1501 MarkRange( aRange, false ); // aRange was modified in FillAuto
1502 pDocSh->UpdateOle(GetViewData());
1503 UpdateScrollBars();
1505 const ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
1506 const bool bDoAutoSpell = pTabViewShell && pTabViewShell->IsAutoSpell();
1507 if ( bDoAutoSpell )
1508 CopyAutoSpellData(eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount);
1510 ScModelObj* pModelObj = pDocSh->GetModel();
1512 ScRangeList aChangeRanges;
1513 ScRange aChangeRange( aRange );
1514 switch (eDir)
1516 case FILL_TO_BOTTOM:
1517 aChangeRange.aStart.SetRow( aSourceRange.aEnd.Row() + 1 );
1518 break;
1519 case FILL_TO_TOP:
1520 aChangeRange.aEnd.SetRow( aSourceRange.aStart.Row() - 1 );
1521 break;
1522 case FILL_TO_RIGHT:
1523 aChangeRange.aStart.SetCol( aSourceRange.aEnd.Col() + 1 );
1524 break;
1525 case FILL_TO_LEFT:
1526 aChangeRange.aEnd.SetCol( aSourceRange.aStart.Col() - 1 );
1527 break;
1528 default:
1529 break;
1531 aChangeRanges.push_back( aChangeRange );
1533 if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
1534 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
1535 else if (pModelObj)
1536 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"data-area-invalidate"_ustr);
1539 void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow,
1540 SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount )
1542 const ScDocument* pDoc = &GetViewData().GetDocument();
1543 SCTAB nTab = GetViewData().GetTabNo();
1544 CellType eCellType;
1546 ScGridWindow* pWin = GetActiveWin();
1547 if ( pWin->InsideVisibleRange(nStartCol, nStartRow) && pWin->InsideVisibleRange(nEndCol, nEndRow) )
1549 if ( nCount == ::std::numeric_limits<sal_uLong>::max() )
1551 switch( eDir )
1553 case FILL_TO_BOTTOM:
1554 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1556 eCellType = pDoc->GetCellType(nColItr, nStartRow, nTab); // We need this optimization only for EditTextObject source cells
1557 if (eCellType != CELLTYPE_EDIT)
1558 continue;
1560 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nStartRow);
1561 if ( !pRanges )
1562 continue;
1563 for ( SCROW nRowItr = nStartRow + 1; nRowItr <= nEndRow; ++nRowItr )
1564 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1566 break;
1567 case FILL_TO_TOP:
1568 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1570 eCellType = pDoc->GetCellType(nColItr, nEndRow, nTab); // We need this optimization only for EditTextObject source cells
1571 if (eCellType != CELLTYPE_EDIT)
1572 continue;
1574 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nColItr, nEndRow);
1575 if ( !pRanges )
1576 continue;
1577 for ( SCROW nRowItr = nEndRow - 1; nRowItr >= nStartRow; --nRowItr )
1578 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1580 break;
1581 case FILL_TO_RIGHT:
1582 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1584 eCellType = pDoc->GetCellType(nStartCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells
1585 if (eCellType != CELLTYPE_EDIT)
1586 continue;
1588 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nStartCol, nRowItr);
1589 if ( !pRanges )
1590 continue;
1591 for ( SCCOL nColItr = nStartCol + 1; nColItr <= nEndCol; ++nColItr )
1592 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1594 break;
1595 case FILL_TO_LEFT:
1596 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1598 eCellType = pDoc->GetCellType(nEndCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells
1599 if (eCellType != CELLTYPE_EDIT)
1600 continue;
1602 const std::vector<editeng::MisspellRanges>* pRanges = pWin->GetAutoSpellData(nEndCol, nRowItr);
1603 if ( !pRanges )
1604 continue;
1605 for ( SCCOL nColItr = nEndCol - 1; nColItr >= nStartCol; --nColItr )
1606 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1608 break;
1610 return;
1613 typedef const std::vector<editeng::MisspellRanges>* MisspellRangesType;
1614 SCROW nRowRepeatSize = nEndRow - nStartRow + 1;
1615 SCCOL nColRepeatSize = nEndCol - nStartCol + 1;
1616 SCROW nTillRow = 0;
1617 SCCOL nTillCol = 0;
1618 std::vector<std::vector<MisspellRangesType>> aSourceSpellRanges(nRowRepeatSize, std::vector<MisspellRangesType>(nColRepeatSize, nullptr));
1620 for ( SCROW nRowIdx = 0; nRowIdx < nRowRepeatSize; ++nRowIdx )
1622 for ( SCCOL nColIdx = 0; nColIdx < nColRepeatSize; ++nColIdx )
1624 eCellType = pDoc->GetCellType(nStartCol + nColIdx, nStartRow + nRowIdx, nTab); // We need this optimization only for EditTextObject source cells
1625 if (eCellType != CELLTYPE_EDIT)
1626 continue;
1628 aSourceSpellRanges[nRowIdx][nColIdx] = pWin->GetAutoSpellData( nStartCol + nColIdx, nStartRow + nRowIdx );
1632 switch( eDir )
1634 case FILL_TO_BOTTOM:
1635 nTillRow = nEndRow + nCount;
1636 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1638 for ( SCROW nRowItr = nEndRow + 1; nRowItr <= nTillRow; ++nRowItr )
1640 size_t nSourceRowIdx = ( nRowItr - nEndRow - 1 ) % nRowRepeatSize;
1641 MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
1642 if ( !pRanges )
1643 continue;
1644 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1647 break;
1649 case FILL_TO_TOP:
1650 nTillRow = nStartRow - nCount;
1651 for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr )
1653 for ( SCROW nRowItr = nStartRow - 1; nRowItr >= nTillRow; --nRowItr )
1655 size_t nSourceRowIdx = nRowRepeatSize - 1 - ( ( nStartRow - 1 - nRowItr ) % nRowRepeatSize );
1656 MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol];
1657 if ( !pRanges )
1658 continue;
1659 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1662 break;
1664 case FILL_TO_RIGHT:
1665 nTillCol = nEndCol + nCount;
1666 for ( SCCOL nColItr = nEndCol + 1; nColItr <= nTillCol; ++nColItr )
1668 size_t nSourceColIdx = ( nColItr - nEndCol - 1 ) % nColRepeatSize;
1669 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1671 MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
1672 if ( !pRanges )
1673 continue;
1674 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1677 break;
1679 case FILL_TO_LEFT:
1680 nTillCol = nStartCol - nCount;
1681 for ( SCCOL nColItr = nStartCol - 1; nColItr >= nTillCol; --nColItr )
1683 size_t nSourceColIdx = nColRepeatSize - 1 - ( ( nStartCol - 1 - nColItr ) % nColRepeatSize );
1684 for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr )
1686 MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx];
1687 if ( !pRanges )
1688 continue;
1689 pWin->SetAutoSpellData(nColItr, nRowItr, pRanges);
1692 break;
1695 else
1696 pWin->ResetAutoSpellForContentChange();
1700 void ScViewFunc::FillTab( InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bAsLink )
1702 //! allow source sheet to be protected
1703 ScEditableTester aTester( this );
1704 if (!aTester.IsEditable())
1706 ErrorMessage(aTester.GetMessageId());
1707 return;
1710 ScDocShell* pDocSh = GetViewData().GetDocShell();
1711 ScDocument& rDoc = pDocSh->GetDocument();
1712 ScMarkData& rMark = GetViewData().GetMarkData();
1713 SCTAB nTab = GetViewData().GetTabNo();
1714 bool bUndo(rDoc.IsUndoEnabled());
1716 ScRange aMarkRange;
1717 rMark.MarkToSimple();
1718 bool bMulti = rMark.IsMultiMarked();
1719 if (bMulti)
1720 aMarkRange = rMark.GetMultiMarkArea();
1721 else if (rMark.IsMarked())
1722 aMarkRange = rMark.GetMarkArea();
1723 else
1724 aMarkRange = ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab );
1726 ScDocumentUniquePtr pUndoDoc;
1728 if (bUndo)
1730 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1731 pUndoDoc->InitUndo( rDoc, nTab, nTab );
1733 for (const SCTAB& i : rMark)
1734 if (i != nTab )
1736 pUndoDoc->AddUndoTab( i, i );
1737 aMarkRange.aStart.SetTab( i );
1738 aMarkRange.aEnd.SetTab( i );
1739 rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc );
1743 if (bMulti)
1744 rDoc.FillTabMarked( nTab, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
1745 else
1747 aMarkRange.aStart.SetTab( nTab );
1748 aMarkRange.aEnd.SetTab( nTab );
1749 rDoc.FillTab( aMarkRange, rMark, nFlags, nFunction, bSkipEmpty, bAsLink );
1752 if (bUndo)
1753 { //! for ChangeTrack not until the end
1754 pDocSh->GetUndoManager()->AddUndoAction(
1755 std::make_unique<ScUndoFillTable>( pDocSh, rMark,
1756 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nTab,
1757 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab,
1758 std::move(pUndoDoc), bMulti, nTab, nFlags, nFunction, bSkipEmpty, bAsLink ) );
1761 pDocSh->PostPaintGridAll();
1762 pDocSh->PostDataChanged();
1765 /** Downward fill of selected cell(s) by double-clicking cross-hair cursor
1767 Either, extends a current selection if non-empty cells exist immediately
1768 below the selection, overwriting cells below the selection up to the
1769 minimum row of already filled cells.
1771 Or, extends a current selection down to the last non-empty cell of an
1772 adjacent column when the lower-right corner of the selection is
1773 double-clicked. It uses a left-adjoining non-empty column as a guide if
1774 such is available, otherwise a right-adjoining non-empty column is used.
1776 @return No return value
1778 @see #i12313#
1780 void ScViewFunc::FillCrossDblClick()
1782 ScRange aRange;
1783 GetViewData().GetSimpleArea( aRange );
1784 aRange.PutInOrder();
1786 SCTAB nTab = GetViewData().GetCurPos().Tab();
1787 SCCOL nStartX = aRange.aStart.Col();
1788 SCROW nStartY = aRange.aStart.Row();
1789 SCCOL nEndX = aRange.aEnd.Col();
1790 SCROW nEndY = aRange.aEnd.Row();
1792 ScDocument& rDoc = GetViewData().GetDocument();
1794 if (nEndY >= rDoc.MaxRow())
1795 // Nothing to fill.
1796 return;
1798 // Make sure the selection is not empty
1799 if ( rDoc.IsBlockEmpty( nStartX, nStartY, nEndX, nEndY, nTab ) )
1800 return;
1802 // If there is data in all columns immediately below the selection then
1803 // switch to overwriting fill.
1804 SCROW nOverWriteEndRow = rDoc.MaxRow();
1805 for (SCCOL nCol = nStartX; nCol <= nEndX; ++nCol)
1807 if (rDoc.HasData( nCol, nEndY + 1, nTab))
1809 // Determine the shortest data column to end the fill.
1810 SCROW nY = nEndY + 1;
1811 // FindAreaPos() returns the start row of the next data block if
1812 // the current row is the last row of a data block and an empty
1813 // cell follows. Somewhat unexpected behaviour...
1814 // So check beforehand if there is one non-empty cell following.
1815 if (rDoc.HasData( nCol, nY + 1, nTab))
1817 rDoc.FindAreaPos( nCol, nY, nTab, SC_MOVE_DOWN);
1818 if (nOverWriteEndRow > nY)
1819 nOverWriteEndRow = nY;
1821 else
1823 nOverWriteEndRow = nY;
1826 else
1828 nOverWriteEndRow = 0;
1829 break; // for
1833 if (nOverWriteEndRow > nEndY)
1835 FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nOverWriteEndRow - nEndY);
1836 return;
1839 // Non-overwriting fill follows.
1841 const bool bDataLeft = (nStartX > 0);
1842 if (!bDataLeft && nEndX >= rDoc.MaxCol())
1843 // Absolutely no data left or right of selection.
1844 return;
1846 // Check that there is
1847 // 1) data immediately left (preferred) or right of start (row) of selection
1848 // 2) data there below
1849 // 3) no data immediately below selection
1851 SCCOL nMovX = (bDataLeft ? nStartX - 1 : nEndX + 1);
1852 SCROW nMovY = nStartY;
1853 bool bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
1854 if (!bDataFound && bDataLeft && nEndX < rDoc.MaxCol())
1856 nMovX = nEndX + 1; // check right
1857 bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab));
1860 if (!(bDataFound && rDoc.IsEmptyData( nStartX, nEndY + 1, nEndX, nEndY + 1, nTab )))
1861 return;
1863 // Get end of data left or right.
1864 rDoc.FindAreaPos( nMovX, nMovY, nTab, SC_MOVE_DOWN);
1865 // Find minimum end row of below empty area and data right.
1866 for (SCCOL nX = nStartX; nX <= nEndX; ++nX)
1868 SCROW nY = nEndY + 1;
1869 // Get next row with data in this column.
1870 rDoc.FindAreaPos( nX, nY, nTab, SC_MOVE_DOWN);
1871 if (nMovY == rDoc.MaxRow() && nY == rDoc.MaxRow())
1873 // FindAreaPos() returns MAXROW also if there is no data at all
1874 // from the start, so check if that contains data if the nearby
1875 // (left or right) data ends there and increment if no data
1876 // here, pretending the next data would be thereafter so nMovY
1877 // will not be decremented.
1878 if (!rDoc.HasData( nX, nY, nTab))
1879 ++nY;
1881 if (nMovY > nY - 1)
1882 nMovY = nY - 1;
1885 if (nMovY > nEndY)
1887 FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nMovY - nEndY);
1891 void ScViewFunc::ConvertFormulaToValue()
1893 ScRange aRange;
1894 GetViewData().GetSimpleArea(aRange);
1895 aRange.PutInOrder();
1897 ScDocShell* pDocSh = GetViewData().GetDocShell();
1898 pDocSh->GetDocFunc().ConvertFormulaToValue(aRange, true);
1899 // tdf#131326 - invalidate cell slots and update input line with new content
1900 CellContentChanged();
1901 pDocSh->PostPaint(aRange, PaintPartFlags::Grid);
1904 void ScViewFunc::TransliterateText( TransliterationFlags nType )
1906 ScMarkData aFuncMark = GetViewData().GetMarkData();
1907 if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
1909 // no selection -> use cursor position
1911 ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
1912 aFuncMark.SetMarkArea( ScRange( aCursor ) );
1915 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
1916 TransliterateText( aFuncMark, nType, false );
1917 if (bSuccess)
1919 GetViewData().GetViewShell()->UpdateInputHandler();
1923 // AutoFormat
1925 ScAutoFormatData* ScViewFunc::CreateAutoFormatData()
1927 ScAutoFormatData* pData = nullptr;
1928 SCCOL nStartCol;
1929 SCROW nStartRow;
1930 SCTAB nStartTab;
1931 SCCOL nEndCol;
1932 SCROW nEndRow;
1933 SCTAB nEndTab;
1934 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
1936 if ( nEndCol-nStartCol >= 3 && nEndRow-nStartRow >= 3 )
1938 ScDocument& rDoc = GetViewData().GetDocument();
1939 pData = new ScAutoFormatData;
1940 rDoc.GetAutoFormatData( nStartTab, nStartCol,nStartRow,nEndCol,nEndRow, *pData );
1943 return pData;
1946 void ScViewFunc::AutoFormat( sal_uInt16 nFormatNo )
1948 ScRange aRange;
1949 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
1951 ScDocShell* pDocSh = GetViewData().GetDocShell();
1952 ScMarkData& rMark = GetViewData().GetMarkData();
1954 bool bSuccess = pDocSh->GetDocFunc().AutoFormat( aRange, &rMark, nFormatNo, false );
1955 if (bSuccess)
1956 pDocSh->UpdateOle(GetViewData());
1958 else
1959 ErrorMessage(STR_NOMULTISELECT);
1962 // Search & Replace
1964 bool ScViewFunc::SearchAndReplace( const SvxSearchItem* pSearchItem,
1965 bool bAddUndo, bool bIsApi )
1967 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
1968 ScDocShell* pDocSh = GetViewData().GetDocShell();
1969 ScDocument& rDoc = pDocSh->GetDocument();
1970 ScMarkData& rMark = GetViewData().GetMarkData();
1971 if (bAddUndo && !rDoc.IsUndoEnabled())
1972 bAddUndo = false;
1974 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() && (pSearchItem->HasStartPoint()) )
1976 // No selection -> but we have a start point (top left corner of the
1977 // current view), start searching from there, not from the current
1978 // cursor position.
1979 SCCOL nPosX;
1980 SCROW nPosY;
1982 int nPixelX = pSearchItem->GetStartPointX() * GetViewData().GetPPTX();
1983 int nPixelY = pSearchItem->GetStartPointY() * GetViewData().GetPPTY();
1985 GetViewData().GetPosFromPixel(nPixelX, nPixelY, GetViewData().GetActivePart(), nPosX, nPosY);
1987 AlignToCursor( nPosX, nPosY, SC_FOLLOW_JUMP );
1988 SetCursor( nPosX, nPosY, true );
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();
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 )
2005 nStartTab = 0;
2006 nEndTab = nLastTab;
2007 std::set<SCTAB> aTmp(rMark.begin(), rMark.end());
2008 aOldSelectedTables.swap(aTmp);
2010 else
2011 { //! at least one is always selected
2012 nStartTab = rMark.GetFirstSelected();
2013 nEndTab = rMark.GetLastSelected();
2016 if ( nCommand == SvxSearchCmd::FIND
2017 || nCommand == SvxSearchCmd::FIND_ALL)
2018 bAddUndo = false;
2020 //! account for bAttrib during Undo !!!
2022 ScDocumentUniquePtr pUndoDoc;
2023 std::unique_ptr<ScMarkData> pUndoMark;
2024 OUString aUndoStr;
2025 if (bAddUndo)
2027 pUndoMark.reset(new ScMarkData(rMark)); // Mark is being modified
2028 if ( nCommand == SvxSearchCmd::REPLACE_ALL )
2030 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
2031 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
2035 if ( bAllTables )
2036 { //! select all, after pUndoMark has been created
2037 for ( SCTAB j = nStartTab; j <= nEndTab; j++ )
2039 rMark.SelectTable( j, true );
2043 DoneBlockMode(true); // don't delete mark
2044 InitOwnBlockMode( ScRange( nCol, nRow, nStartTab, nCol, nRow, nEndTab));
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;
2051 bool bFound = false;
2052 while (true)
2054 GetFrameWin()->EnterWait();
2055 ScRangeList aMatchedRanges;
2056 bool bMatchedRangesWereClamped = false;
2057 if (rDoc.SearchAndReplace(*pSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, pUndoDoc.get(), bMatchedRangesWereClamped))
2059 bFound = true;
2060 if (bAddUndo)
2062 GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
2063 std::make_unique<ScUndoReplace>( GetViewData().GetDocShell(), *pUndoMark,
2064 nCol, nRow, nTab,
2065 aUndoStr, std::move(pUndoDoc), pSearchItem ) );
2068 if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
2070 SfxViewFrame* pViewFrm = SfxViewFrame::Current();
2071 bool bShow = GetViewData().GetViewShell()->GetViewData().GetOptions().GetOption( VOPT_SUMMARY );
2073 if (bShow && pViewFrm && !comphelper::LibreOfficeKit::isActive())
2075 pViewFrm->ShowChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
2076 SfxChildWindow* pWnd = pViewFrm->GetChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId());
2077 if (pWnd)
2079 sc::SearchResultsDlg* pDlg = static_cast<sc::SearchResultsDlg*>(pWnd->GetController().get());
2080 if (pDlg)
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);
2095 rMark.ResetMark();
2096 for (size_t i = 0, n = aMatchedRanges.size(); i < n; ++i)
2098 const ScRange& r = aMatchedRanges[i];
2099 if (r.aStart.Tab() == nTab)
2100 rMark.SetMultiMarkArea(r);
2104 break; // break 'while (TRUE)'
2106 else if ( bFirst && (nCommand == SvxSearchCmd::FIND ||
2107 nCommand == SvxSearchCmd::REPLACE) )
2109 bFirst = false;
2110 GetFrameWin()->LeaveWait();
2111 if (!bIsApi)
2113 if ( nStartTab == nEndTab )
2114 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::EndSheet);
2115 else
2116 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End);
2118 rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow );
2119 if (pSearchItem->GetBackward())
2120 nTab = nEndTab;
2121 else
2122 nTab = nStartTab;
2124 else
2126 break; // break 'while (TRUE)'
2129 else // nothing found
2131 if ( nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL )
2133 pDocSh->PostPaintGridAll(); // Mark
2136 GetFrameWin()->LeaveWait();
2137 if (!bIsApi)
2139 GetViewData().GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, pSearchItem->GetSearchString().toUtf8());
2140 SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound);
2143 break; // break 'while (TRUE)'
2145 } // of while true
2147 if (!aOldSelectedTables.empty())
2149 // restore originally selected table
2150 for (SCTAB i = 0; i <= nEndTab; ++i)
2151 rMark.SelectTable(i, false);
2153 for (const auto& rTab : aOldSelectedTables)
2154 rMark.SelectTable(rTab, true);
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 );
2166 // Avoid LOK selection notifications before we have all the results.
2167 GetViewData().GetViewShell()->setTiledSearching(true);
2168 MarkDataChanged();
2169 GetViewData().GetViewShell()->setTiledSearching(false);
2171 if ( bFound )
2173 if ( nTab != GetViewData().GetTabNo() )
2174 SetTabNo( nTab );
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);
2181 AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP );
2182 SetCursor( nCol, nRow, true );
2184 if (comphelper::LibreOfficeKit::isActive())
2186 Point aCurPos = GetViewData().GetScrPos(nCol, nRow, GetViewData().GetActivePart());
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)
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());
2199 if (pGridWindow)
2201 std::vector<tools::Rectangle> aLogicRects;
2202 pGridWindow->GetCellSelection(aLogicRects);
2204 boost::property_tree::ptree aTree;
2205 aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr());
2206 aTree.put("highlightAll", nCommand == SvxSearchCmd::FIND_ALL);
2208 boost::property_tree::ptree aSelections;
2209 for (const tools::Rectangle& rLogicRect : aLogicRects)
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));
2216 aTree.add_child("searchResultSelection", aSelections);
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);
2224 // Trigger LOK_CALLBACK_TEXT_SELECTION now.
2225 MarkDataChanged();
2229 if ( nCommand == SvxSearchCmd::REPLACE
2230 || nCommand == SvxSearchCmd::REPLACE_ALL )
2232 if ( nCommand == SvxSearchCmd::REPLACE )
2234 pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid );
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 )
2240 SvxSearchItem aSearchItem = ScGlobal::GetSearchItem();
2241 aSearchItem.SetCommand(SvxSearchCmd::FIND);
2242 aSearchItem.SetWhich(SID_SEARCH_ITEM);
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 ) )
2251 AlignToCursor(nCol, nRow, SC_FOLLOW_JUMP);
2252 SetCursor( nCol, nRow, true );
2256 else
2257 pDocSh->PostPaintGridAll();
2258 pDocSh->SetDocumentModified();
2260 else if ( nCommand == SvxSearchCmd::FIND_ALL )
2261 pDocSh->PostPaintGridAll(); // mark
2262 GetFrameWin()->LeaveWait();
2264 return bFound;
2267 // Goal Seek
2269 void ScViewFunc::Solve( const ScSolveParam& rParam )
2271 ScDocument& rDoc = GetViewData().GetDocument();
2273 SCCOL nDestCol = rParam.aRefVariableCell.Col();
2274 SCROW nDestRow = rParam.aRefVariableCell.Row();
2275 SCTAB nDestTab = rParam.aRefVariableCell.Tab();
2277 ScEditableTester aTester( rDoc, nDestTab, nDestCol,nDestRow, nDestCol,nDestRow );
2278 if (!aTester.IsEditable())
2280 ErrorMessage(aTester.GetMessageId());
2281 return;
2284 OUString aTargetValStr;
2285 if ( rParam.pStrTargetVal )
2286 aTargetValStr = *rParam.pStrTargetVal;
2288 OUString aMsgStr;
2289 OUString aResStr;
2290 double nSolveResult;
2291 GetFrameWin()->EnterWait();
2293 bool bExact =
2294 rDoc.Solver(
2295 rParam.aRefFormulaCell.Col(),
2296 rParam.aRefFormulaCell.Row(),
2297 rParam.aRefFormulaCell.Tab(),
2298 nDestCol, nDestRow, nDestTab,
2299 aTargetValStr,
2300 nSolveResult);
2302 GetFrameWin()->LeaveWait();
2304 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
2305 sal_uLong nFormat = 0;
2306 const ScPatternAttr* pPattern = rDoc.GetPattern( nDestCol, nDestRow, nDestTab );
2307 if ( pPattern )
2308 nFormat = pPattern->GetNumberFormat( pFormatter );
2309 const Color* p;
2310 pFormatter->GetOutputString( nSolveResult, nFormat, aResStr, &p );
2312 if ( bExact )
2314 aMsgStr += ScResId( STR_MSSG_SOLVE_0 ) +
2315 aResStr +
2316 ScResId( STR_MSSG_SOLVE_1 );
2318 else
2320 aMsgStr = ScResId( STR_MSSG_SOLVE_2 ) +
2321 ScResId( STR_MSSG_SOLVE_3 ) +
2322 aResStr +
2323 ScResId( STR_MSSG_SOLVE_4 );
2326 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
2327 VclMessageType::Question, VclButtonsType::YesNo, aMsgStr));
2328 xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0));
2329 xBox->set_default_response(RET_NO);
2330 int nResponse = xBox->run();
2331 if (nResponse == RET_YES)
2332 EnterValue( nDestCol, nDestRow, nDestTab, nSolveResult );
2334 GetViewData().GetViewShell()->UpdateInputHandler( true );
2337 // multi operation
2339 void ScViewFunc::TabOp( const ScTabOpParam& rParam, bool bRecord )
2341 ScRange aRange;
2342 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
2344 ScDocShell* pDocSh = GetViewData().GetDocShell();
2345 ScMarkData& rMark = GetViewData().GetMarkData();
2346 pDocSh->GetDocFunc().TabOp( aRange, &rMark, rParam, bRecord, false );
2348 else
2349 ErrorMessage(STR_NOMULTISELECT);
2352 void ScViewFunc::MakeScenario( const OUString& rName, const OUString& rComment,
2353 const Color& rColor, ScScenarioFlags nFlags )
2355 ScDocShell* pDocSh = GetViewData().GetDocShell();
2356 ScMarkData& rMark = GetViewData().GetMarkData();
2357 SCTAB nTab = GetViewData().GetTabNo();
2359 SCTAB nNewTab = pDocSh->MakeScenario( nTab, rName, rComment, rColor, nFlags, rMark );
2360 if (nFlags & ScScenarioFlags::CopyAll)
2361 SetTabNo( nNewTab, true ); // ScScenarioFlags::CopyAll -> visible
2362 else
2364 SfxBindings& rBindings = GetViewData().GetBindings();
2365 rBindings.Invalidate( SID_STATUS_DOCPOS ); // Statusbar
2366 rBindings.Invalidate( SID_ROWCOL_SELCOUNT ); // Statusbar
2367 rBindings.Invalidate( SID_TABLES_COUNT );
2368 rBindings.Invalidate( SID_SELECT_SCENARIO );
2369 rBindings.Invalidate( FID_TABLE_SHOW );
2373 void ScViewFunc::ExtendScenario()
2375 ScEditableTester aTester( this );
2376 if (!aTester.IsEditable())
2378 ErrorMessage(aTester.GetMessageId());
2379 return;
2382 // Undo: apply attributes
2384 ScDocument& rDoc = GetViewData().GetDocument();
2385 ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
2386 aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) );
2387 aPattern.GetItemSet().Put( ScProtectionAttr( true ) );
2388 ApplySelectionPattern(aPattern);
2391 void ScViewFunc::UseScenario( const OUString& rName )
2393 ScDocShell* pDocSh = GetViewData().GetDocShell();
2394 SCTAB nTab = GetViewData().GetTabNo();
2396 DoneBlockMode();
2397 InitOwnBlockMode( ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab));
2398 pDocSh->UseScenario( nTab, rName );
2401 // Insert table
2403 bool ScViewFunc::InsertTable( const OUString& rName, SCTAB nTab, bool bRecord )
2405 // Order Table/Name is inverted for DocFunc
2406 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
2407 InsertTable( nTab, rName, bRecord, false );
2408 if (bSuccess)
2409 SetTabNo( nTab, true );
2411 return bSuccess;
2414 // Insert tables
2416 void ScViewFunc::InsertTables(std::vector<OUString>& aNames, SCTAB nTab,
2417 SCTAB nCount, bool bRecord )
2419 ScDocShell* pDocSh = GetViewData().GetDocShell();
2420 ScDocument& rDoc = pDocSh->GetDocument();
2421 if (bRecord && !rDoc.IsUndoEnabled())
2422 bRecord = false;
2424 weld::WaitObject aWait(GetViewData().GetDialogParent());
2426 if (bRecord)
2428 rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
2431 bool bFlag=false;
2433 if(aNames.empty())
2435 rDoc.CreateValidTabNames(aNames, nCount);
2437 if (rDoc.InsertTabs(nTab, aNames))
2439 pDocSh->Broadcast( ScTablesHint( SC_TABS_INSERTED, nTab, nCount ) );
2440 bFlag = true;
2443 if (!bFlag)
2444 return;
2446 if (bRecord)
2447 pDocSh->GetUndoManager()->AddUndoAction(
2448 std::make_unique<ScUndoInsertTables>( pDocSh, nTab, std::move(aNames)));
2450 // Update views
2452 SetTabNo( nTab, true );
2453 pDocSh->PostPaintExtras();
2454 pDocSh->SetDocumentModified();
2455 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2458 bool ScViewFunc::AppendTable( const OUString& rName, bool bRecord )
2460 ScDocShell* pDocSh = GetViewData().GetDocShell();
2461 ScDocument& rDoc = pDocSh->GetDocument();
2462 if (bRecord && !rDoc.IsUndoEnabled())
2463 bRecord = false;
2465 weld::WaitObject aWait(GetViewData().GetDialogParent());
2467 if (bRecord)
2468 rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage
2470 if (rDoc.InsertTab( SC_TAB_APPEND, rName ))
2472 SCTAB nTab = rDoc.GetTableCount()-1;
2473 if (bRecord)
2474 pDocSh->GetUndoManager()->AddUndoAction(
2475 std::make_unique<ScUndoInsertTab>( pDocSh, nTab, true, rName));
2476 GetViewData().InsertTab( nTab );
2477 SetTabNo( nTab, true );
2478 pDocSh->PostPaintExtras();
2479 pDocSh->SetDocumentModified();
2480 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2481 return true;
2483 else
2485 return false;
2489 void ScViewFunc::DeleteTable( SCTAB nTab, bool bRecord )
2491 ScDocShell* pDocSh = GetViewData().GetDocShell();
2492 ScDocument& rDoc = pDocSh->GetDocument();
2494 bool bSuccess = pDocSh->GetDocFunc().DeleteTable( nTab, bRecord );
2495 if (bSuccess)
2497 SCTAB nNewTab = nTab;
2498 if ( nNewTab >= rDoc.GetTableCount() )
2499 --nNewTab;
2500 SetTabNo( nNewTab, true );
2504 //only use this method for undo for now, all sheets must be connected
2505 //this method doesn't support undo for now, merge it when it with the other method later
2506 void ScViewFunc::DeleteTables( const SCTAB nTab, SCTAB nSheets )
2508 ScDocShell* pDocSh = GetViewData().GetDocShell();
2509 ScDocument& rDoc = pDocSh->GetDocument();
2510 bool bVbaEnabled = rDoc.IsInVBAMode();
2511 SCTAB nNewTab = nTab;
2512 weld::WaitObject aWait(GetViewData().GetDialogParent());
2514 while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
2515 --nNewTab;
2517 if (!rDoc.DeleteTabs(nTab, nSheets))
2518 return;
2520 if( bVbaEnabled )
2522 for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
2524 OUString sCodeName;
2525 bool bHasCodeName = rDoc.GetCodeName( nTab + aTab, sCodeName );
2526 if ( bHasCodeName )
2527 VBA_DeleteModule( *pDocSh, sCodeName );
2531 pDocSh->Broadcast( ScTablesHint( SC_TABS_DELETED, nTab, nSheets ) );
2532 if ( nNewTab >= rDoc.GetTableCount() )
2533 nNewTab = rDoc.GetTableCount() - 1;
2534 SetTabNo( nNewTab, true );
2536 pDocSh->PostPaintExtras();
2537 pDocSh->SetDocumentModified();
2539 SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
2540 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2541 pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
2542 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2545 bool ScViewFunc::DeleteTables(const vector<SCTAB> &TheTabs, bool bRecord )
2547 ScDocShell* pDocSh = GetViewData().GetDocShell();
2548 ScDocument& rDoc = pDocSh->GetDocument();
2549 bool bVbaEnabled = rDoc.IsInVBAMode();
2550 SCTAB nNewTab = TheTabs.front();
2551 weld::WaitObject aWait(GetViewData().GetDialogParent());
2552 if (bRecord && !rDoc.IsUndoEnabled())
2553 bRecord = false;
2554 if ( bVbaEnabled )
2555 bRecord = false;
2557 while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) )
2558 --nNewTab;
2560 bool bWasLinked = false;
2561 ScDocumentUniquePtr pUndoDoc;
2562 std::unique_ptr<ScRefUndoData> pUndoData;
2563 if (bRecord)
2565 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2566 SCTAB nCount = rDoc.GetTableCount();
2568 OUString aOldName;
2569 bool isFirstTab = true;
2570 for(SCTAB nTab : TheTabs)
2572 if (isFirstTab)
2574 pUndoDoc->InitUndo( rDoc, nTab,nTab, true,true ); // incl. column/fow flags
2575 isFirstTab = false;
2577 else
2578 pUndoDoc->AddUndoTab( nTab,nTab, true,true ); // incl. column/fow flags
2580 rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
2581 rDoc.GetName( nTab, aOldName );
2582 pUndoDoc->RenameTab( nTab, aOldName );
2583 if (rDoc.IsLinked(nTab))
2585 bWasLinked = true;
2586 pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
2587 rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
2588 rDoc.GetLinkTab(nTab),
2589 rDoc.GetLinkRefreshDelay(nTab) );
2591 if ( rDoc.IsScenario(nTab) )
2593 pUndoDoc->SetScenario( nTab, true );
2594 OUString aComment;
2595 Color aColor;
2596 ScScenarioFlags nScenFlags;
2597 rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
2598 pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
2599 bool bActive = rDoc.IsActiveScenario( nTab );
2600 pUndoDoc->SetActiveScenario( nTab, bActive );
2602 pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
2603 pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
2604 auto pSheetEvents = rDoc.GetSheetEvents( nTab );
2605 pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
2606 pUndoDoc->SetLayoutRTL( nTab, rDoc.IsLayoutRTL( nTab ) );
2608 if ( rDoc.IsTabProtected( nTab ) )
2609 pUndoDoc->SetTabProtection(nTab, rDoc.GetTabProtection(nTab));
2611 // Drawing-Layer is responsible for its Undo !!!
2612 // pUndoDoc->TransferDrawPage(rDoc, nTab,nTab);
2615 pUndoDoc->AddUndoTab( 0, nCount-1 ); // all Tabs for references
2617 rDoc.BeginDrawUndo(); // DeleteTab creates a SdrUndoDelPage
2619 pUndoData.reset(new ScRefUndoData( &rDoc ));
2622 bool bDelDone = false;
2624 for(int i=TheTabs.size()-1; i>=0; --i)
2626 OUString sCodeName;
2627 bool bHasCodeName = rDoc.GetCodeName( TheTabs[i], sCodeName );
2628 if (rDoc.DeleteTab(TheTabs[i]))
2630 bDelDone = true;
2631 if( bVbaEnabled && bHasCodeName )
2633 VBA_DeleteModule( *pDocSh, sCodeName );
2635 pDocSh->Broadcast( ScTablesHint( SC_TAB_DELETED, TheTabs[i] ) );
2638 if (bRecord)
2640 pDocSh->GetUndoManager()->AddUndoAction(
2641 std::make_unique<ScUndoDeleteTab>( GetViewData().GetDocShell(), TheTabs,
2642 std::move(pUndoDoc), std::move(pUndoData) ));
2645 if (bDelDone)
2647 if ( nNewTab >= rDoc.GetTableCount() )
2648 nNewTab = rDoc.GetTableCount() - 1;
2650 SetTabNo( nNewTab, true );
2652 if (bWasLinked)
2654 pDocSh->UpdateLinks(); // update Link-Manager
2655 GetViewData().GetBindings().Invalidate(SID_LINKS);
2658 pDocSh->PostPaintExtras();
2659 pDocSh->SetDocumentModified();
2661 SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
2662 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2663 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
2664 pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
2665 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2667 return bDelDone;
2670 bool ScViewFunc::RenameTable( const OUString& rName, SCTAB nTab )
2672 // order Table/Name is inverted for DocFunc
2673 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().
2674 RenameTable( nTab, rName, true, false );
2675 if (bSuccess)
2677 // the table name might be part of a formula
2678 GetViewData().GetViewShell()->UpdateInputHandler();
2680 return bSuccess;
2683 bool ScViewFunc::SetTabBgColor( const Color& rColor, SCTAB nTab )
2685 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( nTab, rColor, true, false );
2686 if (bSuccess)
2688 GetViewData().GetViewShell()->UpdateInputHandler();
2690 return bSuccess;
2693 bool ScViewFunc::SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList )
2695 bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( rUndoSetTabBgColorInfoList, false );
2696 if (bSuccess)
2698 GetViewData().GetViewShell()->UpdateInputHandler();
2700 return bSuccess;
2703 void ScViewFunc::InsertAreaLink( const OUString& rFile,
2704 const OUString& rFilter, const OUString& rOptions,
2705 const OUString& rSource )
2707 ScDocShell* pDocSh = GetViewData().GetDocShell();
2708 SCCOL nPosX = GetViewData().GetCurX();
2709 SCROW nPosY = GetViewData().GetCurY();
2710 SCTAB nTab = GetViewData().GetTabNo();
2711 ScAddress aPos( nPosX, nPosY, nTab );
2713 pDocSh->GetDocFunc().InsertAreaLink( rFile, rFilter, rOptions, rSource, ScRange(aPos), 0/*nRefresh*/, false, false );
2716 void ScViewFunc::InsertTableLink( const OUString& rFile,
2717 const OUString& rFilter, const OUString& rOptions,
2718 std::u16string_view rTabName )
2720 OUString aFilterName = rFilter;
2721 OUString aOpt = rOptions;
2722 ScDocumentLoader aLoader( rFile, aFilterName, aOpt );
2723 if (aLoader.IsError())
2724 return;
2726 ScDocShell* pSrcSh = aLoader.GetDocShell();
2727 ScDocument& rSrcDoc = pSrcSh->GetDocument();
2728 SCTAB nTab = MAXTAB+1;
2729 if (rTabName.empty()) // no name given -> first table
2730 nTab = 0;
2731 else
2733 OUString aTemp;
2734 SCTAB nCount = rSrcDoc.GetTableCount();
2735 for (SCTAB i=0; i<nCount; i++)
2737 rSrcDoc.GetName( i, aTemp );
2738 if ( aTemp == rTabName )
2739 nTab = i;
2743 if ( nTab <= MAXTAB )
2744 ImportTables( pSrcSh, 1, &nTab, true,
2745 GetViewData().GetTabNo() );
2748 // Copy/link tables from another document
2750 void ScViewFunc::ImportTables( ScDocShell* pSrcShell,
2751 SCTAB nCount, const SCTAB* pSrcTabs, bool bLink,SCTAB nTab )
2753 ScDocument& rSrcDoc = pSrcShell->GetDocument();
2755 ScDocShell* pDocSh = GetViewData().GetDocShell();
2756 ScDocument& rDoc = pDocSh->GetDocument();
2757 bool bUndo(rDoc.IsUndoEnabled());
2759 bool bError = false;
2761 if (rSrcDoc.GetDrawLayer())
2762 pDocSh->MakeDrawLayer();
2764 if (bUndo)
2765 rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
2767 SCTAB nInsCount = 0;
2768 SCTAB i;
2769 for( i=0; i<nCount; i++ )
2770 { // insert sheets first and update all references
2771 OUString aName;
2772 rSrcDoc.GetName( pSrcTabs[i], aName );
2773 rDoc.CreateValidTabName( aName );
2774 if ( !rDoc.InsertTab( nTab+i, aName ) )
2776 bError = true; // total error
2777 break; // for
2779 ++nInsCount;
2781 for (i=0; i<nCount && !bError; i++)
2783 SCTAB nSrcTab = pSrcTabs[i];
2784 SCTAB nDestTab1=nTab+i;
2785 bool bValid = pDocSh->TransferTab( *pSrcShell, nSrcTab, nDestTab1,
2786 false, false ); // no insert
2788 if (!bValid)
2790 bError = true;
2795 if (bLink)
2797 sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
2799 SfxMedium* pMed = pSrcShell->GetMedium();
2800 OUString aFileName = pMed->GetName();
2801 OUString aFilterName;
2802 if (pMed->GetFilter())
2803 aFilterName = pMed->GetFilter()->GetFilterName();
2804 OUString aOptions = ScDocumentLoader::GetOptions(*pMed);
2806 bool bWasThere = rDoc.HasLink( aFileName, aFilterName, aOptions );
2808 sal_uLong nRefresh = 0;
2809 OUString aTabStr;
2810 for (i=0; i<nInsCount; i++)
2812 rSrcDoc.GetName( pSrcTabs[i], aTabStr );
2813 rDoc.SetLink( nTab+i, ScLinkMode::NORMAL,
2814 aFileName, aFilterName, aOptions, aTabStr, nRefresh );
2817 if (!bWasThere) // Insert link only once per source document
2819 ScTableLink* pLink = new ScTableLink( pDocSh, aFileName, aFilterName, aOptions, nRefresh );
2820 pLink->SetInCreate( true );
2821 pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilterName );
2822 pLink->Update();
2823 pLink->SetInCreate( false );
2825 SfxBindings& rBindings = GetViewData().GetBindings();
2826 rBindings.Invalidate( SID_LINKS );
2830 if (bUndo)
2832 pDocSh->GetUndoManager()->AddUndoAction(
2833 std::make_unique<ScUndoImportTab>( pDocSh, nTab, nCount ) );
2836 for (i=0; i<nInsCount; i++)
2837 GetViewData().InsertTab(nTab);
2838 SetTabNo(nTab,true);
2839 pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
2840 PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras );
2842 SfxApplication* pSfxApp = SfxGetpApp();
2843 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
2844 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
2846 pDocSh->PostPaintExtras();
2847 pDocSh->PostPaintGridAll();
2848 pDocSh->SetDocumentModified();
2851 // Move/Copy table to another document
2853 void ScViewFunc::MoveTable(sal_uInt16 nDestDocNo, SCTAB nDestTab, bool bCopy,
2854 const OUString* pNewTabName, bool bContextMenu,
2855 SCTAB nContextMenuSourceTab)
2857 ScDocument& rDoc = GetViewData().GetDocument();
2858 ScDocShell* pDocShell = GetViewData().GetDocShell();
2859 ScDocShell* pDestShell = nullptr;
2860 ScTabViewShell* pDestViewSh = nullptr;
2861 bool bUndo (rDoc.IsUndoEnabled());
2862 bool bRename = pNewTabName && !pNewTabName->isEmpty();
2864 bool bNewDoc = (nDestDocNo == SC_DOC_NEW);
2865 if ( bNewDoc )
2867 nDestTab = 0; // firstly insert
2869 // execute without SfxCallMode::RECORD, because already contained in move command
2871 SfxStringItem aItem( SID_FILE_NAME, "private:factory/" + STRING_SCAPP );
2872 SfxStringItem aTarget( SID_TARGETNAME, u"_blank"_ustr );
2874 const SfxPoolItemHolder aResult(GetViewData().GetDispatcher().ExecuteList(
2875 SID_OPENDOC, SfxCallMode::API|SfxCallMode::SYNCHRON,
2876 { &aItem, &aTarget }));
2878 if (aResult)
2880 if ( auto pObjectItem = dynamic_cast<const SfxObjectItem*>(aResult.getItem()) )
2881 pDestShell = dynamic_cast<ScDocShell*>( pObjectItem->GetShell() );
2882 else if ( auto pViewFrameItem = dynamic_cast<const SfxViewFrameItem*>(aResult.getItem()))
2884 SfxViewFrame* pFrm = pViewFrameItem->GetFrame();
2885 if (pFrm)
2886 pDestShell = dynamic_cast<ScDocShell*>( pFrm->GetObjectShell() );
2888 if (pDestShell)
2889 pDestViewSh = pDestShell->GetBestViewShell();
2892 else
2893 pDestShell = ScDocShell::GetShellByNum( nDestDocNo );
2895 if (!pDestShell)
2897 OSL_FAIL("Destination document not found !!!");
2898 return;
2901 ScMarkData& rMark = GetViewData().GetMarkData();
2902 if (bRename && rMark.GetSelectCount() != 1)
2904 // Custom sheet name is provided, but more than one sheet is selected.
2905 // We don't support this scenario at the moment.
2906 return;
2909 ScDocument& rDestDoc = pDestShell->GetDocument();
2911 if (&rDestDoc != &rDoc)
2913 if (bNewDoc)
2915 while (rDestDoc.GetTableCount() > 1)
2916 rDestDoc.DeleteTab(0);
2917 rDestDoc.RenameTab( 0, u"______42_____"_ustr );
2920 SCTAB nTabCount = rDoc.GetTableCount();
2921 SCTAB nTabSelCount = rMark.GetSelectCount();
2923 vector<SCTAB> TheTabs;
2925 for(SCTAB i=0; i<nTabCount; ++i)
2927 if(rMark.GetTableSelect(i))
2929 OUString aTabName;
2930 rDoc.GetName( i, aTabName);
2931 TheTabs.push_back(i);
2932 for(SCTAB j=i+1;j<nTabCount;j++)
2934 if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
2936 rDoc.GetName( j, aTabName);
2937 TheTabs.push_back(j);
2938 i=j;
2940 else break;
2945 GetFrameWin()->EnterWait();
2947 if (rDoc.GetDrawLayer())
2948 pDestShell->MakeDrawLayer();
2950 if (!bNewDoc && bUndo)
2951 rDestDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
2953 bool bValid = true;
2954 if(nDestTab==SC_TAB_APPEND)
2955 nDestTab=rDestDoc.GetTableCount();
2956 SCTAB nDestTab1=nDestTab;
2957 ScClipParam aParam;
2958 for( size_t j=0; j<TheTabs.size(); ++j, ++nDestTab1 )
2959 { // insert sheets first and update all references
2960 OUString aName;
2961 if (bRename)
2962 aName = *pNewTabName;
2963 else
2964 rDoc.GetName( TheTabs[j], aName );
2966 rDestDoc.CreateValidTabName( aName );
2967 if ( !rDestDoc.InsertTab( nDestTab1, aName ) )
2969 bValid = false; // total error
2970 break; // for
2972 ScRange aRange( 0, 0, TheTabs[j], rDoc.MaxCol(), rDoc.MaxRow(), TheTabs[j] );
2973 aParam.maRanges.push_back(aRange);
2975 rDoc.SetClipParam(aParam);
2976 if ( bValid )
2978 nDestTab1 = nDestTab;
2979 for(SCTAB nTab : TheTabs)
2981 bValid = pDestShell->TransferTab( *pDocShell, nTab, nDestTab1, false, false );
2982 nDestTab1++;
2985 if (!bNewDoc && bUndo)
2987 OUString sName;
2988 rDestDoc.GetName(nDestTab, sName);
2989 pDestShell->GetUndoManager()->AddUndoAction(
2990 std::make_unique<ScUndoImportTab>( pDestShell, nDestTab,
2991 static_cast<SCTAB>(TheTabs.size())));
2994 else
2996 pDestShell->GetUndoManager()->Clear();
2999 GetFrameWin()->LeaveWait();
3001 if (!bValid)
3003 ErrorMessage(STR_TABINSERT_ERROR);
3004 return;
3007 if (!bCopy)
3009 if(nTabCount!=nTabSelCount)
3010 DeleteTables(TheTabs); // incl. Paint & Undo
3011 else
3012 ErrorMessage(STR_TABREMOVE_ERROR);
3015 if (bNewDoc)
3017 // ChartListenerCollection must be updated before DeleteTab
3018 if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
3019 rDestDoc.UpdateChartListenerCollection();
3021 SCTAB nNumTabsInserted = static_cast<SCTAB>(TheTabs.size());
3022 pDestShell->Broadcast( ScTablesHint( SC_TABS_INSERTED, 0, nNumTabsInserted ) );
3024 rDestDoc.DeleteTab( nNumTabsInserted ); // old first table
3025 pDestShell->Broadcast( ScTablesHint( SC_TAB_DELETED, nNumTabsInserted ) );
3027 if (pDestViewSh)
3029 // Make sure to clear the cached page view after sheet
3030 // deletion, which still points to the sdr page belonging to
3031 // the deleted sheet.
3032 SdrView* pSdrView = pDestViewSh->GetScDrawView();
3033 if (pSdrView)
3034 pSdrView->ClearPageView();
3036 pDestViewSh->TabChanged(); // pages on the drawing layer
3038 pDestShell->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB,
3039 PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left |
3040 PaintPartFlags::Extras | PaintPartFlags::Size );
3041 // PaintPartFlags::Size for outline
3043 else
3045 pDestShell->Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestTab ) );
3046 pDestShell->PostPaintExtras();
3047 pDestShell->PostPaintGridAll();
3050 TheTabs.clear();
3052 pDestShell->SetDocumentModified();
3053 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3055 else
3057 // Move or copy within the same document.
3058 SCTAB nTabCount = rDoc.GetTableCount();
3060 unique_ptr< vector<SCTAB> > pSrcTabs(new vector<SCTAB>);
3061 unique_ptr< vector<SCTAB> > pDestTabs(new vector<SCTAB>);
3062 unique_ptr< vector<OUString> > pTabNames(new vector<OUString>);
3063 unique_ptr< vector<OUString> > pDestNames;
3064 pSrcTabs->reserve(nTabCount);
3065 pDestTabs->reserve(nTabCount);
3066 pTabNames->reserve(nTabCount);
3067 OUString aDestName;
3069 if (bContextMenu)
3071 OUString aTabName;
3072 rDoc.GetName(nContextMenuSourceTab, aTabName);
3073 pTabNames->push_back(aTabName);
3075 else
3077 for(SCTAB i=0;i<nTabCount;i++)
3079 if(rMark.GetTableSelect(i))
3081 OUString aTabName;
3082 rDoc.GetName( i, aTabName);
3083 pTabNames->push_back(aTabName);
3085 for(SCTAB j=i+1;j<nTabCount;j++)
3087 if((!rDoc.IsVisible(j)) && rDoc.IsScenario(j))
3089 rDoc.GetName( j, aTabName);
3090 pTabNames->push_back(aTabName);
3091 i=j;
3093 else break;
3099 if (bCopy && bUndo)
3100 rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions
3102 rDoc.GetName( nDestTab, aDestName);
3103 SCTAB nDestTab1=nDestTab;
3104 SCTAB nMovTab=0;
3105 for (size_t j = 0, n = pTabNames->size(); j < n; ++j)
3107 nTabCount = rDoc.GetTableCount();
3108 const OUString& rStr = (*pTabNames)[j];
3109 if(!rDoc.GetTable(rStr,nMovTab))
3111 nMovTab=nTabCount;
3113 if(!rDoc.GetTable(aDestName,nDestTab1))
3115 nDestTab1=nTabCount;
3117 pDocShell->MoveTable( nMovTab, nDestTab1, bCopy, false ); // Undo is here
3119 // tdf#43175 - Adjust chart references on every copied sheet
3120 if (bCopy)
3122 // New position of source table after moving
3123 SCTAB nSrcTab = (nDestTab1 <= nMovTab) ? nMovTab + 1 : nMovTab;
3124 //#i29848# adjust references to data on the copied sheet
3125 ScChartHelper::AdjustRangesOfChartsOnDestinationPage(rDoc, rDestDoc, nSrcTab,
3126 nDestTab1);
3129 if(bCopy && rDoc.IsScenario(nMovTab))
3131 OUString aComment;
3132 Color aColor;
3133 ScScenarioFlags nFlags;
3135 rDoc.GetScenarioData(nMovTab, aComment,aColor, nFlags);
3136 rDoc.SetScenario(nDestTab1,true);
3137 rDoc.SetScenarioData(nDestTab1,aComment,aColor,nFlags);
3138 bool bActive = rDoc.IsActiveScenario(nMovTab );
3139 rDoc.SetActiveScenario( nDestTab1, bActive );
3140 bool bVisible=rDoc.IsVisible(nMovTab);
3141 rDoc.SetVisible(nDestTab1,bVisible );
3144 pSrcTabs->push_back(nMovTab);
3146 if(!bCopy)
3148 if(!rDoc.GetTable(rStr,nDestTab1))
3150 nDestTab1=nTabCount;
3154 pDestTabs->push_back(nDestTab1);
3157 // Rename must be done after all sheets have been moved.
3158 if (bRename)
3160 pDestNames.reset(new vector<OUString>);
3161 size_t n = pDestTabs->size();
3162 pDestNames->reserve(n);
3163 for (size_t j = 0; j < n; ++j)
3165 SCTAB nRenameTab = (*pDestTabs)[j];
3166 OUString aTabName = *pNewTabName;
3167 rDoc.CreateValidTabName( aTabName );
3168 pDestNames->push_back(aTabName);
3169 rDoc.RenameTab(nRenameTab, aTabName);
3172 else
3173 // No need to keep this around when we are not renaming.
3174 pTabNames.reset();
3176 SCTAB nTab = GetViewData().GetTabNo();
3178 if (comphelper::LibreOfficeKit::isActive() && !pSrcTabs->empty())
3180 ScModelObj* pModel = pDocShell->GetModel();
3181 SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
3184 if (bUndo)
3186 if (bCopy)
3188 pDocShell->GetUndoManager()->AddUndoAction(
3189 std::make_unique<ScUndoCopyTab>(
3190 pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pDestNames)));
3192 else
3194 pDocShell->GetUndoManager()->AddUndoAction(
3195 std::make_unique<ScUndoMoveTab>(
3196 pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pTabNames), std::move(pDestNames)));
3200 if (bContextMenu)
3202 for (SCTAB i = 0; i < nTabCount; i++)
3204 if (rMark.GetTableSelect(i))
3205 SetTabNo(i, true);
3208 else
3210 SCTAB nNewTab = nDestTab;
3211 if (nNewTab == SC_TAB_APPEND)
3212 nNewTab = rDoc.GetTableCount() - 1;
3213 else if (!bCopy && nTab < nDestTab)
3214 nNewTab--;
3216 SetTabNo(nNewTab, true);
3221 void ScViewFunc::ShowTable( const std::vector<OUString>& rNames )
3223 ScDocShell* pDocSh = GetViewData().GetDocShell();
3224 ScDocument& rDoc = pDocSh->GetDocument();
3225 bool bUndo(rDoc.IsUndoEnabled());
3227 std::vector<SCTAB> undoTabs;
3228 SCTAB nPos = 0;
3230 bool bFound(false);
3232 for (const OUString& aName : rNames)
3234 if (rDoc.GetTable(aName, nPos))
3236 rDoc.SetVisible( nPos, true );
3237 SetTabNo( nPos, true );
3238 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3239 if (!bFound)
3240 bFound = true;
3241 if (bUndo)
3242 undoTabs.push_back(nPos);
3245 if (bFound)
3247 if (bUndo)
3249 pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, std::move(undoTabs), true ) );
3251 pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
3252 pDocSh->SetDocumentModified();
3256 void ScViewFunc::HideTable( const ScMarkData& rMark, SCTAB nTabToSelect )
3258 ScDocShell* pDocSh = GetViewData().GetDocShell();
3259 ScDocument& rDoc = pDocSh->GetDocument();
3260 bool bUndo(rDoc.IsUndoEnabled());
3261 SCTAB nVisible = 0;
3262 SCTAB nTabCount = rDoc.GetTableCount();
3264 SCTAB nTabSelCount = rMark.GetSelectCount();
3266 // check to make sure we won't hide all sheets. we need at least one visible at all times.
3267 for ( SCTAB i=0; i < nTabCount && nVisible <= nTabSelCount ; i++ )
3268 if (rDoc.IsVisible(i))
3269 ++nVisible;
3271 if (nVisible <= nTabSelCount)
3272 return;
3274 std::vector<SCTAB> undoTabs;
3276 // need to take a copy of selectedtabs since it is modified in the loop
3277 const ScMarkData::MarkedTabsType selectedTabs = rMark.GetSelectedTabs();
3278 for (const SCTAB& nTab : selectedTabs)
3280 if (rDoc.IsVisible( nTab ))
3282 rDoc.SetVisible( nTab, false );
3283 // Update views
3284 pDocSh->Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
3285 SetTabNo( nTab, true );
3286 // Store for undo
3287 if (bUndo)
3288 undoTabs.push_back(nTab);
3292 if (nTabToSelect != -1)
3293 SetTabNo(nTabToSelect);
3295 if (bUndo)
3297 pDocSh->GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( pDocSh, std::move(undoTabs), false ) );
3300 // Update views
3301 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3302 pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
3303 pDocSh->SetDocumentModified();
3306 void ScViewFunc::InsertSpecialChar( const OUString& rStr, const vcl::Font& rFont )
3308 ScEditableTester aTester( this );
3309 if (!aTester.IsEditable())
3311 ErrorMessage(aTester.GetMessageId());
3312 return;
3315 const sal_Unicode* pChar = rStr.getStr();
3316 ScTabViewShell* pViewShell = GetViewData().GetViewShell();
3317 SvxFontItem aFontItem( rFont.GetFamilyType(),
3318 rFont.GetFamilyName(),
3319 rFont.GetStyleName(),
3320 rFont.GetPitch(),
3321 rFont.GetCharSet(),
3322 ATTR_FONT );
3324 // if string contains WEAK characters, set all fonts
3325 SvtScriptType nScript;
3326 ScDocument& rDoc = GetViewData().GetDocument();
3327 if ( rDoc.HasStringWeakCharacters( rStr ) )
3328 nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX;
3329 else
3330 nScript = rDoc.GetStringScriptType( rStr );
3332 SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, pViewShell->GetPool() );
3333 aSetItem.PutItemForScriptType( nScript, aFontItem );
3334 ApplyUserItemSet( aSetItem.GetItemSet() );
3336 while ( *pChar )
3337 pViewShell->TabKeyInput( KeyEvent( *(pChar++), vcl::KeyCode() ) );
3340 void ScViewFunc::UpdateLineAttrs( SvxBorderLine& rLine,
3341 const SvxBorderLine* pDestLine,
3342 const SvxBorderLine* pSrcLine,
3343 bool bColor )
3345 if ( !(pSrcLine && pDestLine) )
3346 return;
3348 if ( bColor )
3350 rLine.SetColor ( pSrcLine->GetColor() );
3351 rLine.SetBorderLineStyle(pDestLine->GetBorderLineStyle());
3352 rLine.SetWidth ( pDestLine->GetWidth() );
3354 else
3356 rLine.SetColor ( pDestLine->GetColor() );
3357 rLine.SetBorderLineStyle(pSrcLine->GetBorderLineStyle());
3358 rLine.SetWidth ( pSrcLine->GetWidth() );
3362 #define SET_LINE_ATTRIBUTES(LINE,BOXLINE) \
3363 pBoxLine = aBoxItem.Get##LINE(); \
3364 if ( pBoxLine ) \
3366 if ( pLine ) \
3368 UpdateLineAttrs( aLine, pBoxLine, pLine, bColorOnly ); \
3369 aBoxItem.SetLine( &aLine, BOXLINE ); \
3371 else \
3372 aBoxItem.SetLine( nullptr, BOXLINE ); \
3375 void ScViewFunc::SetSelectionFrameLines( const SvxBorderLine* pLine,
3376 bool bColorOnly )
3378 // Not editable only due to a matrix? Attribute is ok anyhow.
3379 bool bOnlyNotBecauseOfMatrix;
3380 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
3382 ErrorMessage(STR_PROTECTIONERR);
3383 return;
3386 ScDocument& rDoc = GetViewData().GetDocument();
3387 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
3388 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
3389 ScDocShell* pDocSh = GetViewData().GetDocShell();
3390 const ScPatternAttr* pSelAttrs = GetSelectionPattern();
3391 const SfxItemSet& rSelItemSet = pSelAttrs->GetItemSet();
3393 const SfxPoolItem* pBorderAttr = nullptr;
3394 SfxItemState eItemState = rSelItemSet.GetItemState( ATTR_BORDER, true, &pBorderAttr );
3396 const SfxPoolItem* pTLBRItem = nullptr;
3397 SfxItemState eTLBRState = rSelItemSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem );
3399 const SfxPoolItem* pBLTRItem = nullptr;
3400 SfxItemState eBLTRState = rSelItemSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem );
3402 // any of the lines visible?
3403 if( !((eItemState != SfxItemState::DEFAULT) || (eTLBRState != SfxItemState::DEFAULT) || (eBLTRState != SfxItemState::DEFAULT)) )
3404 return;
3406 // none of the lines don't care?
3407 if( (eItemState != SfxItemState::INVALID) && (eTLBRState != SfxItemState::INVALID) && (eBLTRState != SfxItemState::INVALID) )
3409 SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aOldSet( *rDoc.GetPool() );
3410 SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aNewSet( *rDoc.GetPool() );
3412 SvxBorderLine aLine;
3414 if( pBorderAttr )
3416 const SvxBorderLine* pBoxLine = nullptr;
3417 SvxBoxItem aBoxItem( *static_cast<const SvxBoxItem*>(pBorderAttr) );
3418 SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
3420 // here pBoxLine is used
3421 SET_LINE_ATTRIBUTES(Top,SvxBoxItemLine::TOP)
3422 SET_LINE_ATTRIBUTES(Bottom,SvxBoxItemLine::BOTTOM)
3423 SET_LINE_ATTRIBUTES(Left,SvxBoxItemLine::LEFT)
3424 SET_LINE_ATTRIBUTES(Right,SvxBoxItemLine::RIGHT)
3426 aBoxInfoItem.SetLine( aBoxItem.GetTop(), SvxBoxInfoItemLine::HORI );
3427 aBoxInfoItem.SetLine( aBoxItem.GetLeft(), SvxBoxInfoItemLine::VERT );
3428 aBoxInfoItem.ResetFlags(); // set Lines to Valid
3430 aOldSet.Put( *pBorderAttr );
3431 aNewSet.Put( aBoxItem );
3432 aNewSet.Put( aBoxInfoItem );
3435 if( pTLBRItem && static_cast<const SvxLineItem*>(pTLBRItem)->GetLine() )
3437 SvxLineItem aTLBRItem( *static_cast<const SvxLineItem*>(pTLBRItem) );
3438 UpdateLineAttrs( aLine, aTLBRItem.GetLine(), pLine, bColorOnly );
3439 aTLBRItem.SetLine( &aLine );
3440 aOldSet.Put( *pTLBRItem );
3441 aNewSet.Put( aTLBRItem );
3444 if( pBLTRItem && static_cast<const SvxLineItem*>(pBLTRItem)->GetLine() )
3446 SvxLineItem aBLTRItem( *static_cast<const SvxLineItem*>(pBLTRItem) );
3447 UpdateLineAttrs( aLine, aBLTRItem.GetLine(), pLine, bColorOnly );
3448 aBLTRItem.SetLine( &aLine );
3449 aOldSet.Put( *pBLTRItem );
3450 aNewSet.Put( aBLTRItem );
3453 ApplyAttributes( aNewSet, aOldSet );
3455 else // if ( eItemState == SfxItemState::INVALID )
3457 aFuncMark.MarkToMulti();
3458 rDoc.ApplySelectionLineStyle( aFuncMark, pLine, bColorOnly );
3461 const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
3462 SCCOL nStartCol = aMarkRange.aStart.Col();
3463 SCROW nStartRow = aMarkRange.aStart.Row();
3464 SCTAB nStartTab = aMarkRange.aStart.Tab();
3465 SCCOL nEndCol = aMarkRange.aEnd.Col();
3466 SCROW nEndRow = aMarkRange.aEnd.Row();
3467 SCTAB nEndTab = aMarkRange.aEnd.Tab();
3468 pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
3469 nEndCol, nEndRow, nEndTab,
3470 PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
3472 pDocSh->UpdateOle(GetViewData());
3473 pDocSh->SetDocumentModified();
3476 #undef SET_LINE_ATTRIBUTES
3478 void ScViewFunc::SetValidation( const ScValidationData& rNew )
3480 ScDocument& rDoc = GetViewData().GetDocument();
3481 sal_uInt32 nIndex = rDoc.AddValidationEntry(rNew); // for it there is no Undo
3482 SfxUInt32Item aItem( ATTR_VALIDDATA, nIndex );
3484 ApplyAttr( aItem ); // with Paint and Undo...
3487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */