Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/docshell/servobj.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 <osl/thread.h>
21
#include <osl/diagnose.h>
22
#include <sot/exchange.hxx>
23
#include <sot/formats.hxx>
24
#include <sfx2/app.hxx>
25
#include <sfx2/linkmgr.hxx>
26
#include <servobj.hxx>
27
#include <docsh.hxx>
28
#include <impex.hxx>
29
#include <brdcst.hxx>
30
#include <rangenam.hxx>
31
#include <unotools/charclass.hxx>
32
33
using namespace formula;
34
35
static bool lcl_FillRangeFromName( ScRange& rRange, ScDocShell* pDocSh, const OUString& rName )
36
0
{
37
0
    if (pDocSh)
38
0
    {
39
0
        ScDocument& rDoc = pDocSh->GetDocument();
40
0
        ScRangeName* pNames = rDoc.GetRangeName();
41
0
        if (pNames)
42
0
        {
43
0
            const ScRangeData* pData = pNames->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
44
0
            if (pData)
45
0
            {
46
0
                if ( pData->IsValidReference( rRange ) )
47
0
                    return true;
48
0
            }
49
0
        }
50
0
    }
51
0
    return false;
52
0
}
53
54
ScServerObjectSvtListenerForwarder::ScServerObjectSvtListenerForwarder(
55
        ScServerObject* pObjP)
56
0
    : pObj(pObjP)
57
0
{
58
0
}
59
60
ScServerObjectSvtListenerForwarder::~ScServerObjectSvtListenerForwarder()
61
0
{
62
    //! do NOT access pObj
63
0
}
64
65
void ScServerObjectSvtListenerForwarder::Notify( const SfxHint& rHint )
66
0
{
67
0
    pObj->Notify( aBroadcaster, rHint);
68
0
}
69
70
ScServerObject::ScServerObject( ScDocShell* pShell, const OUString& rItem ) :
71
0
    aForwarder( this ),
72
0
    pDocSh( pShell ),
73
0
    bRefreshListener( false )
74
0
{
75
    //  parse item string
76
77
0
    if ( lcl_FillRangeFromName( aRange, pDocSh, rItem ) )
78
0
    {
79
0
        aItemStr = rItem;               // must be parsed again on ref update
80
0
    }
81
0
    else
82
0
    {
83
        //  parse ref
84
0
        ScDocument& rDoc = pDocSh->GetDocument();
85
0
        SCTAB nTab = ScDocShell::GetCurTab();
86
0
        aRange.aStart.SetTab( nTab );
87
88
        // For DDE link, we always must parse references using OOO A1 convention.
89
90
0
        if ( aRange.Parse( rItem, rDoc, FormulaGrammar::CONV_OOO ) & ScRefFlags::VALID )
91
0
        {
92
            // area reference
93
0
        }
94
0
        else if ( aRange.aStart.Parse( rItem, rDoc, FormulaGrammar::CONV_OOO ) & ScRefFlags::VALID )
95
0
        {
96
            // cell reference
97
0
            aRange.aEnd = aRange.aStart;
98
0
        }
99
0
        else
100
0
        {
101
0
            OSL_FAIL("ScServerObject: invalid item");
102
0
        }
103
0
    }
104
105
0
    pDocSh->GetDocument().GetLinkManager()->InsertServer( this );
106
0
    pDocSh->GetDocument().StartListeningArea( aRange, false, &aForwarder );
107
108
0
    StartListening(*pDocSh);           // to notice if DocShell gets deleted
109
0
    StartListening(*SfxGetpApp());     // for SfxHintId::ScAreasChanged
110
0
}
111
112
ScServerObject::~ScServerObject()
113
0
{
114
0
    Clear();
115
0
}
116
117
void ScServerObject::Clear()
118
0
{
119
0
    if (pDocSh)
120
0
    {
121
0
        ScDocShell* pTemp = pDocSh;
122
0
        pDocSh = nullptr;
123
124
0
        pTemp->GetDocument().EndListeningArea(aRange, false, &aForwarder);
125
0
        pTemp->GetDocument().GetLinkManager()->RemoveServer( this );
126
0
        EndListening(*pTemp);
127
0
        EndListening(*SfxGetpApp());
128
0
    }
129
0
}
130
131
void ScServerObject::EndListeningAll()
132
0
{
133
0
    aForwarder.EndListeningAll();
134
0
    SfxListener::EndListeningAll();
135
0
}
136
137
bool ScServerObject::GetData(
138
        css::uno::Any & rData /*out param*/,
139
        const OUString & rMimeType, bool /* bSynchron */ )
140
0
{
141
0
    if (!pDocSh)
142
0
        return false;
143
144
    // named ranges may have changed -> update aRange
145
0
    if ( !aItemStr.isEmpty() )
146
0
    {
147
0
        ScRange aNew;
148
0
        if ( lcl_FillRangeFromName( aNew, pDocSh, aItemStr ) && aNew != aRange )
149
0
        {
150
0
            aRange = aNew;
151
0
            bRefreshListener = true;
152
0
        }
153
0
    }
154
155
0
    if ( bRefreshListener )
156
0
    {
157
        //  refresh the listeners now (this is called from a timer)
158
159
0
        EndListeningAll();
160
0
        pDocSh->GetDocument().StartListeningArea( aRange, false, &aForwarder );
161
0
        StartListening(*pDocSh);
162
0
        StartListening(*SfxGetpApp());
163
0
        bRefreshListener = false;
164
0
    }
165
166
0
    OUString aDdeTextFmt = pDocSh->GetDdeTextFmt();
167
0
    ScDocument& rDoc = pDocSh->GetDocument();
168
169
0
    SotClipboardFormatId eFormatId = SotExchange::GetFormatIdFromMimeType( rMimeType );
170
0
    if (SotClipboardFormatId::STRING == eFormatId || SotClipboardFormatId::STRING_TSVC == eFormatId)
171
0
    {
172
0
        ScImportExport aObj( rDoc, aRange );
173
0
        if( aDdeTextFmt[0] == 'F' )
174
0
            aObj.SetFormulas( true );
175
0
        if( aDdeTextFmt == "SYLK" || aDdeTextFmt == "FSYLK" )
176
0
        {
177
0
            OString aByteData;
178
0
            if( aObj.ExportByteString( aByteData, osl_getThreadTextEncoding(), SotClipboardFormatId::SYLK ) )
179
0
            {
180
0
                rData <<= css::uno::Sequence< sal_Int8 >(
181
0
                                        reinterpret_cast<const sal_Int8*>(aByteData.getStr()),
182
0
                                        aByteData.getLength() + 1 );
183
0
                return true;
184
0
            }
185
0
            return false;
186
0
        }
187
0
        if( aDdeTextFmt == "CSV" || aDdeTextFmt == "FCSV" )
188
0
            aObj.SetSeparator( ',' );
189
        /* TODO: STRING_TSVC could preserve line breaks with added quotes. */
190
0
        aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
191
0
        return aObj.ExportData( rMimeType, rData );
192
0
    }
193
194
0
    ScImportExport aObj( rDoc, aRange );
195
0
    aObj.SetExportTextOptions( ScExportTextOptions( ScExportTextOptions::ToSpace, ' ', false ) );
196
0
    if( aObj.IsRef() )
197
0
        return aObj.ExportData( rMimeType, rData );
198
0
    return false;
199
0
}
200
201
void ScServerObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
202
0
{
203
0
    bool bDataChanged = false;
204
205
    //  DocShell can't be tested via type info, because SfxHintId::Dying comes from the dtor
206
0
    if ( &rBC == pDocSh )
207
0
    {
208
        //  from DocShell, only SfxHintId::Dying is interesting
209
0
        if ( rHint.GetId() == SfxHintId::Dying )
210
0
        {
211
0
            pDocSh = nullptr;
212
0
            EndListening(*SfxGetpApp());
213
            //  don't access DocShell anymore for EndListening etc.
214
0
        }
215
0
    }
216
0
    else if (dynamic_cast<const SfxApplication*>( &rBC) !=  nullptr)
217
0
    {
218
0
        if ( !aItemStr.isEmpty() && rHint.GetId() == SfxHintId::ScAreasChanged )
219
0
        {
220
            //  check if named range was modified
221
0
            ScRange aNew;
222
0
            if ( lcl_FillRangeFromName( aNew, pDocSh, aItemStr ) && aNew != aRange )
223
0
                bDataChanged = true;
224
0
        }
225
0
    }
226
0
    else
227
0
    {
228
        //  must be from Area broadcasters
229
230
0
        if (rHint.GetId() == SfxHintId::ScDataChanged)
231
0
            bDataChanged = true;
232
0
        else if (rHint.GetId() == SfxHintId::ScAreaChanged)  // position of broadcaster changed
233
0
        {
234
0
            const ScAreaChangedHint *pChgHint = static_cast<const ScAreaChangedHint*>(&rHint);
235
0
            const ScRange& aNewRange = pChgHint->GetRange();
236
0
            if ( aRange != aNewRange )
237
0
            {
238
0
                bRefreshListener = true;
239
0
                bDataChanged = true;
240
0
            }
241
0
        }
242
0
        else if (rHint.GetId() == SfxHintId::Dying)
243
0
        {
244
            //  If the range is being deleted, listening must be restarted
245
            //  after the deletion is complete (done in GetData)
246
0
            bRefreshListener = true;
247
0
            bDataChanged = true;
248
0
        }
249
0
    }
250
251
0
    if ( bDataChanged && HasDataLinks() )
252
0
        SvLinkSource::NotifyDataChanged();
253
0
}
254
255
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */