Coverage Report

Created: 2026-07-10 11:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/view/tabvwsh5.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 <svl/hint.hxx>
21
#include <comphelper/lok.hxx>
22
#include <svx/numfmtsh.hxx>
23
#include <svx/numinf.hxx>
24
#include <svx/svxids.hrc>
25
#include <sfx2/dispatch.hxx>
26
#include <sfx2/objsh.hxx>
27
#include <sfx2/viewfrm.hxx>
28
#include <osl/diagnose.h>
29
30
#include <tabvwsh.hxx>
31
#include <global.hxx>
32
#include <docsh.hxx>
33
#include <document.hxx>
34
#include <formulacell.hxx>
35
#include <scmod.hxx>
36
#include <uiitems.hxx>
37
#include <hints.hxx>
38
#include <cellvalue.hxx>
39
#include <svl/numformat.hxx>
40
#include <svl/sharedstring.hxx>
41
#include <sfx2/infobar.hxx>
42
#include <officecfg/Office/Calc.hxx>
43
#include <strings.hrc>
44
#include <scresid.hxx>
45
#include <brdcst.hxx>
46
47
void ScTabViewShell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
48
0
{
49
0
    if (rHint.GetId() == SfxHintId::ScPaint) // draw new
50
0
    {
51
0
        const ScPaintHint* pPaintHint = static_cast<const ScPaintHint*>(&rHint);
52
0
        PaintPartFlags nParts = pPaintHint->GetParts();
53
0
        SCTAB nTab = GetViewData().CurrentTabForData();
54
0
        if (pPaintHint->GetStartTab() <= nTab && pPaintHint->GetEndTab() >= nTab)
55
0
        {
56
0
            if (nParts & PaintPartFlags::Extras)          // first if table vanished !!!
57
0
                if (PaintExtras())
58
0
                    nParts = PaintPartFlags::All;
59
60
            // if the current sheet has pending row height updates (sheet links refreshed),
61
            // execute them before invalidating the window
62
0
            GetViewData().GetDocShell()->UpdatePendingRowHeights( GetViewData().CurrentTabForData() );
63
64
0
            if (nParts & PaintPartFlags::Size)
65
0
                RepeatResize();                     //! InvalidateBorder ???
66
0
            const tools::Long nWidthAffectedHint = pPaintHint->GetMaxWidthAffectedHint();
67
0
            if (nParts & PaintPartFlags::Grid)
68
0
                PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
69
0
                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(),
70
0
                           ScUpdateMode::All, nWidthAffectedHint );
71
0
            if (nParts & PaintPartFlags::Marks)
72
0
                PaintArea( pPaintHint->GetStartCol(), pPaintHint->GetStartRow(),
73
0
                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(),
74
0
                           ScUpdateMode::Marks, nWidthAffectedHint );
75
0
            if (nParts & PaintPartFlags::Left)
76
0
                PaintLeftArea( pPaintHint->GetStartRow(), pPaintHint->GetEndRow() );
77
0
            if (nParts & PaintPartFlags::Top)
78
0
                PaintTopArea( pPaintHint->GetStartCol(), pPaintHint->GetEndCol() );
79
80
            // #i84689# call UpdateAllOverlays here instead of in ScTabView::PaintArea
81
0
            if (nParts & ( PaintPartFlags::Left | PaintPartFlags::Top ))    // only if widths or heights changed
82
0
                UpdateAllOverlays();
83
84
0
            HideNoteOverlay();
85
0
        }
86
0
    }
87
0
    else if (rHint.GetId() == SfxHintId::ScEditView) // create Edit-View
88
0
    {
89
        //  ScEditViewHint is only received at active view
90
0
        auto pEditViewHint = static_cast<const ScEditViewHint*>(&rHint);
91
92
0
        SCTAB nTab = GetViewData().CurrentTabForData();
93
0
        if ( pEditViewHint->GetTab() == nTab )
94
0
        {
95
0
            SCCOL nCol = pEditViewHint->GetCol();
96
0
            SCROW nRow = pEditViewHint->GetRow();
97
0
            {
98
0
                HideNoteOverlay();
99
100
0
                MakeEditView( pEditViewHint->GetEngine(), nCol, nRow );
101
102
0
                StopEditShell();                    // shouldn't be set
103
104
0
                ScSplitPos eActive = GetViewData().GetActivePart();
105
0
                if ( GetViewData().HasEditView(eActive) )
106
0
                {
107
                    //  MakeEditView will fail, if the cursor is outside the screen.
108
                    //  Then GetEditView will return a none-active view, therefore
109
                    //  calling HasEditView.
110
111
0
                    EditView* pView = GetViewData().GetEditView(eActive);  // isn't zero
112
113
0
                    SetEditShell(pView, true);
114
0
                }
115
0
            }
116
0
        }
117
0
    }
118
0
    else if (rHint.GetId() == SfxHintId::ScTables) // table insert / deleted
119
0
    {
120
0
        auto pTablesHint = static_cast<const ScTablesHint*>(&rHint);
121
        // first fetch current table (can be changed during DeleteTab on ViewData)
122
0
        SCTAB nActiveTab = GetViewData().CurrentTabForData();
123
124
0
        SCTAB nTab1 = pTablesHint->GetTab1();
125
0
        SCTAB nTab2 = pTablesHint->GetTab2();
126
0
        sal_uInt16 nId  = pTablesHint->GetTablesHintId();
127
0
        switch (nId)
128
0
        {
129
0
            case SC_TAB_INSERTED:
130
0
                GetViewData().InsertTab( nTab1 );
131
0
                break;
132
0
            case SC_TAB_DELETED:
133
0
                GetViewData().DeleteTab( nTab1 );
134
0
                break;
135
0
            case SC_TAB_MOVED:
136
0
                GetViewData().MoveTab( nTab1, nTab2 );
137
0
                break;
138
0
            case SC_TAB_COPIED:
139
0
                GetViewData().CopyTab( nTab1, nTab2 );
140
0
                break;
141
0
            case SC_TAB_HIDDEN:
142
0
                break;
143
0
            case SC_TABS_INSERTED:
144
0
                GetViewData().InsertTabs( nTab1, nTab2 );
145
0
                break;
146
0
            case SC_TABS_DELETED:
147
0
                GetViewData().DeleteTabs( nTab1, nTab2 );
148
0
                break;
149
0
            default:
150
0
                OSL_FAIL("unknown ScTablesHint");
151
0
        }
152
153
        //  No calling of IsActive() here, because the actions can be coming from Basic
154
        //  and then also the active view has to be switched.
155
156
0
        SCTAB nNewTab = nActiveTab;
157
0
        bool bStayOnActiveTab = true;
158
0
        switch (nId)
159
0
        {
160
0
            case SC_TAB_INSERTED:
161
0
                if ( nTab1 <= nNewTab )             // insert before
162
0
                    ++nNewTab;
163
0
                break;
164
0
            case SC_TAB_DELETED:
165
0
                if ( nTab1 < nNewTab )              // deleted before
166
0
                    --nNewTab;
167
0
                else if ( nTab1 == nNewTab )        // deleted current
168
0
                    bStayOnActiveTab = false;
169
0
                break;
170
0
            case SC_TAB_MOVED:
171
0
                if ( nNewTab == nTab1 )             // moved table
172
0
                    nNewTab = nTab2;
173
0
                else if ( nTab1 < nTab2 )           // moved back
174
0
                {
175
0
                    if ( nNewTab > nTab1 && nNewTab <= nTab2 )      // succeeding area
176
0
                        --nNewTab;
177
0
                }
178
0
                else                                // move in front
179
0
                {
180
0
                    if ( nNewTab >= nTab2 && nNewTab < nTab1 )      // succeeding area
181
0
                        ++nNewTab;
182
0
                }
183
0
                break;
184
0
            case SC_TAB_COPIED:
185
0
                if ( nNewTab >= nTab2 )             // insert before
186
0
                    ++nNewTab;
187
0
                break;
188
0
            case SC_TAB_HIDDEN:
189
0
                if ( nTab1 == nNewTab )             // current is hidden
190
0
                    bStayOnActiveTab = false;
191
0
                break;
192
0
            case SC_TABS_INSERTED:
193
0
                if ( nTab1 <= nNewTab )
194
0
                    nNewTab += nTab2;
195
0
                break;
196
0
            case SC_TABS_DELETED:
197
0
                if ( nTab1 < nNewTab )
198
0
                    nNewTab -= nTab2;
199
0
                break;
200
0
        }
201
202
0
        ScDocument& rDoc = GetViewData().GetDocument();
203
0
        if ( nNewTab >= rDoc.GetTableCount() )
204
0
            nNewTab = rDoc.GetTableCount() - 1;
205
206
0
        bool bForce = !bStayOnActiveTab;
207
0
        SetTabNo( nNewTab, bForce, false, bStayOnActiveTab );
208
0
    }
209
0
    else if (const ScIndexHint* pIndexHint = dynamic_cast<const ScIndexHint*>(&rHint))
210
0
    {
211
0
        SfxHintId nId = pIndexHint->GetId();
212
0
        sal_uInt16 nIndex = pIndexHint->GetIndex();
213
0
        switch (nId)
214
0
        {
215
0
            case SfxHintId::ScShowRangeFinder:
216
0
                PaintRangeFinder( nIndex );
217
0
                break;
218
0
            default: break;
219
0
        }
220
0
    }
221
0
    else if (rHint.GetId() == SfxHintId::ScRefErrorCreated)
222
0
    {
223
0
        assert(dynamic_cast<const ScHint*>(&rHint));
224
0
        const ScHint& rScHint = static_cast<const ScHint&>(rHint);
225
0
        ShowRefErrorInfoBar(rScHint.GetStartAddress());
226
0
    }
227
0
    else                       // without parameter
228
0
    {
229
0
        const SfxHintId nSlot = rHint.GetId();
230
0
        switch ( nSlot )
231
0
        {
232
0
            case SfxHintId::ScDataChanged:
233
0
                UpdateFormulas();
234
0
                break;
235
236
0
            case SfxHintId::ScRefModeChanged:
237
0
                {
238
0
                    bool bRefMode = ScModule::get()->IsFormulaMode();
239
0
                    if (!bRefMode)
240
0
                        StopRefMode();
241
0
                    else
242
0
                        GetSelEngine()->Reset();
243
0
                }
244
0
                break;
245
246
0
            case SfxHintId::ScKillEditView:
247
0
            case SfxHintId::ScKillEditViewNoPaint:
248
0
                if (!comphelper::LibreOfficeKit::isActive()
249
0
                    || this == SfxViewShell::Current()
250
0
                    || bInPrepareClose
251
0
                    || bInDispose)
252
0
                {
253
0
                    StopEditShell();
254
0
                    KillEditView( nSlot == SfxHintId::ScKillEditViewNoPaint );
255
0
                }
256
0
                break;
257
258
0
            case SfxHintId::DocChanged:
259
0
                {
260
0
                    ScDocument& rDoc = GetViewData().GetDocument();
261
0
                    if (!rDoc.HasTable( GetViewData().CurrentTabForData() ))
262
0
                    {
263
0
                        SetTabNo(0);
264
0
                    }
265
0
                }
266
0
                break;
267
268
0
            case SfxHintId::ScDrawLayerNew:
269
0
                MakeDrawView(TRISTATE_INDET);
270
0
                break;
271
272
0
            case SfxHintId::ScDocSaved:
273
0
                {
274
                    //  "Save as" can make a write-protected document writable,
275
                    //  therefore the Layer-Locks anew (#39884#)
276
                    //  (Invalidate etc. is happening already from Sfx)
277
                    //  by SID_EDITDOC no SfxHintId::TitleChanged will occur, that
278
                    //  is why the own hint from DoSaveCompleted
279
                    //! what is with SfxHintId::SAVECOMPLETED ?
280
281
0
                    UpdateLayerLocks();
282
283
                    //  Would be too much to change Design-Mode with every save
284
                    //  (when saving under the name, it should remain unchanged)
285
                    //  Therefore only by SfxHintId::ModeChanged (from ViewFrame)
286
0
                }
287
0
                break;
288
289
0
            case SfxHintId::ModeChanged:
290
                //  Since you can no longer rely on it where this hint was coming
291
                //  from, always switch the design mode when the ReadOnly state
292
                //  really was changed:
293
294
0
                if ( GetViewData().GetSfxDocShell()->IsReadOnly() != bReadOnly )
295
0
                {
296
0
                    bReadOnly = GetViewData().GetSfxDocShell()->IsReadOnly();
297
298
0
                    SfxBoolItem aItem( SID_FM_DESIGN_MODE, !bReadOnly);
299
0
                    GetViewData().GetDispatcher().ExecuteList(SID_FM_DESIGN_MODE,
300
0
                            SfxCallMode::ASYNCHRON, { &aItem });
301
302
0
                    UpdateInputContext();
303
0
                }
304
0
                break;
305
306
0
            case SfxHintId::ScShowRangeFinder:
307
0
                PaintRangeFinder(-1);
308
0
                break;
309
310
0
            case SfxHintId::ScForceSetTab:
311
0
                SetTabNo(GetViewData().GetTabNumber(), true);
312
0
                break;
313
314
0
            case SfxHintId::LanguageChanged:
315
0
            {
316
0
                GetViewFrame().GetBindings().Invalidate(SID_LANGUAGE_STATUS);
317
0
                if ( ScGridWindow* pWin = GetViewData().GetActiveWin() )
318
0
                    pWin->ResetAutoSpell();
319
0
            }
320
0
                break;
321
322
0
            case SfxHintId::ScDbAreasChanged:
323
0
                UpdateContextShells();
324
0
                break;
325
326
0
            default:
327
0
                break;
328
0
        }
329
0
    }
330
331
0
    SfxViewShell::Notify( rBC, rHint );
332
0
}
333
334
std::unique_ptr<SvxNumberInfoItem> ScTabViewShell::MakeNumberInfoItem( ScDocument& rDoc, const ScViewData& rViewData )
335
0
{
336
337
    // construct NumberInfo item
338
339
0
    SvxNumberValueType  eValType        = SvxNumberValueType::Undefined;
340
0
    double              nCellValue      = 0;
341
0
    OUString aCellString;
342
343
0
    ScRefCellValue aCell(rDoc, rViewData.GetCurPos());
344
345
0
    switch (aCell.getType())
346
0
    {
347
0
        case CELLTYPE_VALUE:
348
0
        {
349
0
            nCellValue = aCell.getDouble();
350
0
            eValType = SvxNumberValueType::Number;
351
0
        }
352
0
        break;
353
354
0
        case CELLTYPE_STRING:
355
0
        {
356
0
            aCellString = aCell.getSharedString()->getString();
357
0
            eValType = SvxNumberValueType::String;
358
0
        }
359
0
        break;
360
361
0
        case CELLTYPE_FORMULA:
362
0
        {
363
0
            if (aCell.getFormula()->IsValue())
364
0
            {
365
0
                nCellValue = aCell.getFormula()->GetValue();
366
0
                eValType = SvxNumberValueType::Number;
367
0
            }
368
0
            else
369
0
            {
370
0
                nCellValue = 0;
371
0
                eValType   = SvxNumberValueType::Undefined;
372
0
            }
373
0
        }
374
0
        break;
375
376
0
        default:
377
0
            nCellValue = 0;
378
0
            eValType   = SvxNumberValueType::Undefined;
379
0
    }
380
381
0
    switch ( eValType )
382
0
    {
383
0
        case SvxNumberValueType::String:
384
0
            return std::make_unique<SvxNumberInfoItem>(
385
0
                                rDoc.GetFormatTable(),
386
0
                                aCellString,
387
0
                                SID_ATTR_NUMBERFORMAT_INFO );
388
389
0
        case SvxNumberValueType::Number:
390
0
            return std::make_unique<SvxNumberInfoItem>(
391
0
                                rDoc.GetFormatTable(),
392
0
                                nCellValue,
393
0
                                SID_ATTR_NUMBERFORMAT_INFO );
394
395
0
        case SvxNumberValueType::Undefined:
396
0
        default:
397
0
            ;
398
0
    }
399
400
0
    return std::make_unique<SvxNumberInfoItem>(
401
0
        rDoc.GetFormatTable(), SID_ATTR_NUMBERFORMAT_INFO);
402
0
}
403
404
void ScTabViewShell::UpdateNumberFormatter(
405
                        const SvxNumberInfoItem& rInfoItem )
406
0
{
407
0
    for ( sal_uInt32 key : rInfoItem.GetDelFormats() )
408
0
        rInfoItem.GetNumberFormatter()->DeleteEntry( key );
409
0
}
410
411
void ScTabViewShell::ShowRefErrorInfoBar(const ScAddress& rFormulaCell)
412
0
{
413
    // Note: in Online, desktop-style infobars are not forwarded to
414
    // the browser client. A future COOL implementation could hook into
415
    // SfxHintId::ScRefErrorCreated at the kit protocol layer and surface this
416
    // as a suggestion popup instead. The ScHint already carries the formula
417
    // cell address, so all the data needed is available in the hint.
418
0
    if (!officecfg::Office::Calc::Input::WarnOnDeleteCellReferences::get())
419
0
        return;
420
421
    // Remember the target so the "Go to First Error" button handler can
422
    // navigate to it. Overwritten each time the infobar is (re-)shown, so
423
    // the button always points at the most recent delete operation's first
424
    // error.
425
0
    m_aRefErrorCell = rFormulaCell;
426
427
0
    auto pInfoBar = GetViewFrame().AppendInfoBar(
428
0
        u"ref_logic_error"_ustr,
429
0
        ScResId(STR_REF_ERROR_TITLE),
430
0
        ScResId(STR_REF_ERROR_MSG),
431
0
        InfobarType::WARNING,
432
0
        true);
433
434
    // AppendInfoBar returns nullptr if an infobar with this ID already
435
    // exists. In that case the existing infobar still has its button, so we
436
    // just keep it — m_aRefErrorCell update above is enough.
437
0
    if (pInfoBar)
438
0
    {
439
0
        weld::Button& rBtn = pInfoBar->addButton();
440
0
        rBtn.set_label(ScResId(STR_REF_ERROR_GOTO));
441
0
        rBtn.connect_clicked(LINK(this, ScTabViewShell, GoToRefErrorHdl));
442
0
    }
443
0
}
444
445
IMPL_LINK_NOARG(ScTabViewShell, GoToRefErrorHdl, weld::Button&, void)
446
0
{
447
0
    if (!m_aRefErrorCell.IsValid())
448
0
        return;
449
450
0
    SetTabNo(m_aRefErrorCell.Tab());
451
0
    AlignToCursor(m_aRefErrorCell.Col(), m_aRefErrorCell.Row(), SC_FOLLOW_JUMP);
452
0
    SetCursor(m_aRefErrorCell.Col(), m_aRefErrorCell.Row());
453
0
}
454
455
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */