Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/desktop/source/lib/lokclipboard.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
10
#include "lokclipboard.hxx"
11
#include <unordered_map>
12
#include <tools/lazydelete.hxx>
13
#include <vcl/svapp.hxx>
14
#include <sfx2/lokhelper.hxx>
15
#include <sal/log.hxx>
16
#include <cppuhelper/supportsservice.hxx>
17
#include <com/sun/star/uno/XComponentContext.hpp>
18
19
using namespace css;
20
using namespace css::uno;
21
22
/* static */ osl::Mutex LOKClipboardFactory::gMutex;
23
static tools::DeleteOnDeinit<std::unordered_map<int, rtl::Reference<LOKClipboard>>>& getClipboards()
24
0
{
25
0
    static tools::DeleteOnDeinit<std::unordered_map<int, rtl::Reference<LOKClipboard>>>
26
0
        gClipboards{};
27
0
    return gClipboards;
28
0
}
29
30
rtl::Reference<LOKClipboard> LOKClipboardFactory::getClipboardForCurView()
31
0
{
32
0
    int nViewId = SfxLokHelper::getCurrentView(); // currently active.
33
34
0
    osl::MutexGuard aGuard(gMutex);
35
36
0
    auto& gClipboards = getClipboards();
37
0
    auto it = gClipboards.get()->find(nViewId);
38
0
    if (it != gClipboards.get()->end())
39
0
    {
40
0
        SAL_INFO("lok", "Got clip: " << it->second.get() << " from " << nViewId);
41
0
        return it->second;
42
0
    }
43
0
    rtl::Reference<LOKClipboard> xClip(new LOKClipboard());
44
0
    (*gClipboards.get())[nViewId] = xClip;
45
0
    SAL_INFO("lok", "Created clip: " << xClip.get() << " for viewId " << nViewId);
46
0
    return xClip;
47
0
}
48
49
void LOKClipboardFactory::releaseClipboardForView(int nViewId)
50
0
{
51
0
    osl::MutexGuard aGuard(gMutex);
52
53
0
    auto& gClipboards = getClipboards();
54
0
    if (nViewId < 0) // clear all
55
0
    {
56
0
        gClipboards.get()->clear();
57
0
        SAL_INFO("lok", "Released all clipboards on doc destroy\n");
58
0
    }
59
0
    else if (gClipboards.get())
60
0
    {
61
0
        auto it = gClipboards.get()->find(nViewId);
62
0
        if (it != gClipboards.get()->end())
63
0
        {
64
0
            SAL_INFO("lok", "Releasing clip: " << it->second.get() << " for destroyed " << nViewId);
65
0
            gClipboards.get()->erase(it);
66
0
        }
67
0
    }
68
0
}
69
70
uno::Reference<uno::XInterface>
71
    SAL_CALL LOKClipboardFactory::createInstanceWithArguments(const Sequence<Any>& /* rArgs */)
72
0
{
73
0
    return { static_cast<cppu::OWeakObject*>(getClipboardForCurView().get()) };
74
0
}
75
76
LOKClipboard::LOKClipboard()
77
0
    : cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard,
78
0
                                    css::lang::XServiceInfo>(m_aMutex)
79
0
{
80
    // Encourage 'paste' menu items to always show up.
81
0
    uno::Reference<datatransfer::XTransferable> xTransferable(new LOKTransferable());
82
0
    setContents(xTransferable, uno::Reference<datatransfer::clipboard::XClipboardOwner>());
83
0
}
84
85
Sequence<OUString> LOKClipboard::getSupportedServiceNames_static()
86
0
{
87
0
    Sequence<OUString> aRet{ u"com.sun.star.datatransfer.clipboard.LokClipboard"_ustr };
88
0
    return aRet;
89
0
}
90
91
OUString LOKClipboard::getImplementationName()
92
0
{
93
0
    return u"com.sun.star.datatransfer.LOKClipboard"_ustr;
94
0
}
95
96
Sequence<OUString> LOKClipboard::getSupportedServiceNames()
97
0
{
98
0
    return getSupportedServiceNames_static();
99
0
}
100
101
sal_Bool LOKClipboard::supportsService(const OUString& ServiceName)
102
0
{
103
0
    return cppu::supportsService(this, ServiceName);
104
0
}
105
106
0
Reference<css::datatransfer::XTransferable> LOKClipboard::getContents() { return m_xTransferable; }
107
108
void LOKClipboard::setContents(
109
    const Reference<css::datatransfer::XTransferable>& xTrans,
110
    const Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
111
0
{
112
0
    osl::ClearableMutexGuard aGuard(m_aMutex);
113
0
    Reference<datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner);
114
0
    Reference<datatransfer::XTransferable> xOldContents(m_xTransferable);
115
0
    m_xTransferable = xTrans;
116
0
    m_aOwner = xClipboardOwner;
117
118
0
    std::vector<Reference<datatransfer::clipboard::XClipboardListener>> aListeners(m_aListeners);
119
0
    datatransfer::clipboard::ClipboardEvent aEv;
120
0
    aEv.Contents = m_xTransferable;
121
0
    SAL_INFO("lok", "Clip: " << this << " set contents to " << m_xTransferable);
122
123
0
    aGuard.clear();
124
125
0
    if (xOldOwner.is() && xOldOwner != xClipboardOwner)
126
0
        xOldOwner->lostOwnership(this, xOldContents);
127
0
    for (auto const& listener : aListeners)
128
0
    {
129
0
        listener->changedContents(aEv);
130
0
    }
131
0
}
132
133
void LOKClipboard::addClipboardListener(
134
    const Reference<datatransfer::clipboard::XClipboardListener>& listener)
135
0
{
136
0
    osl::ClearableMutexGuard aGuard(m_aMutex);
137
0
    m_aListeners.push_back(listener);
138
0
}
139
140
void LOKClipboard::removeClipboardListener(
141
    const Reference<datatransfer::clipboard::XClipboardListener>& listener)
142
0
{
143
0
    osl::ClearableMutexGuard aGuard(m_aMutex);
144
0
    std::erase(m_aListeners, listener);
145
0
}
146
LOKTransferable::LOKTransferable(const OUString& sMimeType,
147
                                 const css::uno::Sequence<sal_Int8>& aSequence)
148
0
{
149
0
    m_aContent.reserve(1);
150
0
    m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(1);
151
0
    initFlavourFromMime(m_aFlavors.getArray()[0], sMimeType);
152
153
0
    uno::Any aContent;
154
0
    if (m_aFlavors[0].DataType == cppu::UnoType<OUString>::get())
155
0
    {
156
0
        auto pText = reinterpret_cast<const char*>(aSequence.getConstArray());
157
0
        aContent <<= OUString(pText, aSequence.getLength(), RTL_TEXTENCODING_UTF8);
158
0
    }
159
0
    else
160
0
        aContent <<= aSequence;
161
0
    m_aContent.push_back(aContent);
162
0
}
163
164
/// Use to ensure we have some dummy content on the clipboard to allow a 1st 'paste'
165
LOKTransferable::LOKTransferable()
166
0
{
167
0
    m_aContent.reserve(1);
168
0
    m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(1);
169
0
    initFlavourFromMime(m_aFlavors.getArray()[0], u"text/plain"_ustr);
170
0
    uno::Any aContent;
171
0
    aContent <<= OUString();
172
0
    m_aContent.push_back(aContent);
173
0
}
174
175
// cf. sot/source/base/exchange.cxx for these two exceptional types.
176
void LOKTransferable::initFlavourFromMime(css::datatransfer::DataFlavor& rFlavor,
177
                                          OUString aMimeType)
178
0
{
179
0
    if (aMimeType.startsWith("text/plain"))
180
0
    {
181
0
        aMimeType = "text/plain;charset=utf-16";
182
0
        rFlavor.DataType = cppu::UnoType<OUString>::get();
183
0
    }
184
0
    else if (aMimeType == "application/x-libreoffice-tsvc")
185
0
        rFlavor.DataType = cppu::UnoType<OUString>::get();
186
0
    else
187
0
        rFlavor.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get();
188
0
    rFlavor.MimeType = aMimeType;
189
0
    rFlavor.HumanPresentableName = aMimeType;
190
0
}
191
192
LOKTransferable::LOKTransferable(const size_t nInCount, const char** pInMimeTypes,
193
                                 const size_t* pInSizes, const char** pInStreams)
194
0
{
195
0
    m_aContent.reserve(nInCount);
196
0
    m_aFlavors = css::uno::Sequence<css::datatransfer::DataFlavor>(nInCount);
197
0
    auto p_aFlavors = m_aFlavors.getArray();
198
0
    for (size_t i = 0; i < nInCount; ++i)
199
0
    {
200
0
        initFlavourFromMime(p_aFlavors[i], OUString::fromUtf8(pInMimeTypes[i]));
201
202
0
        uno::Any aContent;
203
0
        if (m_aFlavors[i].DataType == cppu::UnoType<OUString>::get())
204
0
            aContent <<= OUString(pInStreams[i], pInSizes[i], RTL_TEXTENCODING_UTF8);
205
0
        else
206
0
            aContent <<= css::uno::Sequence<sal_Int8>(
207
0
                reinterpret_cast<const sal_Int8*>(pInStreams[i]), pInSizes[i]);
208
0
        m_aContent.push_back(aContent);
209
0
    }
210
0
}
211
212
uno::Any SAL_CALL LOKTransferable::getTransferData(const datatransfer::DataFlavor& rFlavor)
213
0
{
214
0
    assert(m_aContent.size() == static_cast<size_t>(m_aFlavors.getLength()));
215
0
    for (size_t i = 0; i < m_aContent.size(); ++i)
216
0
    {
217
0
        if (m_aFlavors[i].MimeType == rFlavor.MimeType)
218
0
        {
219
0
            if (m_aFlavors[i].DataType != rFlavor.DataType)
220
0
                SAL_WARN("lok", "Horror type mismatch!");
221
0
            return m_aContent[i];
222
0
        }
223
0
    }
224
0
    return {};
225
0
}
226
227
uno::Sequence<datatransfer::DataFlavor> SAL_CALL LOKTransferable::getTransferDataFlavors()
228
0
{
229
0
    return m_aFlavors;
230
0
}
231
232
sal_Bool SAL_CALL LOKTransferable::isDataFlavorSupported(const datatransfer::DataFlavor& rFlavor)
233
0
{
234
0
    return std::find_if(std::cbegin(m_aFlavors), std::cend(m_aFlavors),
235
0
                        [&rFlavor](const datatransfer::DataFlavor& i) {
236
0
                            return i.MimeType == rFlavor.MimeType && i.DataType == rFlavor.DataType;
237
0
                        })
238
0
           != std::cend(m_aFlavors);
239
0
}
240
241
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
242
desktop_LOKClipboard_get_implementation(css::uno::XComponentContext*,
243
                                        css::uno::Sequence<css::uno::Any> const& /*args*/)
244
0
{
245
0
    SolarMutexGuard aGuard;
246
247
0
    cppu::OWeakObject* pClipboard = LOKClipboardFactory::getClipboardForCurView().get();
248
249
0
    pClipboard->acquire();
250
0
    return pClipboard;
251
0
}
252
253
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */