Coverage Report

Created: 2026-02-14 09:37

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