1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code 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 <address.hxx>
21 #include <config_features.h>
22
23 #include <scitems.hxx>
24
25 #include <sfx2/app.hxx>
26 #include <svx/algitem.hxx>
27 #include <editeng/boxitem.hxx>
28 #include <editeng/editobj.hxx>
29 #include <editeng/langitem.hxx>
30 #include <editeng/justifyitem.hxx>
31 #include <o3tl/unit_conversion.hxx>
32 #include <sfx2/bindings.hxx>
33 #include <svl/numformat.hxx>
34 #include <svl/zforlist.hxx>
35 #include <svl/zformat.hxx>
36 #include <vcl/weld.hxx>
37 #include <vcl/virdev.hxx>
38 #include <stdlib.h>
39 #include <unotools/charclass.hxx>
40 #include <vcl/uitest/logger.hxx>
41 #include <vcl/uitest/eventdescription.hxx>
42 #include <osl/diagnose.h>
43
44 #include <viewfunc.hxx>
45 #include <tabvwsh.hxx>
46 #include <docsh.hxx>
47 #include <attrib.hxx>
48 #include <patattr.hxx>
49 #include <sc.hrc>
50 #include <undocell.hxx>
51 #include <undoblk.hxx>
52 #include <refundo.hxx>
53 #include <olinetab.hxx>
54 #include <rangenam.hxx>
55 #include <globstr.hrc>
56 #include <global.hxx>
57 #include <stlsheet.hxx>
58 #include <editutil.hxx>
59 #include <formulacell.hxx>
60 #include <scresid.hxx>
61 #include <inputhdl.hxx>
62 #include <scmod.hxx>
63 #include <inputopt.hxx>
64 #include <compiler.hxx>
65 #include <docfunc.hxx>
66 #include <appoptio.hxx>
67 #include <sizedev.hxx>
68 #include <editable.hxx>
69 #include <scui_def.hxx>
70 #include <funcdesc.hxx>
71 #include <docuno.hxx>
72 #include <cellsuno.hxx>
73 #include <tokenarray.hxx>
74 #include <rowheightcontext.hxx>
75 #include <comphelper/lok.hxx>
76 #include <conditio.hxx>
77 #include <columnspanset.hxx>
78 #include <stringutil.hxx>
79 #include <SparklineList.hxx>
80
81 #include <memory>
82
ShowFilteredRows(ScDocument & rDoc,SCTAB nTab,SCCOLROW nStartNo,SCCOLROW nEndNo,bool bShow)83 static void ShowFilteredRows(ScDocument& rDoc, SCTAB nTab, SCCOLROW nStartNo, SCCOLROW nEndNo,
84 bool bShow)
85 {
86 SCROW nFirstRow = nStartNo;
87 SCROW nLastRow = nStartNo;
88 do
89 {
90 if (!rDoc.RowFiltered(nFirstRow, nTab, nullptr, &nLastRow))
91 rDoc.ShowRows(nFirstRow, nLastRow < nEndNo ? nLastRow : nEndNo, nTab, bShow);
92 nFirstRow = nLastRow + 1;
93 } while (nFirstRow <= nEndNo);
94 }
95
lcl_PostRepaintCondFormat(const ScConditionalFormat * pCondFmt,ScDocShell * pDocSh)96 static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell *pDocSh )
97 {
98 if( pCondFmt )
99 {
100 const ScRangeList& rRanges = pCondFmt->GetRange();
101
102 pDocSh->PostPaint( rRanges, PaintPartFlags::All );
103 }
104 }
105
lcl_PostRepaintSparkLine(sc::SparklineList * pSparklineList,const ScRange & rRange,ScDocShell * pDocSh)106 static void lcl_PostRepaintSparkLine(sc::SparklineList* pSparklineList, const ScRange& rRange,
107 ScDocShell* pDocSh)
108 {
109 if (pSparklineList)
110 {
111 for (auto& rSparkLineGroup : pSparklineList->getSparklineGroups())
112 {
113 for (auto& rSparkline : pSparklineList->getSparklinesFor(rSparkLineGroup))
114 {
115 if (rSparkline->getInputRange().Contains(rRange))
116 {
117 pDocSh->PostPaint(
118 ScRange(rSparkline->getColumn(), rSparkline->getRow(), rRange.aStart.Tab()),
119 PaintPartFlags::All, SC_PF_TESTMERGE);
120 }
121 }
122 }
123 }
124 }
125
ScViewFunc(vcl::Window * pParent,ScDocShell & rDocSh,ScTabViewShell * pViewShell)126 ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
127 ScTabView( pParent, rDocSh, pViewShell ),
128 bFormatValid( false )
129 {
130 }
131
~ScViewFunc()132 ScViewFunc::~ScViewFunc()
133 {
134 }
135
136 namespace {
137
138 struct FormulaProcessingContext
139 {
140 std::shared_ptr<ScAddress> aPos;
141 std::shared_ptr<ScCompiler> aComp;
142 std::shared_ptr<ScDocShellModificator> aModificator;
143 std::shared_ptr<ScTokenArray> pArr;
144 std::shared_ptr<ScTokenArray> pArrFirst;
145
146 const EditTextObject* pData;
147 ScMarkData aMark;
148 ScViewFunc& rViewFunc;
149
150 OUString aCorrectedFormula;
151 OUString aFormula;
152 OUString aString;
153
154 SCCOL nCol;
155 SCROW nRow;
156 SCTAB nTab;
157
158 bool bMatrixExpand;
159 bool bNumFmtChanged;
160 bool bRecord;
161
GetViewData__anonf45594590111::FormulaProcessingContext162 ScViewData& GetViewData() const
163 {
164 return rViewFunc.GetViewData();
165 }
166
GetDocFunc__anonf45594590111::FormulaProcessingContext167 ScDocFunc& GetDocFunc() const
168 {
169 return GetViewData().GetDocFunc();
170 }
171
GetDoc__anonf45594590111::FormulaProcessingContext172 ScDocument& GetDoc() const
173 {
174 return GetViewData().GetDocument();
175 }
176 };
177
collectUIInformation(std::map<OUString,OUString> && aParameters,const OUString & rAction)178 void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
179 {
180 EventDescription aDescription;
181 aDescription.aID = "grid_window";
182 aDescription.aAction = rAction;
183 aDescription.aParameters = std::move(aParameters);
184 aDescription.aParent = "MainWindow";
185 aDescription.aKeyWord = "ScGridWinUIObject";
186
187 UITestLogger::getInstance().logEvent(aDescription);
188 }
189
190 }
191
StartFormatArea()192 void ScViewFunc::StartFormatArea()
193 {
194 // anything to do?
195 if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
196 return;
197
198 // start only with single cell (marked or cursor position)
199 ScRange aMarkRange;
200 bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
201 if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
202 bOk = false;
203
204 if (bOk)
205 {
206 bFormatValid = true;
207 aFormatSource = aMarkRange.aStart;
208 aFormatArea = ScRange( aFormatSource );
209 }
210 else
211 bFormatValid = false; // discard old range
212 }
213
TestFormatArea(SCCOL nCol,SCROW nRow,SCTAB nTab,bool bAttrChanged)214 bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
215 {
216 // anything to do?
217 if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
218 return false;
219
220 // Test: treat input with numberformat (bAttrChanged) always as new Attribute
221 // (discard old Area ). If not wanted, discard if-statement
222 if ( bAttrChanged )
223 {
224 StartFormatArea();
225 return false;
226 }
227
228 //! Test if cell empty ???
229
230 bool bFound = false;
231 ScRange aNewRange = aFormatArea;
232 if ( bFormatValid && nTab == aFormatSource.Tab() )
233 {
234 if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
235 {
236 // within range?
237 if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
238 {
239 bFound = true; // do not change range
240 }
241 // left ?
242 if ( nCol+1 == aFormatArea.aStart.Col() )
243 {
244 bFound = true;
245 aNewRange.aStart.SetCol( nCol );
246 }
247 // right ?
248 if ( nCol == aFormatArea.aEnd.Col()+1 )
249 {
250 bFound = true;
251 aNewRange.aEnd.SetCol( nCol );
252 }
253 }
254 if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
255 {
256 // top ?
257 if ( nRow+1 == aFormatArea.aStart.Row() )
258 {
259 bFound = true;
260 aNewRange.aStart.SetRow( nRow );
261 }
262 // bottom ?
263 if ( nRow == aFormatArea.aEnd.Row()+1 )
264 {
265 bFound = true;
266 aNewRange.aEnd.SetRow( nRow );
267 }
268 }
269 }
270
271 if (bFound)
272 aFormatArea = aNewRange; // extend
273 else
274 bFormatValid = false; // outside of range -> break
275
276 return bFound;
277 }
278
DoAutoAttributes(SCCOL nCol,SCROW nRow,SCTAB nTab,bool bAttrChanged)279 void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
280 bool bAttrChanged )
281 {
282 ScDocShell* pDocSh = GetViewData().GetDocShell();
283 ScDocument& rDoc = pDocSh->GetDocument();
284
285 const ScPatternAttr* pSource = rDoc.GetPattern(
286 aFormatSource.Col(), aFormatSource.Row(), nTab );
287 if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
288 {
289 ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
290 ScMarkData aMark(rDoc.GetSheetLimits());
291 aMark.SetMarkArea( aRange );
292
293 ScDocFunc &rFunc = GetViewData().GetDocFunc();
294
295 // pOldPattern is only valid until call to ApplyAttributes!
296 const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
297 const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
298 if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
299 rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );
300
301 rFunc.ApplyAttributes( aMark, *pSource, false );
302 }
303
304 if ( bAttrChanged ) // value entered with number format?
305 aFormatSource.Set( nCol, nRow, nTab ); // then set a new source
306 }
307
308 // additional routines
309
setupSizeDeviceProviderForColWidth(ScSizeDeviceProvider & rProv,Fraction & rZoomX,Fraction & rZoomY,double & rPPTX,double & rPPTY)310 void ScViewData::setupSizeDeviceProviderForColWidth(ScSizeDeviceProvider& rProv, Fraction& rZoomX, Fraction& rZoomY, double& rPPTX, double &rPPTY)
311 {
312 if (rProv.IsPrinter())
313 {
314 rPPTX = rProv.GetPPTX();
315 rPPTY = rProv.GetPPTY();
316 rZoomX = rZoomY = Fraction(1, 1);
317 }
318 else
319 {
320 rPPTX = GetPPTX();
321 rPPTY = GetPPTY();
322 rZoomX = GetZoomX();
323 rZoomY = GetZoomY();
324 }
325 }
326
GetOptimalColWidth(SCCOL nCol,SCTAB nTab,bool bFormula)327 sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
328 {
329 ScDocShell* pDocSh = GetViewData().GetDocShell();
330 ScDocument& rDoc = pDocSh->GetDocument();
331 ScMarkData& rMark = GetViewData().GetMarkData();
332
333 ScSizeDeviceProvider aProv(pDocSh);
334
335 Fraction aZoomX, aZoomY;
336 double nPPTX, nPPTY;
337 GetViewData().setupSizeDeviceProviderForColWidth(aProv, aZoomX, aZoomY, nPPTX, nPPTY);
338
339 sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
340 nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
341 return nTwips;
342 }
343
SelectionEditable(bool * pOnlyNotBecauseOfMatrix)344 bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
345 {
346 bool bRet;
347 ScDocument& rDoc = GetViewData().GetDocument();
348 ScMarkData& rMark = GetViewData().GetMarkData();
349 if (rMark.IsMarked() || rMark.IsMultiMarked())
350 bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
351 else
352 {
353 SCCOL nCol = GetViewData().GetCurX();
354 SCROW nRow = GetViewData().GetCurY();
355 SCTAB nTab = GetViewData().GetTabNo();
356 bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
357 pOnlyNotBecauseOfMatrix );
358 }
359 return bRet;
360 }
361
lcl_FunctionKnown(sal_uInt16 nOpCode)362 static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
363 {
364 const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
365 if ( pFuncList )
366 {
367 sal_uLong nCount = pFuncList->GetCount();
368 for (sal_uLong i=0; i<nCount; i++)
369 if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
370 return true;
371 }
372 return false;
373 }
374
lcl_AddFunction(ScAppOptions & rAppOpt,sal_uInt16 nOpCode)375 static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
376 {
377 sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
378 sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
379 sal_uInt16 nPos;
380 for (nPos=0; nPos<nOldCount; nPos++)
381 if (pOldList[nPos] == nOpCode) // is the function already in the list?
382 {
383 if ( nPos == 0 )
384 return false; // already at the top -> no change
385
386 // count doesn't change, so the original array is modified
387
388 for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
389 pOldList[nCopy] = pOldList[nCopy-1];
390 pOldList[0] = nOpCode;
391
392 return true; // list has changed
393 }
394
395 if ( !lcl_FunctionKnown( nOpCode ) )
396 return false; // not in function list -> no change
397
398 sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
399 sal_uInt16 nNewList[LRU_MAX];
400 nNewList[0] = nOpCode;
401 for (nPos=1; nPos<nNewCount; nPos++)
402 nNewList[nPos] = pOldList[nPos-1];
403 rAppOpt.SetLRUFuncList( nNewList, nNewCount );
404
405 return true; // list has changed
406 }
407
408 namespace HelperNotifyChanges
409 {
NotifyIfChangesListeners(const ScDocShell & rDocShell,const ScMarkData & rMark,SCCOL nCol,SCROW nRow,const OUString & rType=u"cell-change"_ustr)410 static void NotifyIfChangesListeners(const ScDocShell &rDocShell, const ScMarkData& rMark,
411 SCCOL nCol, SCROW nRow, const OUString& rType = u"cell-change"_ustr)
412 {
413 ScModelObj* pModelObj = rDocShell.GetModel();
414
415 ScRangeList aChangeRanges;
416 for (const auto& rTab : rMark)
417 aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );
418
419 if (getMustPropagateChangesModel(pModelObj))
420 Notify(*pModelObj, aChangeRanges, rType);
421 else
422 {
423 Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
424 ? u"data-area-invalidate"_ustr : u"data-area-extend"_ustr);
425 }
426 }
427 }
428
429 namespace
430 {
431 class AutoCorrectQuery : public weld::MessageDialogController
432 {
433 private:
434 std::unique_ptr<weld::TextView> m_xError;
435 public:
AutoCorrectQuery(weld::Window * pParent,const OUString & rFormula)436 AutoCorrectQuery(weld::Window* pParent, const OUString& rFormula)
437 : weld::MessageDialogController(pParent, u"modules/scalc/ui/warnautocorrect.ui"_ustr, u"WarnAutoCorrect"_ustr, u"grid"_ustr)
438 , m_xError(m_xBuilder->weld_text_view(u"error"_ustr))
439 {
440 m_xDialog->set_default_response(RET_YES);
441
442 const int nMaxWidth = m_xError->get_approximate_digit_width() * 65;
443 const int nMaxHeight = m_xError->get_height_rows(6);
444 m_xError->set_size_request(nMaxWidth, nMaxHeight);
445
446 m_xError->set_text(rFormula);
447 }
448 };
449 }
450 namespace
451 {
452 void runAutoCorrectQueryAsync(std::shared_ptr<FormulaProcessingContext> context);
453
performAutoFormatAndUpdate(std::u16string_view rString,const ScMarkData & rMark,SCCOL nCol,SCROW nRow,SCTAB nTab,bool bNumFmtChanged,bool bRecord,const std::shared_ptr<ScDocShellModificator> & pModificator,ScViewFunc & rViewFunc)454 void performAutoFormatAndUpdate(std::u16string_view rString, const ScMarkData& rMark, SCCOL nCol,
455 SCROW nRow, SCTAB nTab, bool bNumFmtChanged, bool bRecord,
456 const std::shared_ptr<ScDocShellModificator>& pModificator,
457 ScViewFunc& rViewFunc)
458 {
459 bool bAutoFormat = rViewFunc.TestFormatArea(nCol, nRow, nTab, bNumFmtChanged);
460
461 if (bAutoFormat)
462 rViewFunc.DoAutoAttributes(nCol, nRow, nTab, bNumFmtChanged);
463
464 ScViewData& rViewData = rViewFunc.GetViewData();
465 ScDocShell* pDocSh = rViewData.GetDocShell();
466 pDocSh->UpdateOle(rViewData);
467
468 const OUString aType(rString.empty() ? u"delete-content" : u"cell-change");
469 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
470
471 if (bRecord)
472 {
473 ScDocFunc &rFunc = rViewData.GetDocFunc();
474 rFunc.EndListAction();
475 }
476
477 pModificator->SetDocumentModified();
478 ScDocument& rDoc = rViewData.GetDocument();
479 lcl_PostRepaintCondFormat(rDoc.GetCondFormat(nCol, nRow, nTab), pDocSh);
480 lcl_PostRepaintSparkLine(rDoc.GetSparklineList(nTab), ScRange(nCol, nRow, nTab), pDocSh);
481 }
482
finalizeFormulaProcessing(std::shared_ptr<FormulaProcessingContext> context)483 void finalizeFormulaProcessing(std::shared_ptr<FormulaProcessingContext> context)
484 {
485 // to be used in multiple tabs, the formula must be compiled anew
486 // via ScFormulaCell copy-ctor because of RangeNames,
487 // the same code-array for all cells is not possible.
488 // If the array has an error, (it) must be RPN-erased in the newly generated
489 // cells and the error be set explicitly, so that
490 // via FormulaCell copy-ctor and Interpreter it will be, when possible,
491 // ironed out again, too intelligent... e.g.: =1))
492 FormulaError nError = context->pArr->GetCodeError();
493 if ( nError == FormulaError::NONE )
494 {
495 // update list of recent functions with all functions that
496 // are not within parentheses
497
498 ScModule* pScMod = SC_MOD();
499 ScAppOptions aAppOpt = pScMod->GetAppOptions();
500 bool bOptChanged = false;
501
502 formula::FormulaToken** ppToken = context->pArr->GetArray();
503 sal_uInt16 nTokens = context->pArr->GetLen();
504 sal_uInt16 nLevel = 0;
505 for (sal_uInt16 nTP=0; nTP<nTokens; nTP++)
506 {
507 formula::FormulaToken* pTok = ppToken[nTP];
508 OpCode eOp = pTok->GetOpCode();
509 if ( eOp == ocOpen )
510 ++nLevel;
511 else if ( eOp == ocClose && nLevel )
512 --nLevel;
513 if ( nLevel == 0 && pTok->IsFunction() &&
514 lcl_AddFunction( aAppOpt, sal::static_int_cast<sal_uInt16>( eOp ) ) )
515 bOptChanged = true;
516 }
517
518 if ( bOptChanged )
519 {
520 pScMod->SetAppOptions(aAppOpt);
521 }
522
523 if (context->bMatrixExpand)
524 {
525 // If the outer function/operator returns an array/matrix then
526 // enter a matrix formula. ScViewFunc::EnterMatrix() takes care
527 // of selection/mark of the result dimensions or preselected
528 // mark. If the user wanted less or a single cell then should
529 // mark such prior to entering the formula.
530 const formula::FormulaToken* pToken = context->pArr->LastRPNToken();
531 if (pToken && (formula::FormulaCompiler::IsMatrixFunction( pToken->GetOpCode())
532 || pToken->IsInForceArray()))
533 {
534 // Discard this (still empty here) Undo action,
535 // EnterMatrix() will create its own.
536 if (context->bRecord)
537 context->GetDocFunc().EndListAction();
538
539 // Use corrected formula string.
540 context->rViewFunc.EnterMatrix( context->aFormula, context->GetDoc().GetGrammar());
541
542 return;
543 }
544 }
545 }
546
547 ScFormulaCell aCell(context->GetDoc(), *context->aPos, std::move(*context->pArr), formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::NONE);
548
549 SCTAB i;
550 SvNumberFormatter* pFormatter = context->GetDoc().GetFormatTable();
551 for (const auto& rTab : context->aMark)
552 {
553 i = rTab;
554 context->aPos->SetTab( i );
555 const sal_uInt32 nIndex = context->GetDoc().GetAttr(
556 context->nCol, context->nRow, i, ATTR_VALUE_FORMAT )->GetValue();
557 const SvNumFormatType nType = pFormatter->GetType( nIndex);
558 if (nType == SvNumFormatType::TEXT ||
559 ((context->aString[0] == '+' || context->aString[0] == '-') && nError != FormulaError::NONE && context->aString == context->aFormula))
560 {
561 if ( context->pData )
562 {
563 // A clone of context->pData will be stored in the cell.
564 context->GetDocFunc().SetEditCell(*(context->aPos), *context->pData, true);
565 }
566 else
567 context->GetDocFunc().SetStringCell(*(context->aPos), context->aFormula, true);
568 }
569 else
570 {
571 ScFormulaCell* pCell = new ScFormulaCell( aCell, context->GetDoc(), *(context->aPos) );
572 if ( nError != FormulaError::NONE )
573 {
574 pCell->GetCode()->DelRPN();
575 pCell->SetErrCode( nError );
576 if(pCell->GetCode()->IsHyperLink())
577 pCell->GetCode()->SetHyperLink(false);
578 }
579 if (nType == SvNumFormatType::LOGICAL)
580 {
581 // Reset to General so the actual format can be determined
582 // after the cell has been interpreted. A sticky boolean
583 // number format is highly likely unwanted... see tdf#75650.
584 // General of same locale as current number format.
585 const SvNumberformat* pEntry = pFormatter->GetEntry( nIndex);
586 const LanguageType nLang = (pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge);
587 const sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, nLang);
588 ScPatternAttr aPattern(context->GetDoc().getCellAttributeHelper());
589 aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat));
590 ScMarkData aMark(context->GetDoc().GetSheetLimits());
591 aMark.SelectTable( i, true);
592 aMark.SetMarkArea( ScRange( *(context->aPos)));
593 context->GetDocFunc().ApplyAttributes( aMark, aPattern, false);
594 context->bNumFmtChanged = true;
595 }
596 context->GetDocFunc().SetFormulaCell(*(context->aPos), pCell, true);
597 }
598 }
599
600 performAutoFormatAndUpdate(context->aString, context->aMark, context->nCol,
601 context->nRow, context->nTab, context->bNumFmtChanged,
602 context->bRecord, context->aModificator, context->rViewFunc);
603 }
604
parseAndCorrectFormula(std::shared_ptr<FormulaProcessingContext> context)605 void parseAndCorrectFormula(std::shared_ptr<FormulaProcessingContext> context)
606 {
607 bool bAddEqual = false;
608 context->pArr = context->aComp->CompileString(context->aFormula);
609 bool bCorrected = context->aComp->IsCorrected();
610
611 if (bCorrected) {
612 context->pArrFirst = context->pArr;
613 context->pArr = context->aComp->CompileString(context->aComp->GetCorrectedFormula());
614 }
615
616 if (context->pArr->GetCodeError() == FormulaError::NONE) {
617 bAddEqual = true;
618 context->aComp->CompileTokenArray();
619 bCorrected |= context->aComp->IsCorrected();
620 }
621
622 if (bCorrected) {
623 context->aCorrectedFormula = bAddEqual ? "=" + context->aComp->GetCorrectedFormula()
624 : context->aComp->GetCorrectedFormula();
625 if (context->aCorrectedFormula.getLength() == 1) {
626 // empty formula, just '='
627 if (context->pArrFirst)
628 context->pArr = context->pArrFirst;
629 }
630 else
631 {
632 runAutoCorrectQueryAsync(context);
633 return;
634 }
635 }
636 finalizeFormulaProcessing(context);
637 }
638
runAutoCorrectQueryAsync(std::shared_ptr<FormulaProcessingContext> context)639 void runAutoCorrectQueryAsync(std::shared_ptr<FormulaProcessingContext> context)
640 {
641 auto aQueryBox = std::make_shared<AutoCorrectQuery>(context->GetViewData().GetDialogParent(), context->aCorrectedFormula);
642 weld::DialogController::runAsync(aQueryBox, [context] (int nResult)
643 {
644 if (nResult == RET_YES) {
645 context->aFormula = context->aCorrectedFormula;
646 parseAndCorrectFormula(context);
647 } else {
648 if (context->pArrFirst)
649 context->pArr = context->pArrFirst;
650
651 finalizeFormulaProcessing(context);
652 }
653 });
654 }
655 }
656
657 // actual functions
658
659 // input - undo OK
EnterData(SCCOL nCol,SCROW nRow,SCTAB nTab,const OUString & rString,const EditTextObject * pData,bool bMatrixExpand)660 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
661 const OUString& rString,
662 const EditTextObject* pData,
663 bool bMatrixExpand )
664 {
665 ScDocument& rDoc = GetViewData().GetDocument();
666 ScMarkData rMark(GetViewData().GetMarkData());
667 bool bRecord = rDoc.IsUndoEnabled();
668 SCTAB i;
669
670 ScDocShell* pDocSh = GetViewData().GetDocShell();
671 ScDocFunc &rFunc = GetViewData().GetDocFunc();
672 std::shared_ptr<ScDocShellModificator> xModificator = std::make_shared<ScDocShellModificator>(*pDocSh);
673
674 ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, rMark );
675 if (!aTester.IsEditable())
676 {
677 ErrorMessage(aTester.GetMessageId());
678 PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
679 return;
680 }
681
682 if ( bRecord )
683 rFunc.EnterListAction( STR_UNDO_ENTERDATA );
684
685 bool bFormula = false;
686
687 // do not check formula if it is a text cell
688 sal_uInt32 format = rDoc.GetNumberFormat( nCol, nRow, nTab );
689 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
690 // a single '=' character is handled as string (needed for special filters)
691 if ( pFormatter->GetType(format) != SvNumFormatType::TEXT && rString.getLength() > 1 )
692 {
693 if ( rString[0] == '=' )
694 {
695 // handle as formula
696 bFormula = true;
697 }
698 else if ( rString[0] == '+' || rString[0] == '-' )
699 {
700 // if there is more than one leading '+' or '-' character, remove the additional ones
701 sal_Int32 nIndex = 1;
702 sal_Int32 nLen = rString.getLength();
703 while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
704 {
705 ++nIndex;
706 }
707 OUString aString = rString.replaceAt( 1, nIndex - 1, u"" );
708
709 // if the remaining part without the leading '+' or '-' character
710 // is non-empty and not a number, handle as formula
711 if ( aString.getLength() > 1 )
712 {
713 double fNumber = 0;
714 if ( !pFormatter->IsNumberFormat( aString, format, fNumber ) )
715 {
716 bFormula = true;
717 }
718 }
719 }
720 }
721
722 bool bNumFmtChanged = false;
723 if ( bFormula )
724 { // formula, compile with autoCorrection
725 i = rMark.GetFirstSelected();
726 auto aPosPtr = std::make_shared<ScAddress>(nCol, nRow, i);
727 auto aCompPtr = std::make_shared<ScCompiler>(rDoc, *aPosPtr, rDoc.GetGrammar(), true, false);
728
729 //2do: enable/disable autoCorrection via calcoptions
730 aCompPtr->SetAutoCorrection( true );
731 if ( rString[0] == '+' || rString[0] == '-' )
732 {
733 aCompPtr->SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK );
734 }
735
736 OUString aFormula( rString );
737
738 FormulaProcessingContext context_instance{
739 aPosPtr, aCompPtr, xModificator, nullptr, nullptr, pData,
740 rMark, *this, OUString(), aFormula, rString, nCol,
741 nRow, nTab, bMatrixExpand, bNumFmtChanged, bRecord
742 };
743
744 parseAndCorrectFormula(std::make_shared<FormulaProcessingContext>(context_instance));
745 }
746 else
747 {
748 ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
749 for (const auto& rTab : rMark)
750 {
751 bool bNumFmtSet = false;
752 const ScAddress aScAddress(nCol, nRow, rTab);
753
754 // tdf#104902 - handle embedded newline
755 if (ScStringUtil::isMultiline(rString))
756 {
757 rEngine.SetTextCurrentDefaults(rString);
758 rDoc.SetEditText(aScAddress, rEngine.CreateTextObject());
759 pDocSh->AdjustRowHeight(nRow, nRow, rTab);
760 }
761 else
762 {
763 rFunc.SetNormalString(bNumFmtSet, aScAddress, rString, false);
764 }
765
766 if (bNumFmtSet)
767 {
768 /* FIXME: if set on any sheet results in changed only on
769 * sheet nTab for TestFormatArea() and DoAutoAttributes() */
770 bNumFmtChanged = true;
771 }
772 }
773 performAutoFormatAndUpdate(rString, rMark, nCol, nRow, nTab, bNumFmtChanged, bRecord, xModificator, *this);
774 }
775 }
776
777 // enter value in single cell (on nTab only)
778
EnterValue(SCCOL nCol,SCROW nRow,SCTAB nTab,const double & rValue)779 void ScViewFunc::EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue )
780 {
781 ScDocument& rDoc = GetViewData().GetDocument();
782 ScDocShell* pDocSh = GetViewData().GetDocShell();
783
784 if (!pDocSh)
785 return;
786
787 bool bUndo(rDoc.IsUndoEnabled());
788 ScDocShellModificator aModificator( *pDocSh );
789
790 ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
791 if (aTester.IsEditable())
792 {
793 ScAddress aPos( nCol, nRow, nTab );
794 ScCellValue aUndoCell;
795 if (bUndo)
796 aUndoCell.assign(rDoc, aPos);
797
798 rDoc.SetValue( nCol, nRow, nTab, rValue );
799
800 // because of ChangeTrack after change in document
801 if (bUndo)
802 {
803 pDocSh->GetUndoManager()->AddUndoAction(
804 std::make_unique<ScUndoEnterValue>(pDocSh, aPos, aUndoCell, rValue));
805 }
806
807 pDocSh->PostPaintCell( aPos );
808 pDocSh->UpdateOle(GetViewData());
809 aModificator.SetDocumentModified();
810 }
811 else
812 ErrorMessage(aTester.GetMessageId());
813 }
814
EnterData(SCCOL nCol,SCROW nRow,SCTAB nTab,const EditTextObject & rData,bool bTestSimple)815 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
816 const EditTextObject& rData, bool bTestSimple )
817 {
818 ScDocShell* pDocSh = GetViewData().GetDocShell();
819 ScMarkData& rMark = GetViewData().GetMarkData();
820 ScDocument& rDoc = pDocSh->GetDocument();
821 bool bRecord = rDoc.IsUndoEnabled();
822
823 ScDocShellModificator aModificator( *pDocSh );
824
825 ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
826 if (aTester.IsEditable())
827 {
828
829 // test for attribute
830
831 bool bSimple = false;
832 bool bCommon = false;
833 std::unique_ptr<ScPatternAttr> pCellAttrs;
834 OUString aString;
835
836 const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
837 ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
838 aEngine.SetTextCurrentDefaults(rData);
839
840 if (bTestSimple) // test, if simple string without attribute
841 {
842 ScEditAttrTester aAttrTester( &aEngine );
843 bSimple = !aAttrTester.NeedsObject();
844 bCommon = aAttrTester.NeedsCellAttr();
845
846 // formulas have to be recognized even if they're formatted
847 // (but common attributes are still collected)
848
849 if (!bSimple)
850 {
851 OUString aParStr(aEngine.GetText( 0 ));
852 if ( aParStr[0] == '=' )
853 bSimple = true;
854 }
855
856 if (bCommon) // attribute for tab
857 {
858 pCellAttrs.reset(new ScPatternAttr( *pOldPattern ));
859 pCellAttrs->GetFromEditItemSet( &aAttrTester.GetAttribs() );
860 //! remove common attributes from EditEngine?
861 }
862 }
863
864 // #i97726# always get text for "repeat" of undo action
865 aString = ScEditUtil::GetMultilineString(aEngine);
866
867 // undo
868
869 std::unique_ptr<EditTextObject> pUndoData;
870 ScUndoEnterData::ValuesType aOldValues;
871
872 if (bRecord && !bSimple)
873 {
874 for (const auto& rTab : rMark)
875 {
876 ScUndoEnterData::Value aOldValue;
877 aOldValue.mnTab = rTab;
878 aOldValue.maCell.assign(rDoc, ScAddress(nCol, nRow, rTab));
879 aOldValues.push_back(aOldValue);
880 }
881
882 pUndoData = rData.Clone();
883 }
884
885 // enter data
886
887 if (bCommon)
888 rDoc.ApplyPattern(nCol,nRow,nTab,*pCellAttrs); //! undo
889
890 if (bSimple)
891 {
892 if (bCommon)
893 AdjustRowHeight(nRow,nRow,true);
894
895 EnterData( nCol, nRow, nTab, aString, nullptr, true /*bMatrixExpand*/);
896 }
897 else
898 {
899 for (const auto& rTab : rMark)
900 {
901 ScAddress aPos(nCol, nRow, rTab);
902 rDoc.SetEditText(aPos, rData, rDoc.GetEditPool());
903 }
904
905 if ( bRecord )
906 { // because of ChangeTrack current first
907 pDocSh->GetUndoManager()->AddUndoAction(
908 std::make_unique<ScUndoEnterData>(pDocSh, ScAddress(nCol,nRow,nTab), aOldValues, aString, std::move(pUndoData)));
909 }
910
911 HideAllCursors();
912
913 AdjustRowHeight(nRow,nRow,true);
914
915 for (const auto& rTab : rMark)
916 pDocSh->PostPaintCell( nCol, nRow, rTab );
917
918 ShowAllCursors();
919
920 pDocSh->UpdateOle(GetViewData());
921
922 bool bIsEmpty = rData.GetParagraphCount() == 0
923 || (rData.GetParagraphCount() == 1 && !rData.HasText(0));
924 const OUString aType(bIsEmpty ? u"delete-content" : u"cell-change");
925 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
926
927 aModificator.SetDocumentModified();
928 }
929 lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
930 }
931 else
932 {
933 ErrorMessage(aTester.GetMessageId());
934 PaintArea( nCol, nRow, nCol, nRow ); // possibly the edit-engine is still painted there
935 }
936 }
937
EnterDataAtCursor(const OUString & rString)938 void ScViewFunc::EnterDataAtCursor( const OUString& rString )
939 {
940 SCCOL nPosX = GetViewData().GetCurX();
941 SCROW nPosY = GetViewData().GetCurY();
942 SCTAB nTab = GetViewData().GetTabNo();
943
944 EnterData( nPosX, nPosY, nTab, rString );
945 // tdf#154174: update repeated data in the cell
946 GetViewData().GetViewShell()->UpdateInputHandler();
947 }
948
EnterMatrix(const OUString & rString,::formula::FormulaGrammar::Grammar eGram)949 void ScViewFunc::EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram )
950 {
951 ScViewData& rData = GetViewData();
952 const SCCOL nCol = rData.GetCurX();
953 const SCROW nRow = rData.GetCurY();
954 const ScMarkData& rMark = rData.GetMarkData();
955 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
956 {
957 // nothing marked -> temporarily calculate block
958 // with size of result formula to get the size
959
960 ScDocument& rDoc = rData.GetDocument();
961 SCTAB nTab = rData.GetTabNo();
962 ScFormulaCell aFormCell( rDoc, ScAddress(nCol,nRow,nTab), rString, eGram, ScMatrixMode::Formula );
963
964 SCSIZE nSizeX;
965 SCSIZE nSizeY;
966 aFormCell.GetResultDimensions( nSizeX, nSizeY );
967 if ( nSizeX != 0 && nSizeY != 0 &&
968 nCol+nSizeX-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxCol()) &&
969 nRow+nSizeY-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxRow()) )
970 {
971 ScRange aResult( nCol, nRow, nTab,
972 sal::static_int_cast<SCCOL>(nCol+nSizeX-1),
973 sal::static_int_cast<SCROW>(nRow+nSizeY-1), nTab );
974 MarkRange( aResult, false );
975 }
976 }
977
978 ScRange aRange;
979 if (rData.GetSimpleArea(aRange) == SC_MARK_SIMPLE)
980 {
981 ScDocShell* pDocSh = rData.GetDocShell();
982 bool bSuccess = pDocSh->GetDocFunc().EnterMatrix(
983 aRange, &rMark, nullptr, rString, false, false, OUString(), eGram );
984 if (bSuccess)
985 pDocSh->UpdateOle(GetViewData());
986 else
987 PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
988 }
989 else
990 ErrorMessage(STR_NOMULTISELECT);
991 }
992
GetSelectionScriptType()993 SvtScriptType ScViewFunc::GetSelectionScriptType()
994 {
995 SvtScriptType nScript = SvtScriptType::NONE;
996
997 ScDocument& rDoc = GetViewData().GetDocument();
998 const ScMarkData& rMark = GetViewData().GetMarkData();
999 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
1000 {
1001 // no selection -> cursor
1002
1003 nScript = rDoc.GetScriptType( GetViewData().GetCurX(),
1004 GetViewData().GetCurY(), GetViewData().GetTabNo());
1005 }
1006 else
1007 {
1008 ScRangeList aRanges;
1009 rMark.FillRangeListWithMarks( &aRanges, false );
1010 nScript = rDoc.GetRangeScriptType(aRanges);
1011 }
1012
1013 if (nScript == SvtScriptType::NONE)
1014 nScript = ScGlobal::GetDefaultScriptType();
1015
1016 return nScript;
1017 }
1018
1019 static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc);
1020
GetSelectionPattern()1021 const ScPatternAttr* ScViewFunc::GetSelectionPattern()
1022 {
1023 // Don't use UnmarkFiltered in slot state functions, for performance reasons.
1024 // The displayed state is always that of the whole selection including filtered rows.
1025
1026 ScMarkData aMark = GetViewData().GetMarkData();
1027 ScDocument& rDoc = GetViewData().GetDocument();
1028
1029 // tdf#155368 if the selection is the whole sheet, we need to shrink the mark area, otherwise
1030 // we will not return a consistent result
1031 // (consistent compared to what happens in ScViewFunc::ApplySelectionPattern)
1032 ShrinkToDataArea( aMark, rDoc );
1033
1034 if ( aMark.IsMarked() || aMark.IsMultiMarked() )
1035 {
1036 // MarkToMulti is no longer necessary for rDoc.GetSelectionPattern
1037 const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
1038 return pAttr;
1039 }
1040 else
1041 {
1042 SCCOL nCol = GetViewData().GetCurX();
1043 SCROW nRow = GetViewData().GetCurY();
1044 SCTAB nTab = GetViewData().GetTabNo();
1045
1046 // copy sheet selection
1047 aMark.SetMarkArea( ScRange( nCol, nRow, nTab ) );
1048 const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
1049 return pAttr;
1050 }
1051 }
1052
GetSelectionFrame(std::shared_ptr<SvxBoxItem> & rLineOuter,std::shared_ptr<SvxBoxInfoItem> & rLineInner)1053 void ScViewFunc::GetSelectionFrame(
1054 std::shared_ptr<SvxBoxItem>& rLineOuter,
1055 std::shared_ptr<SvxBoxInfoItem>& rLineInner )
1056 {
1057 ScDocument& rDoc = GetViewData().GetDocument();
1058 const ScMarkData& rMark = GetViewData().GetMarkData();
1059
1060 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1061 {
1062 rDoc.GetSelectionFrame( rMark, *rLineOuter, *rLineInner );
1063 }
1064 else
1065 {
1066 const ScPatternAttr* pAttrs =
1067 rDoc.GetPattern( GetViewData().GetCurX(),
1068 GetViewData().GetCurY(),
1069 GetViewData().GetTabNo() );
1070
1071 rLineOuter.reset(pAttrs->GetItem(ATTR_BORDER).Clone());
1072 rLineInner.reset(pAttrs->GetItem(ATTR_BORDER_INNER).Clone());
1073
1074 rLineInner->SetTable(false);
1075 rLineInner->SetDist(true);
1076 rLineInner->SetMinDist(false);
1077 }
1078 }
1079
1080 // apply attribute - undo OK
1081 //
1082 // complete set ( ATTR_STARTINDEX, ATTR_ENDINDEX )
1083
ApplyAttributes(const SfxItemSet & rDialogSet,const SfxItemSet & rOldSet,bool bAdjustBlockHeight)1084 void ScViewFunc::ApplyAttributes( const SfxItemSet& rDialogSet,
1085 const SfxItemSet& rOldSet,
1086 bool bAdjustBlockHeight)
1087 {
1088 // not editable because of matrix only? attribute OK nonetheless
1089 bool bOnlyNotBecauseOfMatrix;
1090 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1091 {
1092 ErrorMessage(STR_PROTECTIONERR);
1093 return;
1094 }
1095
1096 ScDocument& rDoc = GetViewData().GetDocument();
1097 ScPatternAttr aOldAttrs(rDoc.getCellAttributeHelper(), &rOldSet);
1098 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper(), &rDialogSet);
1099 aNewAttrs.DeleteUnchanged( &aOldAttrs );
1100
1101 if ( rDialogSet.GetItemState( ATTR_VALUE_FORMAT ) == SfxItemState::SET )
1102 { // don't reset to default SYSTEM GENERAL if not intended
1103 sal_uInt32 nOldFormat =
1104 rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
1105 sal_uInt32 nNewFormat =
1106 rDialogSet.Get( ATTR_VALUE_FORMAT ).GetValue();
1107 if ( nNewFormat != nOldFormat )
1108 {
1109 SvNumberFormatter* pFormatter =
1110 GetViewData().GetDocument().GetFormatTable();
1111 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
1112 LanguageType eOldLang =
1113 pOldEntry ? pOldEntry->GetLanguage() : LANGUAGE_DONTKNOW;
1114 const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
1115 LanguageType eNewLang =
1116 pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
1117 if ( eNewLang != eOldLang )
1118 {
1119 aNewAttrs.GetItemSet().Put(
1120 SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
1121
1122 // only the language has changed -> do not touch numberformat-attribute
1123 sal_uInt32 nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
1124 if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
1125 nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
1126 aNewAttrs.GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
1127 }
1128 }
1129 }
1130
1131 if (rDialogSet.HasItem(ATTR_FONT_LANGUAGE))
1132 // font language has changed. Redo the online spelling.
1133 ResetAutoSpell();
1134
1135 const SvxBoxItem& rOldOuter = rOldSet.Get(ATTR_BORDER);
1136 const SvxBoxItem& rNewOuter = rDialogSet.Get(ATTR_BORDER);
1137 const SvxBoxInfoItem& rOldInner = rOldSet.Get(ATTR_BORDER_INNER);
1138 const SvxBoxInfoItem& rNewInner = rDialogSet.Get(ATTR_BORDER_INNER);
1139 SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
1140
1141 // protect referenced Items from disappearing (was: don't delete yet)
1142 const SfxPoolItemHolder aHoldOuter(*rDialogSet.GetPool() , &rNewOuter);
1143 const SfxPoolItemHolder aHoldInner(*rDialogSet.GetPool() , &rNewInner);
1144 (void)aHoldOuter;
1145 (void)aHoldInner;
1146
1147 rNewSet.ClearItem( ATTR_BORDER );
1148 rNewSet.ClearItem( ATTR_BORDER_INNER );
1149
1150 /*
1151 * establish whether border attribute is to be set:
1152 * 1. new != old
1153 * 2. is one of the borders not-DontCare (since 238.f: IsxxValid())
1154 *
1155 */
1156
1157 bool bFrame = (rDialogSet.GetItemState( ATTR_BORDER ) != SfxItemState::DEFAULT)
1158 || (rDialogSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::DEFAULT);
1159
1160 if (SfxPoolItem::areSame(rNewOuter, rOldOuter) && SfxPoolItem::areSame(rNewInner, rOldInner))
1161 bFrame = false;
1162
1163 // this should be intercepted by the pool: ?!??!??
1164
1165 if (bFrame && rNewOuter == rOldOuter && rNewInner == rOldInner)
1166 bFrame = false;
1167
1168 bFrame = bFrame
1169 && ( rNewInner.IsValid(SvxBoxInfoItemValidFlags::LEFT)
1170 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT)
1171 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::TOP)
1172 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM)
1173 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::HORI)
1174 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::VERT) );
1175
1176 if (!bFrame)
1177 ApplySelectionPattern( aNewAttrs ); // standard only
1178 else
1179 {
1180 // if new items are default-items, overwrite the old items:
1181
1182 bool bDefNewOuter = IsStaticDefaultItem(&rNewOuter);
1183 bool bDefNewInner = IsStaticDefaultItem(&rNewInner);
1184
1185 ApplyPatternLines( aNewAttrs,
1186 bDefNewOuter ? rOldOuter : rNewOuter,
1187 bDefNewInner ? &rOldInner : &rNewInner );
1188 }
1189
1190 // adjust height only if needed
1191 if (bAdjustBlockHeight)
1192 AdjustBlockHeight();
1193
1194 // CellContentChanged is called in ApplySelectionPattern / ApplyPatternLines
1195 }
1196
ApplyAttr(const SfxPoolItem & rAttrItem,bool bAdjustBlockHeight)1197 void ScViewFunc::ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight )
1198 {
1199 // not editable because of matrix only? attribute OK nonetheless
1200 bool bOnlyNotBecauseOfMatrix;
1201 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1202 {
1203 ErrorMessage(STR_PROTECTIONERR);
1204 return;
1205 }
1206
1207 ScDocument& rDoc = GetViewData().GetDocument();
1208 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
1209
1210 aNewAttrs.GetItemSet().Put( rAttrItem );
1211 // if justify is set (with Buttons), always indentation 0
1212 if ( rAttrItem.Which() == ATTR_HOR_JUSTIFY )
1213 aNewAttrs.GetItemSet().Put( ScIndentItem( 0 ) );
1214 ApplySelectionPattern( aNewAttrs );
1215
1216 // Prevent useless compute
1217 if (bAdjustBlockHeight)
1218 AdjustBlockHeight();
1219
1220 // CellContentChanged is called in ApplySelectionPattern
1221 }
1222
1223 // patterns and borders
1224
ApplyPatternLines(const ScPatternAttr & rAttr,const SvxBoxItem & rNewOuter,const SvxBoxInfoItem * pNewInner)1225 void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem& rNewOuter,
1226 const SvxBoxInfoItem* pNewInner )
1227 {
1228 ScDocument& rDoc = GetViewData().GetDocument();
1229 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
1230 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1231 bool bRecord = true;
1232 if (!rDoc.IsUndoEnabled())
1233 bRecord = false;
1234
1235 bool bRemoveAdjCellBorder = rNewOuter.IsRemoveAdjacentCellBorder();
1236 ScRange aMarkRange, aMarkRangeWithEnvelope;
1237 aFuncMark.MarkToSimple();
1238 bool bMulti = aFuncMark.IsMultiMarked();
1239 if (bMulti)
1240 aMarkRange = aFuncMark.GetMultiMarkArea();
1241 else if (aFuncMark.IsMarked())
1242 aMarkRange = aFuncMark.GetMarkArea();
1243 else
1244 {
1245 aMarkRange = ScRange( GetViewData().GetCurX(),
1246 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1247 DoneBlockMode();
1248 InitOwnBlockMode( aMarkRange );
1249 aFuncMark.SetMarkArea(aMarkRange);
1250 MarkDataChanged();
1251 }
1252 if( bRemoveAdjCellBorder )
1253 aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
1254 else
1255 aMarkRangeWithEnvelope = aMarkRange;
1256
1257 ScDocShell* pDocSh = GetViewData().GetDocShell();
1258
1259 ScDocShellModificator aModificator( *pDocSh );
1260
1261 if (bRecord)
1262 {
1263 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1264 SCTAB nStartTab = aMarkRange.aStart.Tab();
1265 SCTAB nTabCount = rDoc.GetTableCount();
1266 bool bCopyOnlyMarked = false;
1267 if( !bRemoveAdjCellBorder )
1268 bCopyOnlyMarked = bMulti;
1269 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1270 for (const auto& rTab : aFuncMark)
1271 if (rTab != nStartTab)
1272 pUndoDoc->AddUndoTab( rTab, rTab );
1273
1274 ScRange aCopyRange = aMarkRangeWithEnvelope;
1275 aCopyRange.aStart.SetTab(0);
1276 aCopyRange.aEnd.SetTab(nTabCount-1);
1277 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, *pUndoDoc, &aFuncMark );
1278
1279 pDocSh->GetUndoManager()->AddUndoAction(
1280 std::make_unique<ScUndoSelectionAttr>(
1281 pDocSh, aFuncMark,
1282 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
1283 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
1284 std::move(pUndoDoc), bCopyOnlyMarked, &rAttr, &rNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
1285 }
1286
1287 sal_uInt16 nExt = SC_PF_TESTMERGE;
1288 pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change
1289
1290 rDoc.ApplySelectionFrame(aFuncMark, rNewOuter, pNewInner);
1291
1292 pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change
1293
1294 aFuncMark.MarkToMulti();
1295 rDoc.ApplySelectionPattern( rAttr, aFuncMark );
1296
1297 pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid, nExt );
1298 pDocSh->UpdateOle(GetViewData());
1299 aModificator.SetDocumentModified();
1300 CellContentChanged();
1301
1302 StartFormatArea();
1303 }
1304
1305 // tdf#147842 if the marked area is the entire sheet, then shrink it to the data area.
1306 // Otherwise ctrl-A, perform-action, will take a very long time as it tries to modify
1307 // cells that we are not using.
ShrinkToDataArea(ScMarkData & rFuncMark,ScDocument & rDoc)1308 static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc)
1309 {
1310 // do not make it marked if it is not already marked
1311 if (!rFuncMark.IsMarked())
1312 return;
1313 if (rFuncMark.IsMultiMarked())
1314 return;
1315 ScRange aMarkArea = rFuncMark.GetMarkArea();
1316 const ScSheetLimits& rLimits = rDoc.GetSheetLimits();
1317 if (aMarkArea.aStart.Row() != 0 || aMarkArea.aStart.Col() != 0)
1318 return;
1319 if (aMarkArea.aEnd.Row() != rLimits.MaxRow() || aMarkArea.aEnd.Col() != rLimits.MaxCol())
1320 return;
1321 if (aMarkArea.aStart.Tab() != aMarkArea.aEnd.Tab())
1322 return;
1323 SCCOL nStartCol = aMarkArea.aStart.Col();
1324 SCROW nStartRow = aMarkArea.aStart.Row();
1325 SCCOL nEndCol = aMarkArea.aEnd.Col();
1326 SCROW nEndRow = aMarkArea.aEnd.Row();
1327 rDoc.ShrinkToDataArea(aMarkArea.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow);
1328 aMarkArea.aStart.SetCol(nStartCol);
1329 aMarkArea.aStart.SetRow(nStartRow);
1330 aMarkArea.aEnd.SetCol(nEndCol);
1331 aMarkArea.aEnd.SetRow(nEndRow);
1332 rFuncMark.ResetMark();
1333 rFuncMark.SetMarkArea(aMarkArea);
1334 }
1335
1336 // pattern only
1337
ApplySelectionPattern(const ScPatternAttr & rAttr,bool bCursorOnly)1338 void ScViewFunc::ApplySelectionPattern( const ScPatternAttr& rAttr, bool bCursorOnly )
1339 {
1340 ScViewData& rViewData = GetViewData();
1341 ScDocShell* pDocSh = rViewData.GetDocShell();
1342 ScDocument& rDoc = pDocSh->GetDocument();
1343 ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
1344 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1345
1346 bool bRecord = true;
1347 if (!rDoc.IsUndoEnabled())
1348 bRecord = false;
1349
1350 // State from old ItemSet doesn't matter for paint flags, as any change will be
1351 // from SfxItemState::SET in the new ItemSet (default is ignored in ApplyPattern).
1352 // New alignment is checked (check in PostPaint isn't enough) in case a right
1353 // alignment is changed to left.
1354 const SfxItemSet& rNewSet = rAttr.GetItemSet();
1355 bool bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
1356 rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
1357 bool bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
1358
1359 sal_uInt16 nExtFlags = 0;
1360 if ( bSetLines )
1361 nExtFlags |= SC_PF_LINES;
1362 if ( bSetAlign )
1363 nExtFlags |= SC_PF_WHOLEROWS;
1364
1365 ScDocShellModificator aModificator( *pDocSh );
1366
1367 bool bMulti = aFuncMark.IsMultiMarked();
1368 aFuncMark.MarkToMulti();
1369 bool bOnlyTab = (!aFuncMark.IsMultiMarked() && !bCursorOnly && aFuncMark.GetSelectCount() > 1);
1370 if (bOnlyTab)
1371 {
1372 SCCOL nCol = rViewData.GetCurX();
1373 SCROW nRow = rViewData.GetCurY();
1374 SCTAB nTab = rViewData.GetTabNo();
1375 aFuncMark.SetMarkArea(ScRange(nCol,nRow,nTab));
1376 aFuncMark.MarkToMulti();
1377 }
1378
1379 ScRangeList aChangeRanges;
1380
1381 if (aFuncMark.IsMultiMarked() && !bCursorOnly)
1382 {
1383 const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
1384 SCTAB nTabCount = rDoc.GetTableCount();
1385 for (const auto& rTab : aFuncMark)
1386 {
1387 ScRange aChangeRange( aMarkRange );
1388 aChangeRange.aStart.SetTab( rTab );
1389 aChangeRange.aEnd.SetTab( rTab );
1390 aChangeRanges.push_back( aChangeRange );
1391 }
1392
1393 SCCOL nStartCol = aMarkRange.aStart.Col();
1394 SCROW nStartRow = aMarkRange.aStart.Row();
1395 SCTAB nStartTab = aMarkRange.aStart.Tab();
1396 SCCOL nEndCol = aMarkRange.aEnd.Col();
1397 SCROW nEndRow = aMarkRange.aEnd.Row();
1398 SCTAB nEndTab = aMarkRange.aEnd.Tab();
1399
1400 ScEditDataArray* pEditDataArray = nullptr;
1401 if (bRecord)
1402 {
1403 ScRange aCopyRange = aMarkRange;
1404 aCopyRange.aStart.SetTab(0);
1405 aCopyRange.aEnd.SetTab(nTabCount-1);
1406
1407 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1408 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1409 for (const auto& rTab : aFuncMark)
1410 if (rTab != nStartTab)
1411 pUndoDoc->AddUndoTab( rTab, rTab );
1412 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &aFuncMark );
1413
1414 aFuncMark.MarkToMulti();
1415
1416 ScUndoSelectionAttr* pUndoAttr = new ScUndoSelectionAttr(
1417 pDocSh, aFuncMark, nStartCol, nStartRow, nStartTab,
1418 nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), bMulti, &rAttr );
1419 pDocSh->GetUndoManager()->AddUndoAction(std::unique_ptr<ScUndoSelectionAttr>(pUndoAttr));
1420 pEditDataArray = pUndoAttr->GetDataArray();
1421 }
1422
1423 rDoc.ApplySelectionPattern( rAttr, aFuncMark, pEditDataArray );
1424
1425 pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
1426 nEndCol, nEndRow, nEndTab,
1427 PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
1428 pDocSh->UpdateOle(GetViewData());
1429 aModificator.SetDocumentModified();
1430 CellContentChanged();
1431 }
1432 else // single cell - simpler undo
1433 {
1434 SCCOL nCol = rViewData.GetCurX();
1435 SCROW nRow = rViewData.GetCurY();
1436 SCTAB nTab = rViewData.GetTabNo();
1437
1438 std::unique_ptr<EditTextObject> pOldEditData;
1439 std::unique_ptr<EditTextObject> pNewEditData;
1440 ScAddress aPos(nCol, nRow, nTab);
1441 ScRefCellValue aCell(rDoc, aPos);
1442 if (aCell.getType() == CELLTYPE_EDIT)
1443 {
1444 const EditTextObject* pEditObj = aCell.getEditText();
1445 pOldEditData = pEditObj->Clone();
1446 rDoc.RemoveEditTextCharAttribs(aPos, rAttr);
1447 pEditObj = rDoc.GetEditText(aPos);
1448 pNewEditData = pEditObj->Clone();
1449 }
1450
1451 aChangeRanges.push_back(aPos);
1452 std::optional<ScPatternAttr> pOldPat(*rDoc.GetPattern( nCol, nRow, nTab ));
1453
1454 rDoc.ApplyPattern( nCol, nRow, nTab, rAttr );
1455
1456 const ScPatternAttr* pNewPat = rDoc.GetPattern( nCol, nRow, nTab );
1457
1458 if (bRecord)
1459 {
1460 std::unique_ptr<ScUndoCursorAttr> pUndo(new ScUndoCursorAttr(
1461 pDocSh, nCol, nRow, nTab, &*pOldPat, pNewPat, &rAttr ));
1462 pUndo->SetEditData(std::move(pOldEditData), std::move(pNewEditData));
1463 pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndo));
1464 }
1465 pOldPat.reset(); // is copied in undo (Pool)
1466
1467 pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
1468 pDocSh->UpdateOle(GetViewData());
1469 aModificator.SetDocumentModified();
1470 CellContentChanged();
1471 }
1472
1473 ScModelObj* pModelObj = pDocSh->GetModel();
1474
1475 if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
1476 {
1477 css::uno::Sequence< css::beans::PropertyValue > aProperties;
1478 sal_Int32 nCount = 0;
1479 const SfxItemPropertyMap& rMap = ScCellObj::GetCellPropertyMap();
1480 for ( sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; ++nWhich )
1481 {
1482 const SfxPoolItem* pItem = nullptr;
1483 if ( rNewSet.GetItemState( nWhich, true, &pItem ) == SfxItemState::SET && pItem )
1484 {
1485 for ( const auto pEntry : rMap.getPropertyEntries())
1486 {
1487 if ( pEntry->nWID == nWhich )
1488 {
1489 css::uno::Any aVal;
1490 pItem->QueryValue( aVal, pEntry->nMemberId );
1491 aProperties.realloc( nCount + 1 );
1492 auto pProperties = aProperties.getArray();
1493 pProperties[ nCount ].Name = pEntry->aName;
1494 pProperties[ nCount ].Value = aVal;
1495 ++nCount;
1496 }
1497 }
1498 }
1499 }
1500 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"attribute"_ustr, aProperties);
1501 }
1502
1503 StartFormatArea();
1504 }
1505
ApplyUserItemSet(const SfxItemSet & rItemSet)1506 void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
1507 {
1508 // ItemSet from UI, may have different pool
1509
1510 bool bOnlyNotBecauseOfMatrix;
1511 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1512 {
1513 ErrorMessage(STR_PROTECTIONERR);
1514 return;
1515 }
1516
1517 ScPatternAttr aNewAttrs(GetViewData().GetDocument().getCellAttributeHelper());
1518 SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
1519 rNewSet.Put( rItemSet, false );
1520 ApplySelectionPattern( aNewAttrs );
1521
1522 AdjustBlockHeight();
1523 }
1524
GetStyleSheetFromMarked()1525 const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
1526 {
1527 // Don't use UnmarkFiltered in slot state functions, for performance reasons.
1528 // The displayed state is always that of the whole selection including filtered rows.
1529
1530 const ScStyleSheet* pSheet = nullptr;
1531 ScViewData& rViewData = GetViewData();
1532 ScDocument& rDoc = rViewData.GetDocument();
1533 ScMarkData& rMark = rViewData.GetMarkData();
1534
1535 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1536 pSheet = rDoc.GetSelectionStyle( rMark ); // MarkToMulti isn't necessary
1537 else
1538 pSheet = rDoc.GetStyle( rViewData.GetCurX(),
1539 rViewData.GetCurY(),
1540 rViewData.GetTabNo() );
1541
1542 return pSheet;
1543 }
1544
SetStyleSheetToMarked(const SfxStyleSheet * pStyleSheet)1545 void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
1546 {
1547 // not editable because of matrix only? attribute OK nonetheless
1548 bool bOnlyNotBecauseOfMatrix;
1549 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1550 {
1551 ErrorMessage(STR_PROTECTIONERR);
1552 return;
1553 }
1554
1555 if ( !pStyleSheet) return;
1556
1557 ScViewData& rViewData = GetViewData();
1558 ScDocShell* pDocSh = rViewData.GetDocShell();
1559 ScDocument& rDoc = pDocSh->GetDocument();
1560 ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
1561 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1562 SCTAB nTabCount = rDoc.GetTableCount();
1563 bool bRecord = true;
1564 if (!rDoc.IsUndoEnabled())
1565 bRecord = false;
1566
1567 ScDocShellModificator aModificator( *pDocSh );
1568
1569 if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
1570 {
1571 aFuncMark.MarkToMulti();
1572 const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
1573
1574 if ( bRecord )
1575 {
1576 SCTAB nTab = rViewData.GetTabNo();
1577 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1578 pUndoDoc->InitUndo( rDoc, nTab, nTab );
1579 for (const auto& rTab : aFuncMark)
1580 if (rTab != nTab)
1581 pUndoDoc->AddUndoTab( rTab, rTab );
1582
1583 ScRange aCopyRange = aMarkRange;
1584 aCopyRange.aStart.SetTab(0);
1585 aCopyRange.aEnd.SetTab(nTabCount-1);
1586 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aFuncMark );
1587 aFuncMark.MarkToMulti();
1588
1589 OUString aName = pStyleSheet->GetName();
1590 pDocSh->GetUndoManager()->AddUndoAction(
1591 std::make_unique<ScUndoSelectionStyle>( pDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1592 }
1593
1594 rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );
1595
1596 if (!AdjustBlockHeight())
1597 rViewData.GetDocShell()->PostPaint( aMarkRange, PaintPartFlags::Grid );
1598
1599 aFuncMark.MarkToSimple();
1600 }
1601 else
1602 {
1603 SCCOL nCol = rViewData.GetCurX();
1604 SCROW nRow = rViewData.GetCurY();
1605 SCTAB nTab = rViewData.GetTabNo();
1606
1607 if ( bRecord )
1608 {
1609 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1610 pUndoDoc->InitUndo( rDoc, nTab, nTab );
1611 for (const auto& rTab : aFuncMark)
1612 if (rTab != nTab)
1613 pUndoDoc->AddUndoTab( rTab, rTab );
1614
1615 ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
1616 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );
1617
1618 ScRange aMarkRange ( nCol, nRow, nTab );
1619 ScMarkData aUndoMark = aFuncMark;
1620 aUndoMark.SetMultiMarkArea( aMarkRange );
1621
1622 OUString aName = pStyleSheet->GetName();
1623 pDocSh->GetUndoManager()->AddUndoAction(
1624 std::make_unique<ScUndoSelectionStyle>( pDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1625 }
1626
1627 for (const auto& rTab : aFuncMark)
1628 rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );
1629
1630 if (!AdjustBlockHeight())
1631 rViewData.GetDocShell()->PostPaintCell( nCol, nRow, nTab );
1632
1633 }
1634
1635 aModificator.SetDocumentModified();
1636
1637 StartFormatArea();
1638 }
1639
RemoveStyleSheetInUse(const SfxStyleSheetBase * pStyleSheet)1640 void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1641 {
1642 if ( !pStyleSheet) return;
1643
1644 ScViewData& rViewData = GetViewData();
1645 ScDocument& rDoc = rViewData.GetDocument();
1646 ScDocShell* pDocSh = rViewData.GetDocShell();
1647
1648 ScDocShellModificator aModificator( *pDocSh );
1649
1650 ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1651 pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1652 rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
1653 rViewData.GetPPTX(),
1654 rViewData.GetPPTY(),
1655 rViewData.GetZoomX(),
1656 rViewData.GetZoomY() );
1657
1658 pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1659 aModificator.SetDocumentModified();
1660
1661 ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
1662 if (pHdl)
1663 pHdl->ForgetLastPattern();
1664 }
1665
UpdateStyleSheetInUse(const SfxStyleSheetBase * pStyleSheet)1666 void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1667 {
1668 if ( !pStyleSheet) return;
1669
1670 ScViewData& rViewData = GetViewData();
1671 ScDocument& rDoc = rViewData.GetDocument();
1672 ScDocShell* pDocSh = rViewData.GetDocShell();
1673
1674 ScDocShellModificator aModificator( *pDocSh );
1675
1676 ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1677 pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1678 rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
1679 rViewData.GetPPTX(),
1680 rViewData.GetPPTY(),
1681 rViewData.GetZoomX(),
1682 rViewData.GetZoomY() );
1683
1684 pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1685 aModificator.SetDocumentModified();
1686
1687 ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
1688 if (pHdl)
1689 pHdl->ForgetLastPattern();
1690 }
1691
1692
OnLOKInsertDeleteColumn(SCCOL nStartCol,tools::Long nOffset)1693 void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
1694 {
1695 if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1696 return;
1697
1698 SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1699 SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1700 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1701 while (pViewShell)
1702 {
1703 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1704 if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1705 {
1706 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
1707 pPosHelper->invalidateByIndex(nStartCol);
1708
1709 // if we remove a column the cursor position and the current selection
1710 // in other views could need to be moved on the left by one column.
1711 if (pTabViewShell != this)
1712 {
1713 if (pTabViewShell->getPart() == nCurrentTabIndex)
1714 {
1715 SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1716 if (nX > nStartCol)
1717 {
1718 tools::Long offset = nOffset;
1719 if (nOffset + nStartCol > nX)
1720 offset = nX - nStartCol;
1721 else if (nOffset < 0 && nStartCol - nOffset > nX)
1722 offset = -1 * (nX - nStartCol);
1723
1724 ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1725 SCROW nY = pTabViewShell->GetViewData().GetCurY();
1726 pTabViewShell->SetCursor(nX + offset, nY);
1727 if (pInputHdl && pInputHdl->IsInputMode())
1728 {
1729 pInputHdl->SetModified();
1730 }
1731 }
1732
1733 ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1734 aMultiMark.SetMarking( false );
1735
1736 if (aMultiMark.IsMultiMarked() || aMultiMark.IsMarked())
1737 {
1738 aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
1739 pTabViewShell->SetMarkData(aMultiMark);
1740 }
1741 }
1742 else
1743 {
1744 SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
1745 if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
1746 {
1747 pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
1748 }
1749 }
1750 }
1751 }
1752 pViewShell = SfxViewShell::GetNext(*pViewShell);
1753 }
1754 }
1755
OnLOKInsertDeleteRow(SCROW nStartRow,tools::Long nOffset)1756 void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
1757 {
1758 if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1759 return;
1760
1761 SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1762 SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1763 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1764 while (pViewShell)
1765 {
1766 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1767 if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1768 {
1769 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
1770 pPosHelper->invalidateByIndex(nStartRow);
1771
1772 // if we remove a row the cursor position and the current selection
1773 // in other views could need to be moved up by one row.
1774 if (pTabViewShell != this)
1775 {
1776 if (pTabViewShell->getPart() == nCurrentTabIndex)
1777 {
1778 SCROW nY = pTabViewShell->GetViewData().GetCurY();
1779 if (nY > nStartRow)
1780 {
1781 tools::Long offset = nOffset;
1782 if (nOffset + nStartRow > nY)
1783 offset = nY - nStartRow;
1784 else if (nOffset < 0 && nStartRow - nOffset > nY)
1785 offset = -1 * (nY - nStartRow);
1786
1787 ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1788 SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1789 pTabViewShell->SetCursor(nX, nY + offset);
1790 if (pInputHdl && pInputHdl->IsInputMode())
1791 {
1792 pInputHdl->SetModified();
1793 }
1794 }
1795
1796 ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1797 aMultiMark.SetMarking( false );
1798
1799 if (aMultiMark.IsMultiMarked() || aMultiMark.IsMarked())
1800 {
1801 aMultiMark.ShiftRows(pTabViewShell->GetViewData().GetDocument(), nStartRow, nOffset);
1802 pTabViewShell->SetMarkData(aMultiMark);
1803 }
1804 }
1805 else
1806 {
1807 SCROW nY = pTabViewShell->GetViewData().GetCurYForTab(nCurrentTabIndex);
1808 if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
1809 {
1810 pTabViewShell->GetViewData().SetCurYForTab(nY + nOffset, nCurrentTabIndex);
1811 }
1812 }
1813 }
1814 }
1815 pViewShell = SfxViewShell::GetNext(*pViewShell);
1816 }
1817 }
1818
OnLOKSetWidthOrHeight(SCCOLROW nStart,bool bWidth)1819 void ScViewFunc::OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth)
1820 {
1821 if (!comphelper::LibreOfficeKit::isActive())
1822 return;
1823
1824 SCTAB nCurTab = GetViewData().GetTabNo();
1825 SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1826 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1827 while (pViewShell)
1828 {
1829 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1830 if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1831 {
1832 if (bWidth)
1833 {
1834 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurTab))
1835 pPosHelper->invalidateByIndex(nStart);
1836 }
1837 else
1838 {
1839 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurTab))
1840 pPosHelper->invalidateByIndex(nStart);
1841 }
1842 }
1843 pViewShell = SfxViewShell::GetNext(*pViewShell);
1844 }
1845 }
1846
1847 // insert cells - undo OK
1848
InsertCells(InsCellCmd eCmd,bool bRecord,bool bPartOfPaste,size_t nCount)1849 bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste, size_t nCount )
1850 {
1851 ScRange aRange;
1852 ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
1853 if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
1854 {
1855 ScDocShell* pDocSh = GetViewData().GetDocShell();
1856 const ScMarkData& rMark = GetViewData().GetMarkData();
1857 bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste, nCount );
1858 if (bSuccess)
1859 {
1860 ResetAutoSpellForContentChange();
1861 bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
1862 bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
1863
1864 pDocSh->UpdateOle(GetViewData());
1865 CellContentChanged();
1866
1867 if ( bInsertCols || bInsertRows )
1868 {
1869 OUString aOperation = bInsertRows ?
1870 u"insert-rows"_ustr:
1871 u"insert-columns"_ustr;
1872 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1873 }
1874
1875 if (comphelper::LibreOfficeKit::isActive())
1876 {
1877 if (bInsertCols)
1878 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1879
1880 if (bInsertRows)
1881 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1882
1883 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1884 bInsertCols, bInsertRows, true /* bSizes*/,
1885 true /* bHidden */, true /* bFiltered */,
1886 true /* bGroups */, GetViewData().GetTabNo());
1887 }
1888 }
1889 else
1890 {
1891 ErrorMessage(STR_ERR_INSERT_CELLS);
1892 }
1893
1894 OUString aStartAddress = aRange.aStart.GetColRowString();
1895 OUString aEndAddress = aRange.aEnd.GetColRowString();
1896 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"INSERT_CELLS"_ustr);
1897 return bSuccess;
1898 }
1899 else
1900 {
1901 ErrorMessage(STR_NOMULTISELECT);
1902 return false;
1903 }
1904 }
1905
1906 // delete cells - undo OK
1907
DeleteCells(DelCellCmd eCmd)1908 void ScViewFunc::DeleteCells( DelCellCmd eCmd )
1909 {
1910 ScRange aRange;
1911 if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
1912 {
1913 ScDocShell* pDocSh = GetViewData().GetDocShell();
1914 const ScMarkData& rMark = GetViewData().GetMarkData();
1915
1916 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1917 // #i94841# [Collaboration] if deleting rows is rejected, the content is sometimes wrong
1918 if ( pDocSh->IsDocShared() && ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols ) )
1919 {
1920 ScRange aDelRange( aRange.aStart );
1921 SCCOLROW nCount = 0;
1922 if ( eCmd == DelCellCmd::Rows )
1923 {
1924 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
1925 }
1926 else
1927 {
1928 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Col() - aRange.aStart.Col() + 1 );
1929 }
1930 while ( nCount > 0 )
1931 {
1932 pDocSh->GetDocFunc().DeleteCells( aDelRange, &rMark, eCmd, false );
1933 --nCount;
1934 }
1935 }
1936 else
1937 #endif
1938 {
1939 pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
1940 }
1941
1942 ResetAutoSpellForContentChange();
1943 pDocSh->UpdateOle(GetViewData());
1944 CellContentChanged();
1945
1946 if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
1947 {
1948 OUString aOperation = ( eCmd == DelCellCmd::Rows) ?
1949 u"delete-rows"_ustr:
1950 u"delete-columns"_ustr;
1951 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1952 }
1953
1954 // put cursor directly behind deleted range
1955 SCCOL nCurX = GetViewData().GetCurX();
1956 SCROW nCurY = GetViewData().GetCurY();
1957 if ( eCmd==DelCellCmd::CellsLeft || eCmd==DelCellCmd::Cols )
1958 nCurX = aRange.aStart.Col();
1959 else
1960 nCurY = aRange.aStart.Row();
1961 SetCursor( nCurX, nCurY );
1962
1963 if (comphelper::LibreOfficeKit::isActive())
1964 {
1965 bool bColsDeleted = (eCmd == DelCellCmd::Cols);
1966 bool bRowsDeleted = (eCmd == DelCellCmd::Rows);
1967 if (bColsDeleted)
1968 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1969
1970 if (bRowsDeleted)
1971 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1972
1973 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1974 bColsDeleted, bRowsDeleted, true /* bSizes*/,
1975 true /* bHidden */, true /* bFiltered */,
1976 true /* bGroups */, GetViewData().GetTabNo());
1977 }
1978 }
1979 else
1980 {
1981 if (eCmd == DelCellCmd::Cols)
1982 DeleteMulti( false );
1983 else if (eCmd == DelCellCmd::Rows)
1984 DeleteMulti( true );
1985 else
1986 ErrorMessage(STR_NOMULTISELECT);
1987 }
1988
1989 OUString aStartAddress = aRange.aStart.GetColRowString();
1990 OUString aEndAddress = aRange.aEnd.GetColRowString();
1991 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"DELETE_CELLS"_ustr);
1992
1993 Unmark();
1994 }
1995
DeleteMulti(bool bRows)1996 void ScViewFunc::DeleteMulti( bool bRows )
1997 {
1998 ScDocShell* pDocSh = GetViewData().GetDocShell();
1999 ScDocShellModificator aModificator( *pDocSh );
2000 SCTAB nTab = GetViewData().GetTabNo();
2001 ScDocument& rDoc = pDocSh->GetDocument();
2002 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
2003 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
2004
2005 bool bRecord = true;
2006 if (!rDoc.IsUndoEnabled())
2007 bRecord = false;
2008
2009 std::vector<sc::ColRowSpan> aSpans;
2010 if (bRows)
2011 aSpans = aFuncMark.GetMarkedRowSpans();
2012 else
2013 aSpans = aFuncMark.GetMarkedColSpans();
2014
2015 if (aSpans.empty())
2016 {
2017 SCCOLROW nCurPos = bRows ? GetViewData().GetCurY() : GetViewData().GetCurX();
2018 aSpans.emplace_back(nCurPos, nCurPos);
2019 }
2020
2021 // test if allowed
2022
2023 TranslateId pErrorId;
2024 bool bNeedRefresh = false;
2025 for (size_t i = 0, n = aSpans.size(); i < n && !pErrorId; ++i)
2026 {
2027 SCCOLROW nStart = aSpans[i].mnStart;
2028 SCCOLROW nEnd = aSpans[i].mnEnd;
2029
2030 SCCOL nStartCol, nEndCol;
2031 SCROW nStartRow, nEndRow;
2032 if ( bRows )
2033 {
2034 nStartCol = 0;
2035 nEndCol = rDoc.MaxCol();
2036 nStartRow = static_cast<SCROW>(nStart);
2037 nEndRow = static_cast<SCROW>(nEnd);
2038 }
2039 else
2040 {
2041 nStartCol = static_cast<SCCOL>(nStart);
2042 nEndCol = static_cast<SCCOL>(nEnd);
2043 nStartRow = 0;
2044 nEndRow = rDoc.MaxRow();
2045 }
2046
2047 // cell protection (only needed for first range, as all following cells are moved)
2048 if (i == 0)
2049 {
2050 // test to the end of the sheet
2051 ScEditableTester aTester( rDoc, nTab, nStartCol, nStartRow, rDoc.MaxCol(), rDoc.MaxRow() );
2052 if (!aTester.IsEditable())
2053 pErrorId = aTester.GetMessageId();
2054 }
2055
2056 // merged cells
2057 SCCOL nMergeStartX = nStartCol;
2058 SCROW nMergeStartY = nStartRow;
2059 SCCOL nMergeEndX = nEndCol;
2060 SCROW nMergeEndY = nEndRow;
2061 rDoc.ExtendMerge( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
2062 rDoc.ExtendOverlapped( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
2063
2064 if ( nMergeStartX != nStartCol || nMergeStartY != nStartRow )
2065 {
2066 // Disallow deleting parts of a merged cell.
2067 // Deleting the start is allowed (merge is removed), so the end doesn't have to be checked.
2068
2069 pErrorId = STR_MSSG_DELETECELLS_0;
2070 }
2071 if ( nMergeEndX != nEndCol || nMergeEndY != nEndRow )
2072 {
2073 // detect if the start of a merged cell is deleted, so the merge flags can be refreshed
2074
2075 bNeedRefresh = true;
2076 }
2077 }
2078
2079 if (pErrorId)
2080 {
2081 ErrorMessage(pErrorId);
2082 return;
2083 }
2084
2085 // proceed
2086
2087 weld::WaitObject aWait(GetViewData().GetDialogParent()); // important for TrackFormulas in UpdateReference
2088
2089 ResetAutoSpellForContentChange();
2090
2091 ScDocumentUniquePtr pUndoDoc;
2092 std::unique_ptr<ScRefUndoData> pUndoData;
2093 if (bRecord)
2094 {
2095 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2096 pUndoDoc->InitUndo( rDoc, nTab, nTab, !bRows, bRows ); // row height
2097
2098 for (const sc::ColRowSpan & rSpan : aSpans)
2099 {
2100 SCCOLROW nStart = rSpan.mnStart;
2101 SCCOLROW nEnd = rSpan.mnEnd;
2102 if (bRows)
2103 rDoc.CopyToDocument( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, InsertDeleteFlags::ALL,false,*pUndoDoc );
2104 else
2105 rDoc.CopyToDocument( static_cast<SCCOL>(nStart),0,nTab,
2106 static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
2107 InsertDeleteFlags::ALL,false,*pUndoDoc );
2108 }
2109
2110 // all Formulas because of references
2111 SCTAB nTabCount = rDoc.GetTableCount();
2112 pUndoDoc->AddUndoTab( 0, nTabCount-1 );
2113 rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA,false,*pUndoDoc );
2114
2115 pUndoData.reset(new ScRefUndoData( &rDoc ));
2116
2117 rDoc.BeginDrawUndo();
2118 }
2119
2120 std::vector<sc::ColRowSpan>::const_reverse_iterator ri = aSpans.rbegin(), riEnd = aSpans.rend();
2121 aFuncMark.SelectOneTable(nTab);
2122 for (; ri != riEnd; ++ri)
2123 {
2124 SCCOLROW nEnd = ri->mnEnd;
2125 SCCOLROW nStart = ri->mnStart;
2126
2127 if (bRows)
2128 {
2129 rDoc.DeleteObjectsInArea(0, nStart, rDoc.MaxCol(), nEnd, aFuncMark, true);
2130 rDoc.DeleteRow(0, nTab, rDoc.MaxCol(), nTab, nStart, static_cast<SCSIZE>(nEnd - nStart + 1));
2131 }
2132 else
2133 {
2134 rDoc.DeleteObjectsInArea(nStart, 0, nEnd, rDoc.MaxRow(), aFuncMark, true);
2135 rDoc.DeleteCol(0, nTab, rDoc.MaxRow(), nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd - nStart + 1));
2136 }
2137 }
2138
2139 if (bNeedRefresh)
2140 {
2141 SCCOLROW nFirstStart = aSpans[0].mnStart;
2142 SCCOL nStartCol = bRows ? 0 : static_cast<SCCOL>(nFirstStart);
2143 SCROW nStartRow = bRows ? static_cast<SCROW>(nFirstStart) : 0;
2144 SCCOL nEndCol = rDoc.MaxCol();
2145 SCROW nEndRow = rDoc.MaxRow();
2146
2147 rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
2148 rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
2149 }
2150
2151 if (bRecord)
2152 {
2153 pDocSh->GetUndoManager()->AddUndoAction(
2154 std::make_unique<ScUndoDeleteMulti>(
2155 pDocSh, bRows, bNeedRefresh, nTab, std::vector(aSpans), std::move(pUndoDoc), std::move(pUndoData)));
2156 }
2157
2158 if (!AdjustRowHeight(0, rDoc.MaxRow(), true))
2159 {
2160 if (bRows)
2161 {
2162 pDocSh->PostPaint(
2163 0, aSpans[0].mnStart, nTab,
2164 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Left));
2165 }
2166 else
2167 {
2168 pDocSh->PostPaint(
2169 static_cast<SCCOL>(aSpans[0].mnStart), 0, nTab,
2170 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Top));
2171 }
2172 }
2173
2174 aModificator.SetDocumentModified();
2175
2176 CellContentChanged();
2177
2178 // put cursor directly behind the first deleted range
2179 SCCOL nCurX = GetViewData().GetCurX();
2180 SCROW nCurY = GetViewData().GetCurY();
2181 if ( bRows )
2182 nCurY = aSpans[0].mnStart;
2183 else
2184 nCurX = static_cast<SCCOL>(aSpans[0].mnStart);
2185 SetCursor( nCurX, nCurY );
2186
2187 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2188 }
2189
2190 // delete contents
2191
DeleteContents(InsertDeleteFlags nFlags)2192 void ScViewFunc::DeleteContents( InsertDeleteFlags nFlags )
2193 {
2194 ScViewData& rViewData = GetViewData();
2195 rViewData.SetPasteMode( ScPasteFlags::NONE );
2196 rViewData.GetViewShell()->UpdateCopySourceOverlay();
2197
2198 // not editable because of matrix only? attribute OK nonetheless
2199 bool bOnlyNotBecauseOfMatrix;
2200 bool bEditable = SelectionEditable( &bOnlyNotBecauseOfMatrix );
2201 if ( !bEditable )
2202 {
2203 if ( !(bOnlyNotBecauseOfMatrix &&
2204 ((nFlags & (InsertDeleteFlags::ATTRIB | InsertDeleteFlags::EDITATTR)) == nFlags)) )
2205 {
2206 ErrorMessage(bOnlyNotBecauseOfMatrix ? STR_MATRIXFRAGMENTERR : STR_PROTECTIONERR);
2207 return;
2208 }
2209 }
2210
2211 ScRange aMarkRange;
2212 bool bSimple = false;
2213
2214 ScDocument& rDoc = GetViewData().GetDocument();
2215 ScDocShell* pDocSh = GetViewData().GetDocShell();
2216 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
2217 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
2218
2219 bool bRecord =true;
2220 if (!rDoc.IsUndoEnabled())
2221 bRecord = false;
2222
2223 if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
2224 {
2225 aMarkRange.aStart.SetCol(GetViewData().GetCurX());
2226 aMarkRange.aStart.SetRow(GetViewData().GetCurY());
2227 aMarkRange.aStart.SetTab(GetViewData().GetTabNo());
2228 aMarkRange.aEnd = aMarkRange.aStart;
2229 if ( rDoc.HasAttrib( aMarkRange, HasAttrFlags::Merged ) )
2230 {
2231 aFuncMark.SetMarkArea( aMarkRange );
2232 }
2233 else
2234 bSimple = true;
2235 }
2236
2237 HideAllCursors(); // for if summary is cancelled
2238
2239 ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
2240
2241 // Can we really be sure that we can pass the bApi parameter as false to DeleteCell() and
2242 // DeleteContents() here? (Meaning that this is interactive use.) Is this never invoked from
2243 // scripting and whatnot?
2244 if (bSimple)
2245 rDocFunc.DeleteCell(aMarkRange.aStart, aFuncMark, nFlags, bRecord, /*bApi=*/ false);
2246 else
2247 rDocFunc.DeleteContents(aFuncMark, nFlags, bRecord, /*bApi=*/ false);
2248
2249 pDocSh->UpdateOle(GetViewData());
2250
2251 if (ScModelObj* pModelObj = pDocSh->GetModel())
2252 {
2253 ScRangeList aChangeRanges;
2254 if ( bSimple )
2255 {
2256 aChangeRanges.push_back( aMarkRange );
2257 }
2258 else
2259 {
2260 aFuncMark.FillRangeListWithMarks( &aChangeRanges, false );
2261 }
2262
2263 if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
2264 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"delete-content"_ustr);
2265 else if (pModelObj)
2266 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"data-area-invalidate"_ustr);
2267 }
2268
2269 CellContentChanged();
2270 ShowAllCursors();
2271
2272 if ( nFlags & InsertDeleteFlags::ATTRIB )
2273 {
2274 if ( nFlags & InsertDeleteFlags::CONTENTS )
2275 bFormatValid = false;
2276 else
2277 StartFormatArea(); // delete attribute is also attribute-change
2278 }
2279 OUString aStartAddress = aMarkRange.aStart.GetColRowString();
2280 OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
2281 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"DELETE"_ustr);
2282 }
2283
2284 // column width/row height (via header) - undo OK
2285
SetWidthOrHeight(bool bWidth,const std::vector<sc::ColRowSpan> & rRanges,ScSizeMode eMode,sal_uInt16 nSizeTwips,bool bRecord,const ScMarkData * pMarkData)2286 void ScViewFunc::SetWidthOrHeight(
2287 bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
2288 sal_uInt16 nSizeTwips, bool bRecord, const ScMarkData* pMarkData )
2289 {
2290 if (rRanges.empty())
2291 return;
2292
2293 // Use view's mark if none specified, but do not modify the original data,
2294 // i.e. no MarkToMulti() on that.
2295 ScMarkData aMarkData( pMarkData ? *pMarkData : GetViewData().GetMarkData());
2296
2297 ScDocShell* pDocSh = GetViewData().GetDocShell();
2298 ScDocument& rDoc = pDocSh->GetDocument();
2299 SCCOL nCurX = GetViewData().GetCurX();
2300 SCROW nCurY = GetViewData().GetCurY();
2301 SCTAB nFirstTab = aMarkData.GetFirstSelected();
2302 SCTAB nCurTab = GetViewData().GetTabNo();
2303 if (bRecord && !rDoc.IsUndoEnabled())
2304 bRecord = false;
2305
2306 ScDocShellModificator aModificator( *pDocSh );
2307
2308 bool bAllowed = true;
2309 for (const SCTAB& nTab : aMarkData)
2310 {
2311 bAllowed = std::all_of(rRanges.begin(), rRanges.end(),
2312 [&bWidth, &rDoc, &nTab](const sc::ColRowSpan& rRange) {
2313 bool bOnlyMatrix;
2314 bool bIsBlockEditable;
2315 if (bWidth)
2316 bIsBlockEditable = rDoc.IsBlockEditable(nTab, rRange.mnStart, 0, rRange.mnEnd, rDoc.MaxRow(), &bOnlyMatrix);
2317 else
2318 bIsBlockEditable = rDoc.IsBlockEditable(nTab, 0, rRange.mnStart, rDoc.MaxCol(), rRange.mnEnd, &bOnlyMatrix);
2319 return bIsBlockEditable || bOnlyMatrix;
2320 });
2321 if (!bAllowed)
2322 break;
2323 }
2324
2325 // Allow users to resize cols/rows in readonly docs despite the r/o state.
2326 // It is frustrating to be unable to see content in mis-sized cells.
2327 if( !bAllowed && !pDocSh->IsReadOnly() )
2328 {
2329 ErrorMessage(STR_PROTECTIONERR);
2330 return;
2331 }
2332
2333 SCCOLROW nStart = rRanges.front().mnStart;
2334 SCCOLROW nEnd = rRanges.back().mnEnd;
2335
2336 OnLOKSetWidthOrHeight(nStart, bWidth);
2337
2338 bool bFormula = false;
2339 if ( eMode == SC_SIZE_OPTIMAL )
2340 {
2341 const ScViewOptions& rOpts = GetViewData().GetOptions();
2342 bFormula = rOpts.GetOption( VOPT_FORMULAS );
2343 }
2344
2345 ScDocumentUniquePtr pUndoDoc;
2346 std::unique_ptr<ScOutlineTable> pUndoTab;
2347 std::vector<sc::ColRowSpan> aUndoRanges;
2348
2349 if ( bRecord )
2350 {
2351 rDoc.BeginDrawUndo(); // Drawing Updates
2352
2353 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2354 for (const SCTAB& nTab : aMarkData)
2355 {
2356 if (bWidth)
2357 {
2358 if ( nTab == nFirstTab )
2359 pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
2360 else
2361 pUndoDoc->AddUndoTab( nTab, nTab, true );
2362 rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab,
2363 static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
2364 false, *pUndoDoc );
2365 }
2366 else
2367 {
2368 if ( nTab == nFirstTab )
2369 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2370 else
2371 pUndoDoc->AddUndoTab( nTab, nTab, false, true );
2372 rDoc.CopyToDocument( 0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2373 }
2374 }
2375
2376 aUndoRanges = rRanges;
2377
2378 //! outlines from all tab?
2379 ScOutlineTable* pTable = rDoc.GetOutlineTable( nCurTab );
2380 if (pTable)
2381 pUndoTab.reset(new ScOutlineTable( *pTable ));
2382 }
2383
2384 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2385 aMarkData.MarkToMulti();
2386
2387 bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
2388 bool bOutline = false;
2389
2390 for (const SCTAB& nTab : aMarkData)
2391 {
2392 for (const sc::ColRowSpan & rRange : rRanges)
2393 {
2394 SCCOLROW nStartNo = rRange.mnStart;
2395 SCCOLROW nEndNo = rRange.mnEnd;
2396
2397 if ( !bWidth ) // height always blockwise
2398 {
2399 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2400 {
2401 bool bAll = ( eMode==SC_SIZE_OPTIMAL );
2402 bool bFiltered = false;
2403 if (!bAll)
2404 {
2405 // delete CRFlags::ManualSize for all in range,
2406 // then SetOptimalHeight with bShrink = FALSE
2407 for (SCROW nRow = nStartNo; nRow <= nEndNo; ++nRow)
2408 {
2409 SCROW nLastRow = nRow;
2410 if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
2411 {
2412 nRow = nLastRow;
2413 continue;
2414 }
2415
2416 CRFlags nOld = rDoc.GetRowFlags(nRow, nTab);
2417 if (nOld & CRFlags::ManualSize)
2418 rDoc.SetRowFlags(nRow, nTab, nOld & ~CRFlags::ManualSize);
2419 }
2420 }
2421 else
2422 {
2423 SCROW nLastRow = nStartNo;
2424 if (rDoc.RowFiltered(nStartNo, nTab, nullptr, &nLastRow)
2425 || nLastRow < nEndNo)
2426 bFiltered = true;
2427 }
2428
2429
2430 double nPPTX = GetViewData().GetPPTX();
2431 double nPPTY = GetViewData().GetPPTY();
2432 Fraction aZoomX = GetViewData().GetZoomX();
2433 Fraction aZoomY = GetViewData().GetZoomY();
2434
2435 ScSizeDeviceProvider aProv(pDocSh);
2436 if (aProv.IsPrinter())
2437 {
2438 nPPTX = aProv.GetPPTX();
2439 nPPTY = aProv.GetPPTY();
2440 aZoomX = aZoomY = Fraction( 1, 1 );
2441 }
2442
2443 sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
2444 aCxt.setForceAutoSize(bAll);
2445 aCxt.setExtraHeight(nSizeTwips);
2446 rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true);
2447
2448 if (bFiltered)
2449 ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, bShow);
2450
2451 // Manual-Flag already (re)set in SetOptimalHeight in case of bAll=sal_True
2452 // (set for Extra-Height, else reset).
2453 }
2454 else if ( eMode==SC_SIZE_DIRECT )
2455 {
2456 if (nSizeTwips)
2457 {
2458 rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
2459 rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
2460 }
2461
2462 // tdf#36383 - Skip consecutive rows hidden by AutoFilter
2463 ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, nSizeTwips != 0);
2464
2465 if (!bShow && nStartNo <= nCurY && nCurY <= nEndNo && nTab == nCurTab)
2466 {
2467 nCurY = -1;
2468 }
2469 }
2470 else if ( eMode==SC_SIZE_SHOW )
2471 {
2472 rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
2473 }
2474 }
2475 else // column width
2476 {
2477 for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
2478 {
2479 const bool bIsColHidden = rDoc.ColHidden(nCol, nTab);
2480 if ( eMode != SC_SIZE_VISOPT || !bIsColHidden )
2481 {
2482 sal_uInt16 nThisSize = nSizeTwips;
2483
2484 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2485 nThisSize = nSizeTwips + GetOptimalColWidth( nCol, nTab, bFormula );
2486 if ( nThisSize )
2487 rDoc.SetColWidth( nCol, nTab, nThisSize );
2488
2489 // tdf#131073 - Don't show hidden cols after setting optimal col width
2490 if (eMode == SC_SIZE_OPTIMAL)
2491 rDoc.ShowCol(nCol, nTab, !bIsColHidden);
2492 else
2493 rDoc.ShowCol( nCol, nTab, bShow );
2494
2495 if (!bShow && nCol == nCurX && nTab == nCurTab)
2496 {
2497 nCurX = -1;
2498 }
2499 }
2500 }
2501 }
2502
2503 // adjust outline
2504 if (bWidth)
2505 {
2506 if ( rDoc.UpdateOutlineCol( static_cast<SCCOL>(nStartNo),
2507 static_cast<SCCOL>(nEndNo), nTab, bShow ) )
2508 bOutline = true;
2509 }
2510 else
2511 {
2512 if ( rDoc.UpdateOutlineRow( nStartNo, nEndNo, nTab, bShow ) )
2513 bOutline = true;
2514 }
2515 }
2516 rDoc.SetDrawPageSize(nTab);
2517 }
2518
2519 if (!bOutline)
2520 pUndoTab.reset();
2521
2522 if (bRecord)
2523 {
2524 pDocSh->GetUndoManager()->AddUndoAction(
2525 std::make_unique<ScUndoWidthOrHeight>(
2526 pDocSh, aMarkData, nStart, nCurTab, nEnd, nCurTab,
2527 std::move(pUndoDoc), std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
2528 }
2529
2530 if (nCurX < 0)
2531 {
2532 MoveCursorRel( 1, 0, SC_FOLLOW_LINE, false );
2533 }
2534
2535 if (nCurY < 0)
2536 {
2537 MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
2538 }
2539
2540 // fdo#36247 Ensure that the drawing layer's map mode scaling factors match
2541 // the new heights and widths.
2542 GetViewData().GetView()->RefreshZoom();
2543
2544 for (const SCTAB& nTab : aMarkData)
2545 rDoc.UpdatePageBreaks( nTab );
2546
2547 bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
2548 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
2549 bWidth /* bColumns */, !bWidth /* bRows */,
2550 true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
2551 false /* bGroups */, nCurTab);
2552 GetViewData().GetView()->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
2553
2554 {
2555 for (const SCTAB& nTab : aMarkData)
2556 {
2557 if (bWidth)
2558 {
2559 if (rDoc.HasAttrib( static_cast<SCCOL>(nStart),0,nTab,
2560 static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
2561 HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2562 nStart = 0;
2563 if (nStart > 0) // go upwards because of Lines and cursor
2564 --nStart;
2565 pDocSh->PostPaint( static_cast<SCCOL>(nStart), 0, nTab,
2566 rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Top );
2567 }
2568 else
2569 {
2570 if (rDoc.HasAttrib( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2571 nStart = 0;
2572 if (nStart != 0)
2573 --nStart;
2574 pDocSh->PostPaint( 0, nStart, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left );
2575 }
2576 }
2577
2578 pDocSh->UpdateOle(GetViewData());
2579 if( !pDocSh->IsReadOnly() )
2580 aModificator.SetDocumentModified();
2581 }
2582
2583 if ( !bWidth )
2584 return;
2585
2586 ScModelObj* pModelObj = pDocSh->GetModel();
2587
2588 if (!HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
2589 return;
2590
2591 ScRangeList aChangeRanges;
2592 for (const SCTAB& nTab : aMarkData)
2593 {
2594 for (const sc::ColRowSpan & rRange : rRanges)
2595 {
2596 SCCOL nStartCol = rRange.mnStart;
2597 SCCOL nEndCol = rRange.mnEnd;
2598 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
2599 {
2600 aChangeRanges.push_back( ScRange( nCol, 0, nTab ) );
2601 }
2602 }
2603 }
2604 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"column-resize"_ustr);
2605 }
2606
2607 // column width/row height (via marked range)
2608
SetMarkedWidthOrHeight(bool bWidth,ScSizeMode eMode,sal_uInt16 nSizeTwips)2609 void ScViewFunc::SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips )
2610 {
2611 ScMarkData& rMark = GetViewData().GetMarkData();
2612
2613 rMark.MarkToMulti();
2614 if (!rMark.IsMultiMarked())
2615 {
2616 SCCOL nCol = GetViewData().GetCurX();
2617 SCROW nRow = GetViewData().GetCurY();
2618 SCTAB nTab = GetViewData().GetTabNo();
2619 const ScRange aMarkRange( nCol, nRow, nTab);
2620 DoneBlockMode();
2621 InitOwnBlockMode( aMarkRange );
2622 rMark.SetMultiMarkArea( aMarkRange );
2623 MarkDataChanged();
2624 }
2625
2626 std::vector<sc::ColRowSpan> aRanges =
2627 bWidth ? rMark.GetMarkedColSpans() : rMark.GetMarkedRowSpans();
2628
2629 SetWidthOrHeight(bWidth, aRanges, eMode, nSizeTwips);
2630
2631 rMark.MarkToSimple();
2632 }
2633
ModifyCellSize(ScDirection eDir,bool bOptimal)2634 void ScViewFunc::ModifyCellSize( ScDirection eDir, bool bOptimal )
2635 {
2636 ScModule* pScMod = SC_MOD();
2637 bool bAnyEdit = pScMod->IsInputMode();
2638 SCCOL nCol = GetViewData().GetCurX();
2639 SCROW nRow = GetViewData().GetCurY();
2640 SCTAB nTab = GetViewData().GetTabNo();
2641 ScDocShell* pDocSh = GetViewData().GetDocShell();
2642 ScDocument& rDoc = pDocSh->GetDocument();
2643
2644 bool bAllowed, bOnlyMatrix;
2645 if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2646 bAllowed = rDoc.IsBlockEditable( nTab, nCol,0, nCol,rDoc.MaxRow(), &bOnlyMatrix );
2647 else
2648 bAllowed = rDoc.IsBlockEditable( nTab, 0,nRow, rDoc.MaxCol(), nRow, &bOnlyMatrix );
2649 if ( !bAllowed && !bOnlyMatrix )
2650 {
2651 ErrorMessage(STR_PROTECTIONERR);
2652 return;
2653 }
2654
2655 HideAllCursors();
2656
2657 //! step size adjustable
2658 // step size is also minimum
2659 constexpr sal_uInt16 nStepX = STD_COL_WIDTH / 5;
2660 const sal_uInt16 nStepY = rDoc.GetSheetOptimalMinRowHeight(nTab);
2661
2662 sal_uInt16 nWidth = rDoc.GetColWidth( nCol, nTab );
2663 sal_uInt16 nHeight = rDoc.GetRowHeight( nRow, nTab );
2664 std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(0,0));
2665 if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2666 {
2667 if (bOptimal) // width of this single cell
2668 {
2669 if ( bAnyEdit )
2670 {
2671 // when editing the actual entered width
2672 ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2673 if (pHdl)
2674 {
2675 tools::Long nEdit = pHdl->GetTextSize().Width(); // in 0.01 mm
2676
2677 const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2678 const SvxMarginItem& rMItem = pPattern->GetItem(ATTR_MARGIN);
2679 sal_uInt16 nMargin = rMItem.GetLeftMargin() + rMItem.GetRightMargin();
2680 if ( pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Left )
2681 nMargin = sal::static_int_cast<sal_uInt16>(
2682 nMargin + pPattern->GetItem(ATTR_INDENT).GetValue() );
2683
2684 nWidth = std::round(o3tl::convert(nEdit * pDocSh->GetOutputFactor(),
2685 o3tl::Length::mm100, o3tl::Length::twip))
2686 + nMargin + STD_EXTRA_WIDTH;
2687 }
2688 }
2689 else
2690 {
2691 double nPPTX = GetViewData().GetPPTX();
2692 double nPPTY = GetViewData().GetPPTY();
2693 Fraction aZoomX = GetViewData().GetZoomX();
2694 Fraction aZoomY = GetViewData().GetZoomY();
2695
2696 ScSizeDeviceProvider aProv(pDocSh);
2697 if (aProv.IsPrinter())
2698 {
2699 nPPTX = aProv.GetPPTX();
2700 nPPTY = aProv.GetPPTY();
2701 aZoomX = aZoomY = Fraction( 1, 1 );
2702 }
2703
2704 tools::Long nPixel = rDoc.GetNeededSize( nCol, nRow, nTab, aProv.GetDevice(),
2705 nPPTX, nPPTY, aZoomX, aZoomY, true );
2706 sal_uInt16 nTwips = static_cast<sal_uInt16>( nPixel / nPPTX );
2707 if (nTwips != 0)
2708 nWidth = nTwips + STD_EXTRA_WIDTH;
2709 else
2710 nWidth = STD_COL_WIDTH;
2711 }
2712 }
2713 else // increment / decrement
2714 {
2715 if ( eDir == DIR_RIGHT )
2716 nWidth = sal::static_int_cast<sal_uInt16>( nWidth + nStepX );
2717 else if ( nWidth > nStepX )
2718 nWidth = sal::static_int_cast<sal_uInt16>( nWidth - nStepX );
2719 if ( nWidth < nStepX ) nWidth = nStepX;
2720 if ( nWidth > MAX_COL_WIDTH ) nWidth = MAX_COL_WIDTH;
2721 }
2722 aRange[0].mnStart = nCol;
2723 aRange[0].mnEnd = nCol;
2724 SetWidthOrHeight(true, aRange, SC_SIZE_DIRECT, nWidth);
2725
2726 // adjust height of this row if width demands/allows this
2727
2728 if (!bAnyEdit)
2729 {
2730 const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2731 bool bNeedHeight =
2732 pPattern->GetItem( ATTR_LINEBREAK ).GetValue() ||
2733 pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block;
2734 if (bNeedHeight)
2735 AdjustRowHeight( nRow, nRow, true );
2736 }
2737 }
2738 else
2739 {
2740 ScSizeMode eMode;
2741 if (bOptimal)
2742 {
2743 eMode = SC_SIZE_OPTIMAL;
2744 nHeight = 0;
2745 }
2746 else
2747 {
2748 eMode = SC_SIZE_DIRECT;
2749 if ( eDir == DIR_BOTTOM )
2750 nHeight = sal::static_int_cast<sal_uInt16>( nHeight + nStepY );
2751 else if ( nHeight > nStepY )
2752 nHeight = sal::static_int_cast<sal_uInt16>( nHeight - nStepY );
2753 if ( nHeight < nStepY ) nHeight = nStepY;
2754 if ( nHeight > MAX_ROW_HEIGHT ) nHeight = MAX_ROW_HEIGHT;
2755 }
2756 aRange[0].mnStart = nRow;
2757 aRange[0].mnEnd = nRow;
2758 SetWidthOrHeight(false, aRange, eMode, nHeight);
2759 }
2760
2761 if ( bAnyEdit )
2762 {
2763 UpdateEditView();
2764 if ( rDoc.HasAttrib( nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::NeedHeight ) )
2765 {
2766 ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2767 if (pHdl)
2768 pHdl->SetModified(); // so that the height is adjusted with Enter
2769 }
2770 }
2771
2772 ShowAllCursors();
2773 }
2774
ProtectSheet(SCTAB nTab,const ScTableProtection & rProtect)2775 void ScViewFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
2776 {
2777 if (nTab == TABLEID_DOC)
2778 return;
2779
2780 ScMarkData& rMark = GetViewData().GetMarkData();
2781 ScDocShell* pDocSh = GetViewData().GetDocShell();
2782 ScDocument& rDoc = pDocSh->GetDocument();
2783 ScDocFunc &rFunc = pDocSh->GetDocFunc();
2784 bool bUndo(rDoc.IsUndoEnabled());
2785
2786 // modifying several tabs is handled here
2787
2788 if (bUndo)
2789 {
2790 OUString aUndo = ScResId( STR_UNDO_PROTECT_TAB );
2791 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2792 }
2793
2794 for (const auto& rTab : rMark)
2795 {
2796 rFunc.ProtectSheet(rTab, rProtect);
2797 }
2798
2799 if (bUndo)
2800 pDocSh->GetUndoManager()->LeaveListAction();
2801
2802 UpdateLayerLocks(); //! broadcast to all views
2803 }
2804
ProtectDoc(const OUString & rPassword)2805 void ScViewFunc::ProtectDoc( const OUString& rPassword )
2806 {
2807 ScDocShell* pDocSh = GetViewData().GetDocShell();
2808 ScDocFunc &rFunc = pDocSh->GetDocFunc();
2809
2810 rFunc.Protect( TABLEID_DOC, rPassword );
2811
2812 UpdateLayerLocks(); //! broadcast to all views
2813 }
2814
Unprotect(SCTAB nTab,const OUString & rPassword)2815 bool ScViewFunc::Unprotect( SCTAB nTab, const OUString& rPassword )
2816 {
2817 ScMarkData& rMark = GetViewData().GetMarkData();
2818 ScDocShell* pDocSh = GetViewData().GetDocShell();
2819 ScDocument& rDoc = pDocSh->GetDocument();
2820 ScDocFunc &rFunc = pDocSh->GetDocFunc();
2821 bool bChanged = false;
2822 bool bUndo (rDoc.IsUndoEnabled());
2823
2824 if ( nTab == TABLEID_DOC || rMark.GetSelectCount() <= 1 )
2825 {
2826 bChanged = rFunc.Unprotect( nTab, rPassword, false );
2827 if (bChanged && nTab != TABLEID_DOC)
2828 SetTabProtectionSymbol(nTab, false);
2829 }
2830 else
2831 {
2832 // modifying several tabs is handled here
2833
2834 if (bUndo)
2835 {
2836 OUString aUndo = ScResId( STR_UNDO_UNPROTECT_TAB );
2837 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2838 }
2839
2840 for (const auto& rTab : rMark)
2841 {
2842 if ( rFunc.Unprotect( rTab, rPassword, false ) )
2843 {
2844 bChanged = true;
2845 SetTabProtectionSymbol( rTab, false);
2846 }
2847 }
2848
2849 if (bUndo)
2850 pDocSh->GetUndoManager()->LeaveListAction();
2851 }
2852
2853 if (bChanged)
2854 UpdateLayerLocks(); //! broadcast to all views
2855
2856 return bChanged;
2857 }
2858
SetNoteText(const ScAddress & rPos,const OUString & rNoteText)2859 void ScViewFunc::SetNoteText( const ScAddress& rPos, const OUString& rNoteText )
2860 {
2861 GetViewData().GetDocShell()->GetDocFunc().SetNoteText( rPos, rNoteText, false );
2862 }
2863
ReplaceNote(const ScAddress & rPos,const OUString & rNoteText,const OUString * pAuthor,const OUString * pDate)2864 void ScViewFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate )
2865 {
2866 GetViewData().GetDocShell()->GetDocFunc().ReplaceNote( rPos, rNoteText, pAuthor, pDate, false );
2867 }
2868
SetNumberFormat(SvNumFormatType nFormatType,sal_uLong nAdd)2869 void ScViewFunc::SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd )
2870 {
2871 // not editable because of matrix only? attribute OK nonetheless
2872 bool bOnlyNotBecauseOfMatrix;
2873 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2874 {
2875 ErrorMessage(STR_PROTECTIONERR);
2876 return;
2877 }
2878
2879 sal_uInt32 nNumberFormat = 0;
2880 ScViewData& rViewData = GetViewData();
2881 ScDocument& rDoc = rViewData.GetDocument();
2882 SvNumberFormatter* pNumberFormatter = rDoc.GetFormatTable();
2883 LanguageType eLanguage = ScGlobal::eLnge;
2884 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
2885
2886 // always take language from cursor position, even if there is a selection
2887
2888 sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(),
2889 rViewData.GetCurY(),
2890 rViewData.GetTabNo());
2891 const SvNumberformat* pEntry = pNumberFormatter->GetEntry( nCurrentNumberFormat );
2892 if (pEntry)
2893 eLanguage = pEntry->GetLanguage(); // else keep ScGlobal::eLnge
2894
2895 nNumberFormat = pNumberFormatter->GetStandardFormat( nFormatType, eLanguage ) + nAdd;
2896
2897 SfxItemSet& rSet = aNewAttrs.GetItemSet();
2898 rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2899 // ATTR_LANGUAGE_FORMAT not
2900 ApplySelectionPattern( aNewAttrs );
2901 }
2902
SetNumFmtByStr(const OUString & rCode)2903 void ScViewFunc::SetNumFmtByStr( const OUString& rCode )
2904 {
2905 // not editable because of matrix only? attribute OK nonetheless
2906 bool bOnlyNotBecauseOfMatrix;
2907 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2908 {
2909 ErrorMessage(STR_PROTECTIONERR);
2910 return;
2911 }
2912
2913 ScViewData& rViewData = GetViewData();
2914 ScDocument& rDoc = rViewData.GetDocument();
2915 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
2916
2917 // language always from cursor position
2918
2919 sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
2920 rViewData.GetTabNo());
2921 const SvNumberformat* pEntry = pFormatter->GetEntry( nCurrentNumberFormat );
2922 LanguageType eLanguage = pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge;
2923
2924 // determine index for String
2925
2926 bool bOk = true;
2927 sal_uInt32 nNumberFormat = pFormatter->GetEntryKey( rCode, eLanguage );
2928 if ( nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
2929 {
2930 // enter new
2931
2932 OUString aFormat = rCode; // will be changed
2933 sal_Int32 nErrPos = 0;
2934 SvNumFormatType nType = SvNumFormatType::ALL; //! ???
2935 bOk = pFormatter->PutEntry( aFormat, nErrPos, nType, nNumberFormat, eLanguage );
2936 }
2937
2938 if ( bOk ) // valid format?
2939 {
2940 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
2941 SfxItemSet& rSet = aNewAttrs.GetItemSet();
2942 rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2943 rSet.Put( SvxLanguageItem( eLanguage, ATTR_LANGUAGE_FORMAT ) );
2944 ApplySelectionPattern( aNewAttrs );
2945 }
2946
2947 //! else return error / issue warning ???
2948 }
2949
ChangeNumFmtDecimals(bool bIncrement)2950 void ScViewFunc::ChangeNumFmtDecimals( bool bIncrement )
2951 {
2952 // not editable because of matrix only? attribute OK nonetheless
2953 bool bOnlyNotBecauseOfMatrix;
2954 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2955 {
2956 ErrorMessage(STR_PROTECTIONERR);
2957 return;
2958 }
2959
2960 ScDocument& rDoc = GetViewData().GetDocument();
2961 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
2962
2963 SCCOL nCol = GetViewData().GetCurX();
2964 SCROW nRow = GetViewData().GetCurY();
2965 SCTAB nTab = GetViewData().GetTabNo();
2966
2967 sal_uInt32 nOldFormat = rDoc.GetNumberFormat( nCol, nRow, nTab );
2968 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
2969 if (!pOldEntry)
2970 {
2971 OSL_FAIL("numberformat not found !!!");
2972 return;
2973 }
2974
2975 // what have we got here?
2976
2977 sal_uInt32 nNewFormat = nOldFormat;
2978 bool bError = false;
2979
2980 LanguageType eLanguage = pOldEntry->GetLanguage();
2981 bool bThousand, bNegRed;
2982 sal_uInt16 nPrecision, nLeading;
2983 pOldEntry->GetFormatSpecialInfo( bThousand, bNegRed, nPrecision, nLeading );
2984
2985 SvNumFormatType nOldType = pOldEntry->GetType();
2986 if ( SvNumFormatType::ALL == ( nOldType & (
2987 SvNumFormatType::NUMBER | SvNumFormatType::CURRENCY | SvNumFormatType::PERCENT | SvNumFormatType::SCIENTIFIC | SvNumFormatType::TIME ) ) )
2988 {
2989 // date, fraction, logical, text can not be changed
2990 bError = true;
2991 }
2992
2993 //! SvNumberformat has a Member bStandard, but doesn't disclose it
2994 bool bWasStandard = ( nOldFormat == pFormatter->GetStandardIndex( eLanguage ) );
2995 OUString sExponentialStandardFormat = u""_ustr;
2996 if (bWasStandard)
2997 {
2998 // with "Standard" the decimal places depend on cell content
2999 // 0 if empty or text -> no decimal places
3000 double nVal = rDoc.GetValue( ScAddress( nCol, nRow, nTab ) );
3001
3002 // the ways of the Numberformatters are unfathomable, so try:
3003 OUString aOut;
3004 const Color* pCol;
3005 pOldEntry->GetOutputString( nVal, aOut, &pCol, pFormatter->GetNatNum(), pFormatter->GetROLanguageData() );
3006
3007 nPrecision = 0;
3008 // 'E' for exponential is fixed in Numberformatter
3009 sal_Int32 nIndexE = aOut.indexOf('E');
3010 if ( nIndexE >= 0 )
3011 {
3012 sExponentialStandardFormat = aOut.copy( nIndexE ).replace( '-', '+' );
3013 for ( sal_Int32 i=1 ; i<sExponentialStandardFormat.getLength() ; i++ )
3014 {
3015 if ( sExponentialStandardFormat[i] >= '1' && sExponentialStandardFormat[i] <= '9' )
3016 sExponentialStandardFormat = sExponentialStandardFormat.replaceAt( i, 1, u"0" );
3017 }
3018 aOut = aOut.copy( 0, nIndexE ); // remove exponential part
3019 }
3020 OUString aDecSep( pFormatter->GetFormatDecimalSep( nOldFormat ) );
3021 sal_Int32 nPos = aOut.indexOf( aDecSep );
3022 if ( nPos >= 0 )
3023 nPrecision = aOut.getLength() - nPos - aDecSep.getLength();
3024 // else keep 0
3025 }
3026 else
3027 {
3028 if ( (nOldType & SvNumFormatType::SCIENTIFIC) && !bThousand &&
3029 (pOldEntry->GetFormatIntegerDigits()%3 == 0) && pOldEntry->GetFormatIntegerDigits() > 0 )
3030 bThousand = true;
3031 }
3032
3033 if (!bError)
3034 {
3035 if (bIncrement)
3036 {
3037 if (nPrecision<20)
3038 ++nPrecision; // increment
3039 else
3040 bError = true; // 20 is maximum
3041 }
3042 else
3043 {
3044 if (nPrecision)
3045 --nPrecision; // decrement
3046 else
3047 bError = true; // 0 is minimum
3048 }
3049 }
3050
3051 if (!bError)
3052 {
3053 OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLanguage,
3054 bThousand, bNegRed,
3055 nPrecision, nLeading)
3056 + sExponentialStandardFormat;
3057
3058 nNewFormat = pFormatter->GetEntryKey( aNewPicture, eLanguage );
3059 if ( nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
3060 {
3061 sal_Int32 nErrPos = 0;
3062 SvNumFormatType nNewType = SvNumFormatType::ALL;
3063 bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos,
3064 nNewType, nNewFormat, eLanguage );
3065 OSL_ENSURE( bOk, "incorrect numberformat generated" );
3066 if (!bOk)
3067 bError = true;
3068 }
3069 }
3070
3071 if (!bError)
3072 {
3073 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
3074 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3075 rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
3076 // ATTR_LANGUAGE_FORMAT not
3077 ApplySelectionPattern( aNewAttrs );
3078 }
3079 }
3080
ChangeIndent(bool bIncrement)3081 void ScViewFunc::ChangeIndent( bool bIncrement )
3082 {
3083 ScViewData& rViewData = GetViewData();
3084 ScDocShell* pDocSh = rViewData.GetDocShell();
3085 ScMarkData& rMark = rViewData.GetMarkData();
3086
3087 ScMarkData aWorkMark = rMark;
3088 ScViewUtil::UnmarkFiltered( aWorkMark, pDocSh->GetDocument() );
3089 aWorkMark.MarkToMulti();
3090 if (!aWorkMark.IsMultiMarked())
3091 {
3092 SCCOL nCol = rViewData.GetCurX();
3093 SCROW nRow = rViewData.GetCurY();
3094 SCTAB nTab = rViewData.GetTabNo();
3095 aWorkMark.SetMultiMarkArea( ScRange(nCol,nRow,nTab) );
3096 }
3097
3098 bool bSuccess = pDocSh->GetDocFunc().ChangeIndent( aWorkMark, bIncrement, false );
3099 if (bSuccess)
3100 {
3101 pDocSh->UpdateOle(rViewData);
3102 StartFormatArea();
3103
3104 // stuff for sidebar panels
3105 SfxBindings& rBindings = GetViewData().GetBindings();
3106 rBindings.Invalidate( SID_H_ALIGNCELL );
3107 rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
3108 }
3109 }
3110
InsertName(const OUString & rName,const OUString & rSymbol,const OUString & rType)3111 bool ScViewFunc::InsertName( const OUString& rName, const OUString& rSymbol,
3112 const OUString& rType )
3113 {
3114 // Type = P,R,C,F (and combinations)
3115 //! undo...
3116
3117 bool bOk = false;
3118 ScDocShell* pDocSh = GetViewData().GetDocShell();
3119 ScDocument& rDoc = pDocSh->GetDocument();
3120 SCTAB nTab = GetViewData().GetTabNo();
3121 ScRangeName* pList = rDoc.GetRangeName();
3122
3123 ScRangeData::Type nType = ScRangeData::Type::Name;
3124 auto pNewEntry = std::make_unique<ScRangeData>(
3125 rDoc, rName, rSymbol, ScAddress( GetViewData().GetCurX(),
3126 GetViewData().GetCurY(), nTab), nType );
3127 OUString aUpType = rType.toAsciiUpperCase();
3128 if ( aUpType.indexOf( 'P' ) != -1 )
3129 nType |= ScRangeData::Type::PrintArea;
3130 if ( aUpType.indexOf( 'R' ) != -1 )
3131 nType |= ScRangeData::Type::RowHeader;
3132 if ( aUpType.indexOf( 'C' ) != -1 )
3133 nType |= ScRangeData::Type::ColHeader;
3134 if ( aUpType.indexOf( 'F' ) != -1 )
3135 nType |= ScRangeData::Type::Criteria;
3136 pNewEntry->AddType(nType);
3137
3138 if ( pNewEntry->GetErrCode() == FormulaError::NONE ) // text valid?
3139 {
3140 ScDocShellModificator aModificator( *pDocSh );
3141
3142 rDoc.PreprocessRangeNameUpdate();
3143
3144 // input available yet? Then remove beforehand (=change)
3145 ScRangeData* pData = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
3146 if (pData)
3147 { // take old Index
3148 pNewEntry->SetIndex(pData->GetIndex());
3149 pList->erase(*pData);
3150 }
3151
3152 // don't delete, insert took ownership, even on failure!
3153 if ( pList->insert( pNewEntry.release() ) )
3154 bOk = true;
3155
3156 rDoc.CompileHybridFormula();
3157
3158 aModificator.SetDocumentModified();
3159 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
3160 }
3161
3162 return bOk;
3163 }
3164
CreateNames(CreateNameFlags nFlags)3165 void ScViewFunc::CreateNames( CreateNameFlags nFlags )
3166 {
3167 bool bDone = false;
3168 ScRange aRange;
3169 if ( GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE )
3170 bDone = GetViewData().GetDocShell()->GetDocFunc().CreateNames( aRange, nFlags, false );
3171
3172 if (!bDone)
3173 ErrorMessage(STR_CREATENAME_MARKERR);
3174 }
3175
GetCreateNameFlags()3176 CreateNameFlags ScViewFunc::GetCreateNameFlags()
3177 {
3178 CreateNameFlags nFlags = CreateNameFlags::NONE;
3179
3180 SCCOL nStartCol, nEndCol;
3181 SCROW nStartRow, nEndRow;
3182 SCTAB nDummy;
3183 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nDummy,nEndCol,nEndRow,nDummy) == SC_MARK_SIMPLE)
3184 {
3185 ScDocument& rDoc = GetViewData().GetDocument();
3186 SCTAB nTab = GetViewData().GetTabNo();
3187 bool bOk;
3188 SCCOL i;
3189 SCROW j;
3190
3191 bOk = true;
3192 SCCOL nFirstCol = nStartCol;
3193 SCCOL nLastCol = nEndCol;
3194 if (nStartCol+1 < nEndCol) { ++nFirstCol; --nLastCol; }
3195 for (i=nFirstCol; i<=nLastCol && bOk; i++)
3196 if (!rDoc.HasStringData( i,nStartRow,nTab ))
3197 bOk = false;
3198 if (bOk)
3199 nFlags |= CreateNameFlags::Top;
3200 else // Bottom only if not Top
3201 {
3202 bOk = true;
3203 for (i=nFirstCol; i<=nLastCol && bOk; i++)
3204 if (!rDoc.HasStringData( i,nEndRow,nTab ))
3205 bOk = false;
3206 if (bOk)
3207 nFlags |= CreateNameFlags::Bottom;
3208 }
3209
3210 bOk = true;
3211 SCROW nFirstRow = nStartRow;
3212 SCROW nLastRow = nEndRow;
3213 if (nStartRow+1 < nEndRow) { ++nFirstRow; --nLastRow; }
3214 for (j=nFirstRow; j<=nLastRow && bOk; j++)
3215 if (!rDoc.HasStringData( nStartCol,j,nTab ))
3216 bOk = false;
3217 if (bOk)
3218 nFlags |= CreateNameFlags::Left;
3219 else // Right only if not Left
3220 {
3221 bOk = true;
3222 for (j=nFirstRow; j<=nLastRow && bOk; j++)
3223 if (!rDoc.HasStringData( nEndCol,j,nTab ))
3224 bOk = false;
3225 if (bOk)
3226 nFlags |= CreateNameFlags::Right;
3227 }
3228 }
3229
3230 if (nStartCol == nEndCol)
3231 nFlags &= ~CreateNameFlags( CreateNameFlags::Left | CreateNameFlags::Right );
3232 if (nStartRow == nEndRow)
3233 nFlags &= ~CreateNameFlags( CreateNameFlags::Top | CreateNameFlags::Bottom );
3234
3235 return nFlags;
3236 }
3237
InsertNameList()3238 void ScViewFunc::InsertNameList()
3239 {
3240 ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
3241 ScDocShell* pDocSh = GetViewData().GetDocShell();
3242 if ( pDocSh->GetDocFunc().InsertNameList( aPos, false ) )
3243 pDocSh->UpdateOle(GetViewData());
3244 }
3245
UpdateSelectionArea(const ScMarkData & rSel,ScPatternAttr * pAttr)3246 void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr )
3247 {
3248 ScDocShell* pDocShell = GetViewData().GetDocShell();
3249 ScRange aMarkRange;
3250 if (rSel.IsMultiMarked() )
3251 aMarkRange = rSel.GetMultiMarkArea();
3252 else
3253 aMarkRange = rSel.GetMarkArea();
3254
3255 bool bSetLines = false;
3256 bool bSetAlign = false;
3257 if ( pAttr )
3258 {
3259 const SfxItemSet& rNewSet = pAttr->GetItemSet();
3260 bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
3261 rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
3262 bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
3263 }
3264
3265 sal_uInt16 nExtFlags = 0;
3266 if ( bSetLines )
3267 nExtFlags |= SC_PF_LINES;
3268 if ( bSetAlign )
3269 nExtFlags |= SC_PF_WHOLEROWS;
3270
3271 SCCOL nStartCol = aMarkRange.aStart.Col();
3272 SCROW nStartRow = aMarkRange.aStart.Row();
3273 SCTAB nStartTab = aMarkRange.aStart.Tab();
3274 SCCOL nEndCol = aMarkRange.aEnd.Col();
3275 SCROW nEndRow = aMarkRange.aEnd.Row();
3276 SCTAB nEndTab = aMarkRange.aEnd.Tab();
3277 pDocShell->PostPaint( nStartCol, nStartRow, nStartTab,
3278 nEndCol, nEndRow, nEndTab,
3279 PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
3280 ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
3281 pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
3282 }
3283
3284 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3285