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