Coverage Report

Created: 2026-09-28 10:59

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