Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/docshell/docsh6.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source 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 <scitems.hxx>
21
22
#include <svx/pageitem.hxx>
23
#include <sfx2/linkmgr.hxx>
24
25
#include <docsh.hxx>
26
27
#include <stlpool.hxx>
28
#include <global.hxx>
29
#include <viewdata.hxx>
30
#include <tabvwsh.hxx>
31
#include <tablink.hxx>
32
#include <globstr.hrc>
33
#include <scresid.hxx>
34
#include <scmod.hxx>
35
#include <compiler.hxx>
36
#include <interpre.hxx>
37
#include <formulaopt.hxx>
38
39
#include <vcl/svapp.hxx>
40
#include <vcl/weld/MessageDialog.hxx>
41
42
#include <memory>
43
#include <utility>
44
45
namespace {
46
47
struct ScStylePair
48
{
49
    SfxStyleSheetBase *pSource;
50
    SfxStyleSheetBase *pDest;
51
};
52
53
}
54
55
//  Ole
56
57
void ScDocShell::SetVisArea( const tools::Rectangle & rVisArea )
58
5.42k
{
59
    //  with the SnapVisArea call in SetVisAreaOrSize, it's safe to always
60
    //  use both the size and position of the VisArea
61
5.42k
    SetVisAreaOrSize( rVisArea );
62
5.42k
}
63
64
static void lcl_SetTopRight( tools::Rectangle& rRect, const Point& rPos )
65
1
{
66
1
    Size aSize = rRect.GetSize();
67
1
    rRect.SetRight( rPos.X() );
68
1
    rRect.SetLeft( rPos.X() - aSize.Width() + 1 );
69
1
    rRect.SetTop( rPos.Y() );
70
1
    rRect.SetBottom( rPos.Y() + aSize.Height() - 1 );
71
1
}
72
73
void ScDocShell::SetVisAreaOrSize( const tools::Rectangle& rVisArea )
74
33.6k
{
75
33.6k
    bool bNegativePage = m_pDocument->IsNegativePage( m_pDocument->GetVisibleTab() );
76
77
33.6k
    tools::Rectangle aArea = rVisArea;
78
    // when loading, don't check for negative values, because the sheet orientation
79
    // might be set later
80
33.6k
    if ( !m_pDocument->IsImportingXML() )
81
32.0k
    {
82
32.0k
        if ( ( bNegativePage ? (aArea.Right() > 0) : (aArea.Left() < 0) ) || aArea.Top() < 0 )
83
1
        {
84
            //  VisArea start position can't be negative.
85
            //  Move the VisArea, otherwise only the upper left position would
86
            //  be changed in SnapVisArea, and the size would be wrong.
87
88
1
            Point aNewPos( 0, std::max( aArea.Top(), tools::Long(0) ) );
89
1
            if ( bNegativePage )
90
1
            {
91
1
                aNewPos.setX( std::min( aArea.Right(), tools::Long(0) ) );
92
1
                lcl_SetTopRight( aArea, aNewPos );
93
1
            }
94
0
            else
95
0
            {
96
0
                aNewPos.setX( std::max( aArea.Left(), tools::Long(0) ) );
97
0
                aArea.SetPos( aNewPos );
98
0
            }
99
1
        }
100
32.0k
    }
101
102
    //      adjust position here!
103
104
    //  when loading an ole object, the VisArea is set from the document's
105
    //  view settings and must be used as-is (document content may not be complete yet).
106
33.6k
    if ( !m_pDocument->IsImportingXML() )
107
32.0k
        SnapVisArea( aArea );
108
109
    //TODO/LATER: it's unclear which IPEnv is used here
110
    /*
111
    SvInPlaceEnvironment* pEnv = GetIPEnv();
112
    if (pEnv)
113
    {
114
        vcl::Window* pWin = pEnv->GetEditWin();
115
        pEnv->MakeScale( aArea.GetSize(), MapUnit::Map100thMM,
116
                            pWin->LogicToPixel( aArea.GetSize() ) );
117
    } */
118
119
    //TODO/LATER: formerly in SvInplaceObject
120
33.6k
    SfxObjectShell::SetVisArea( aArea );
121
122
33.6k
    if (m_bIsInplace)                     // adjust zoom in the InPlace View
123
33.6k
    {
124
33.6k
        ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
125
33.6k
        if (pViewSh)
126
0
        {
127
0
            if (pViewSh->GetViewData().GetDocShell() == this)
128
0
                pViewSh->UpdateOleZoom();
129
0
        }
130
33.6k
    }
131
132
33.6k
    if (!m_pDocument->IsEmbedded())
133
33.6k
        return;
134
135
0
    ScRange aOld;
136
0
    m_pDocument->GetEmbedded( aOld);
137
0
    m_pDocument->SetEmbedded( m_pDocument->GetVisibleTab(), aArea );
138
0
    ScRange aNew;
139
0
    m_pDocument->GetEmbedded( aNew);
140
0
    if (aOld != aNew)
141
0
        PostPaint(0,0,0,m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB,PaintPartFlags::Grid);
142
143
    //TODO/LATER: currently not implemented
144
    //ViewChanged( ASPECT_CONTENT );          // show in the container as well
145
0
}
146
147
bool ScDocShell::IsOle() const
148
0
{
149
0
    return (GetCreateMode() == SfxObjectCreateMode::EMBEDDED);
150
0
}
151
152
void ScDocShell::UpdateOle(const ScViewData& rViewData, bool bSnapSize)
153
0
{
154
    //  if it isn't Ole at all, one can be spared the calculations
155
    //  (VisArea will then be reset at the save)
156
157
0
    if (GetCreateMode() == SfxObjectCreateMode::STANDARD)
158
0
        return;
159
160
0
    tools::Rectangle aOldArea = SfxObjectShell::GetVisArea();
161
0
    tools::Rectangle aNewArea = aOldArea;
162
163
0
    bool bEmbedded = m_pDocument->IsEmbedded();
164
0
    if (bEmbedded)
165
0
        aNewArea = m_pDocument->GetEmbeddedRect();
166
0
    else
167
0
    {
168
0
        SCTAB nTab = rViewData.CurrentTabForData();
169
0
        if ( nTab != m_pDocument->GetVisibleTab() )
170
0
            m_pDocument->SetVisibleTab( nTab );
171
172
0
        bool bNegativePage = m_pDocument->IsNegativePage( nTab );
173
0
        SCCOL nX = rViewData.GetPosX(SC_SPLIT_LEFT);
174
0
        if ( nX != m_pDocument->GetPosLeft() )
175
0
            m_pDocument->SetPosLeft( nX );
176
0
        SCROW nY = rViewData.GetPosY(SC_SPLIT_BOTTOM);
177
0
        if ( nY != m_pDocument->GetPosTop() )
178
0
            m_pDocument->SetPosTop( nY );
179
0
        tools::Rectangle aMMRect = m_pDocument->GetMMRect( nX,nY, nX,nY, nTab );
180
0
        if (bNegativePage)
181
0
            lcl_SetTopRight( aNewArea, aMMRect.TopRight() );
182
0
        else
183
0
            aNewArea.SetPos( aMMRect.TopLeft() );
184
0
        if (bSnapSize)
185
0
            SnapVisArea(aNewArea);            // uses the new VisibleTab
186
0
    }
187
188
0
    if (aNewArea != aOldArea)
189
0
        SetVisAreaOrSize( aNewArea ); // the start must also be adjusted here
190
0
}
191
192
//  Style stuff for Organizer, etc.
193
194
SfxStyleSheetBasePool* ScDocShell::GetStyleSheetPool()
195
0
{
196
0
    return static_cast<SfxStyleSheetBasePool*>(m_pDocument->GetStyleSheetPool());
197
0
}
198
199
//  After loading styles from another document (LoadStyles, Insert), the SetItems
200
//  (ATTR_PAGE_HEADERSET, ATTR_PAGE_FOOTERSET) must be converted to the correct pool
201
//  before the source pool is deleted.
202
203
static void lcl_AdjustPool( SfxStyleSheetBasePool* pStylePool )
204
0
{
205
0
    SfxStyleSheetBase *pStyle = pStylePool->First(SfxStyleFamily::Page);
206
0
    while ( pStyle )
207
0
    {
208
0
        SfxItemSet& rStyleSet = pStyle->GetItemSet();
209
210
0
        if (const SvxSetItem* pItem = rStyleSet.GetItemIfSet(ATTR_PAGE_HEADERSET,false))
211
0
        {
212
0
            const SfxItemSet& rSrcSet = pItem->GetItemSet();
213
0
            SfxItemSet aDestSet(*rStyleSet.GetPool(),rSrcSet.GetRanges());
214
0
            aDestSet.Put(rSrcSet);
215
0
            rStyleSet.Put(SvxSetItem(ATTR_PAGE_HEADERSET, std::move(aDestSet)));
216
0
        }
217
0
        if (const SvxSetItem* pItem = rStyleSet.GetItemIfSet(ATTR_PAGE_FOOTERSET,false))
218
0
        {
219
0
            const SfxItemSet& rSrcSet = pItem->GetItemSet();
220
0
            SfxItemSet aDestSet(*rStyleSet.GetPool(),rSrcSet.GetRanges());
221
0
            aDestSet.Put(rSrcSet);
222
0
            rStyleSet.Put(SvxSetItem(ATTR_PAGE_FOOTERSET, std::move(aDestSet)));
223
0
        }
224
225
0
        pStyle = pStylePool->Next();
226
0
    }
227
0
}
228
229
void ScDocShell::LoadStyles( SfxObjectShell &rSource )
230
0
{
231
0
    m_pDocument->getCellAttributeHelper().AllStylesToNames();
232
233
0
    SfxObjectShell::LoadStyles(rSource);
234
0
    lcl_AdjustPool( GetStyleSheetPool() );      // adjust SetItems
235
236
0
    m_pDocument->getCellAttributeHelper().UpdateAllStyleSheets(*m_pDocument);
237
238
0
    UpdateAllRowHeights();
239
240
        //  Paint
241
242
0
    PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, PaintPartFlags::Grid | PaintPartFlags::Left );
243
0
}
244
245
void ScDocShell::LoadStylesArgs( ScDocShell& rSource, bool bReplace, bool bCellStyles, bool bPageStyles )
246
0
{
247
    //  similar to LoadStyles, but with selectable behavior for XStyleLoader::loadStylesFromURL call
248
249
0
    if ( !bCellStyles && !bPageStyles )     // nothing to do
250
0
        return;
251
252
0
    ScStyleSheetPool* pSourcePool = rSource.GetDocument().GetStyleSheetPool();
253
0
    ScStyleSheetPool* pDestPool = m_pDocument->GetStyleSheetPool();
254
255
0
    SfxStyleFamily eFamily = bCellStyles ?
256
0
            ( bPageStyles ? SfxStyleFamily::All : SfxStyleFamily::Para ) :
257
0
            SfxStyleFamily::Page;
258
0
    SfxStyleSheetIterator aIter( pSourcePool, eFamily );
259
0
    sal_uInt16 nSourceCount = aIter.Count();
260
0
    if ( nSourceCount == 0 )
261
0
        return;                             // no source styles
262
263
0
    std::unique_ptr<ScStylePair[]> pStyles(new ScStylePair[ nSourceCount ]);
264
0
    sal_uInt16 nFound = 0;
265
266
    //  first create all new styles
267
268
0
    SfxStyleSheetBase* pSourceStyle = aIter.First();
269
0
    while (pSourceStyle)
270
0
    {
271
0
        OUString aName = pSourceStyle->GetName();
272
0
        SfxStyleSheetBase* pDestStyle = pDestPool->Find( pSourceStyle->GetName(), pSourceStyle->GetFamily() );
273
0
        if ( pDestStyle )
274
0
        {
275
            // touch existing styles only if replace flag is set
276
0
            if ( bReplace )
277
0
            {
278
0
                pStyles[nFound].pSource = pSourceStyle;
279
0
                pStyles[nFound].pDest = pDestStyle;
280
0
                ++nFound;
281
0
            }
282
0
        }
283
0
        else
284
0
        {
285
0
            pStyles[nFound].pSource = pSourceStyle;
286
0
            pStyles[nFound].pDest = &pDestPool->Make( aName, pSourceStyle->GetFamily(), pSourceStyle->GetMask() );
287
0
            ++nFound;
288
0
        }
289
290
0
        pSourceStyle = aIter.Next();
291
0
    }
292
293
    //  then copy contents (after inserting all styles, for parent etc.)
294
295
0
    for ( sal_uInt16 i = 0; i < nFound; ++i )
296
0
    {
297
0
        pStyles[i].pDest->GetItemSet().PutExtended(
298
0
            pStyles[i].pSource->GetItemSet(), SfxItemState::INVALID, SfxItemState::DEFAULT);
299
0
        if(pStyles[i].pSource->HasParentSupport())
300
0
            pStyles[i].pDest->SetParent(pStyles[i].pSource->GetParent());
301
        // follow is never used
302
0
    }
303
304
0
    lcl_AdjustPool( GetStyleSheetPool() );      // adjust SetItems
305
0
    UpdateAllRowHeights();
306
0
    PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, PaintPartFlags::Grid | PaintPartFlags::Left );      // Paint
307
0
}
308
309
void ScDocShell::ReconnectDdeLink(SfxObjectShell& rServer)
310
0
{
311
0
    ::sfx2::LinkManager* pLinkManager = m_pDocument->GetLinkManager();
312
0
    if (!pLinkManager)
313
0
        return;
314
315
0
    pLinkManager->ReconnectDdeLink(rServer);
316
0
}
317
318
void ScDocShell::UpdateLinks()
319
2.33k
{
320
2.33k
    typedef std::unordered_set<OUString> StrSetType;
321
322
2.33k
    sfx2::LinkManager* pLinkManager = m_pDocument->GetLinkManager();
323
2.33k
    StrSetType aNames;
324
325
    // out with the no longer used links
326
327
2.33k
    size_t nCount = pLinkManager->GetLinks().size();
328
4.83k
    for (size_t k=nCount; k>0; )
329
2.50k
    {
330
2.50k
        --k;
331
2.50k
        ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[k].get();
332
2.50k
        if (ScTableLink* pTabLink = dynamic_cast<ScTableLink*>(pBase))
333
0
        {
334
0
            if (pTabLink->IsUsed())
335
0
                aNames.insert(pTabLink->GetFileName());
336
0
            else        // no longer used -> delete
337
0
            {
338
0
                pTabLink->SetAddUndo(true);
339
0
                pLinkManager->Remove(k);
340
0
            }
341
0
        }
342
2.50k
    }
343
344
    // enter new links
345
346
2.33k
    SCTAB nTabCount = m_pDocument->GetTableCount();
347
5.82k
    for (SCTAB i = 0; i < nTabCount; ++i)
348
3.49k
    {
349
3.49k
        if (!m_pDocument->IsLinked(i))
350
3.49k
            continue;
351
352
0
        OUString aDocName = m_pDocument->GetLinkDoc(i);
353
0
        OUString aFltName = m_pDocument->GetLinkFlt(i);
354
0
        OUString aOptions = m_pDocument->GetLinkOpt(i);
355
0
        sal_Int32 nRefresh  = m_pDocument->GetLinkRefreshDelay(i);
356
0
        bool bThere = false;
357
0
        for (SCTAB j = 0; j < i && !bThere; ++j)                // several times in the document?
358
0
        {
359
0
            if (m_pDocument->IsLinked(j)
360
0
                    && m_pDocument->GetLinkDoc(j) == aDocName
361
0
                    && m_pDocument->GetLinkFlt(j) == aFltName
362
0
                    && m_pDocument->GetLinkOpt(j) == aOptions)
363
                    // Ignore refresh delay in compare, it should be the
364
                    // same for identical links and we don't want dupes
365
                    // if it ain't.
366
0
                bThere = true;
367
0
        }
368
369
0
        if (!bThere)                                        // already entered as filter?
370
0
        {
371
0
            if (!aNames.insert(aDocName).second)
372
0
                bThere = true;
373
0
        }
374
375
0
        if (!bThere)
376
0
        {
377
0
            ScTableLink* pLink = new ScTableLink( *this, aDocName, aFltName, aOptions, nRefresh );
378
0
            pLink->SetInCreate(true);
379
0
            pLinkManager->InsertFileLink(*pLink, sfx2::SvBaseLinkObjectType::ClientFile, aDocName, &aFltName);
380
0
            pLink->Update();
381
0
            pLink->SetInCreate(false);
382
0
        }
383
0
    }
384
2.33k
}
385
386
void ScDocShell::ReloadTabLinks()
387
0
{
388
0
    sfx2::LinkManager* pLinkManager = m_pDocument->GetLinkManager();
389
390
0
    bool bAny = false;
391
0
    size_t nCount = pLinkManager->GetLinks().size();
392
0
    for (size_t i=0; i<nCount; i++ )
393
0
    {
394
0
        ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[i].get();
395
0
        if (ScTableLink* pTabLink = dynamic_cast<ScTableLink*>(pBase))
396
0
        {
397
//          pTabLink->SetAddUndo(sal_False);        //! merge Undos
398
399
            // Painting only after Update() makes no sense:
400
            // ScTableLink::Refresh() will post a Paint only is bDoPaint is true
401
            // pTabLink->SetPaint(false);          //  Paint only once at the end
402
0
            pTabLink->Update();
403
            //pTabLink->SetPaint(true);
404
//          pTabLink->SetAddUndo(sal_True);
405
0
            bAny = true;
406
0
        }
407
0
    }
408
409
0
    if ( bAny )
410
0
    {
411
        //  Paint only once
412
0
        PostPaint( ScRange(0,0,0,m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB),
413
0
                                    PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left );
414
415
0
        SetDocumentModified();
416
0
    }
417
0
}
418
419
void ScDocShell::SetFormulaOptions( const ScFormulaOptions& rOpt, bool bForLoading )
420
28.2k
{
421
28.2k
    m_pDocument->SetGrammar( rOpt.GetFormulaSyntax() );
422
423
    // This is nasty because it resets module globals from within a docshell!
424
    // For actual damage caused see fdo#82183 where an unconditional
425
    // ScGlobal::ResetFunctionList() (without checking GetUseEnglishFuncName())
426
    // lead to a crash because the function list was still used by the Formula
427
    // Wizard when loading the second document.
428
    // Do the stupid stuff only when we're not called while loading a document.
429
430
    /* TODO: bForLoading is a workaround, rather get rid of setting any
431
     * globals from per document instances like ScDocShell. */
432
433
    /* XXX  this is utter crap, we rely on the options being set here at least
434
     * once, for the very first document, empty or loaded. */
435
28.2k
    static bool bInitOnce = true;
436
437
    // LOKit may need to juggle different symbols lists for different users so a
438
    // single load is not enough, otherwise the wrong separators may be expected
439
    // for the users locale
440
28.2k
    if (!bForLoading || bInitOnce || comphelper::LibreOfficeKit::isActive())
441
28.2k
    {
442
28.2k
        bool bForceInit = bInitOnce;
443
28.2k
        bInitOnce = false;
444
28.2k
        if (bForceInit || rOpt.GetUseEnglishFuncName() != ScModule::get()->GetFormulaOptions().GetUseEnglishFuncName())
445
4
        {
446
            // This needs to be called first since it may re-initialize the entire
447
            // opcode map.
448
4
            if (rOpt.GetUseEnglishFuncName())
449
0
            {
450
                // switch native symbols to English.
451
0
                ScAddress aAddress;
452
0
                ScCompiler aComp( *m_pDocument, aAddress);
453
0
                ScCompiler::OpCodeMapPtr xMap = aComp.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH);
454
0
                ScCompiler::SetNativeSymbols(xMap);
455
0
            }
456
4
            else
457
                // re-initialize native symbols with localized function names.
458
4
                ScCompiler::ResetNativeSymbols();
459
460
            // Force re-population of function names for the function wizard, function tip etc.
461
4
            ScGlobal::ResetFunctionList();
462
4
        }
463
464
        // Update the separators.
465
28.2k
        ScCompiler::UpdateSeparatorsNative(
466
28.2k
                rOpt.GetFormulaSepArg(), rOpt.GetFormulaSepArrayCol(), rOpt.GetFormulaSepArrayRow());
467
468
        // Global interpreter settings.
469
28.2k
        ScInterpreter::SetGlobalConfig(rOpt.GetCalcConfig());
470
28.2k
    }
471
472
    // Per document interpreter settings.
473
28.2k
    m_pDocument->SetCalcConfig( rOpt.GetCalcConfig() );
474
28.2k
}
475
476
void ScDocShell::CheckConfigOptions()
477
0
{
478
0
    if (IsConfigOptionsChecked())
479
        // no need to check repeatedly.
480
0
        return;
481
482
0
    OUString aDecSep = ScGlobal::getLocaleData().getNumDecimalSep();
483
0
    OUString aDecSepAlt = ScGlobal::getLocaleData().getNumDecimalSepAlt();
484
485
0
    ScModule* pScMod = ScModule::get();
486
0
    const ScFormulaOptions& rOpt=pScMod->GetFormulaOptions();
487
0
    const OUString& aSepArg = rOpt.GetFormulaSepArg();
488
0
    const OUString& aSepArrRow = rOpt.GetFormulaSepArrayRow();
489
0
    const OUString& aSepArrCol = rOpt.GetFormulaSepArrayCol();
490
491
0
    if (aDecSep == aSepArg || aDecSep == aSepArrRow || aDecSep == aSepArrCol ||
492
0
            aDecSepAlt == aSepArg || aDecSepAlt == aSepArrRow || aDecSepAlt == aSepArrCol)
493
0
    {
494
        // One of arg separators conflicts with the current decimal
495
        // separator.  Reset them to default.
496
0
        ScFormulaOptions aNew = rOpt;
497
0
        aNew.GetCalcConfig().MergeDocumentSpecific(m_pDocument->GetCalcConfig());
498
0
        aNew.ResetFormulaSeparators();
499
0
        SetFormulaOptions(aNew);
500
0
        pScMod->SetFormulaOptions(aNew);
501
502
        // Launch a nice warning dialog to let the users know of this change.
503
0
        ScTabViewShell* pViewShell = comphelper::LibreOfficeKit::isActive() ? nullptr : GetBestViewShell();
504
0
        if (pViewShell)
505
0
        {
506
0
            std::shared_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pViewShell->GetFrameWeld(),
507
0
                                                          VclMessageType::Info, VclButtonsType::Ok,
508
0
                                                          ScResId(STR_OPTIONS_WARN_SEPARATORS)));
509
0
            xInfoBox->runAsync(xInfoBox, [] (int) {});
510
0
        }
511
512
        // For now, this is the only option setting that could launch info
513
        // dialog.  But in the future we may want to implement a nicer
514
        // dialog to display a list of warnings in case we have several
515
        // pieces of information to display.
516
0
    }
517
518
0
    SetConfigOptionsChecked(true);
519
0
}
520
521
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */