Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/view/lokstarmathhelper.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
10
#include <sal/config.h>
11
12
#include <sfx2/ipclient.hxx>
13
#include <sfx2/lokcomponenthelpers.hxx>
14
#include <sfx2/lokhelper.hxx>
15
#include <sfx2/objsh.hxx>
16
17
#include <comphelper/dispatchcommand.hxx>
18
#include <comphelper/lok.hxx>
19
#include <toolkit/helper/vclunohelper.hxx>
20
#include <tools/fract.hxx>
21
#include <tools/mapunit.hxx>
22
#include <tools/UnitConversion.hxx>
23
#include <vcl/layout.hxx>
24
#include <vcl/virdev.hxx>
25
#include <vcl/window.hxx>
26
27
#include <com/sun/star/embed/XEmbeddedObject.hpp>
28
#include <com/sun/star/frame/XModel.hpp>
29
#include <com/sun/star/lang/XServiceInfo.hpp>
30
31
LokStarMathHelper::LokStarMathHelper(const SfxViewShell* pViewShell)
32
0
    : mpViewShell(pViewShell)
33
0
{
34
0
    if (mpViewShell)
35
0
    {
36
0
        if (const SfxInPlaceClient* pIPClient = mpViewShell->GetIPClient())
37
0
        {
38
0
            if (const auto& xEmbObj = pIPClient->GetObject())
39
0
            {
40
0
                css::uno::Reference<css::lang::XServiceInfo> xComp(xEmbObj->getComponent(),
41
0
                                                                   css::uno::UNO_QUERY);
42
0
                if (xComp && xComp->supportsService(u"com.sun.star.formula.FormulaProperties"_ustr))
43
0
                {
44
0
                    if (css::uno::Reference<css::frame::XModel> xModel{ xComp,
45
0
                                                                        css::uno::UNO_QUERY })
46
0
                    {
47
0
                        if (auto xController = xModel->getCurrentController())
48
0
                        {
49
0
                            mpIPClient = pIPClient;
50
0
                            mxFrame = xController->getFrame();
51
0
                        }
52
0
                    }
53
0
                }
54
0
            }
55
0
        }
56
0
    }
57
0
}
58
59
void LokStarMathHelper::Dispatch(
60
    const OUString& cmd, const css::uno::Sequence<css::beans::PropertyValue>& rArguments) const
61
0
{
62
0
    if (mxFrame)
63
0
        comphelper::dispatchCommand(cmd, mxFrame, rArguments);
64
0
}
65
66
namespace
67
{
68
// Find a child SmGraphicWindow*
69
vcl::Window* FindSmGraphicWindow(vcl::Window* pWin)
70
0
{
71
0
    if (!pWin)
72
0
        return nullptr;
73
74
0
    if (pWin->IsStarMath())
75
0
        return pWin;
76
77
0
    pWin = pWin->GetWindow(GetWindowType::FirstChild);
78
0
    while (pWin)
79
0
    {
80
0
        if (vcl::Window* pSmGraphicWindow = FindSmGraphicWindow(pWin))
81
0
            return pSmGraphicWindow;
82
0
        pWin = pWin->GetWindow(GetWindowType::Next);
83
0
    }
84
0
    return nullptr;
85
0
}
86
87
// Find a child window that corresponds to SmGraphicWidget
88
vcl::Window* FindChildSmGraphicWidgetWindow(vcl::Window* pWin)
89
0
{
90
0
    if (!pWin)
91
0
        return nullptr;
92
93
    // The needed window is a VclDrawingArea
94
0
    if (dynamic_cast<VclDrawingArea*>(pWin))
95
0
        return pWin;
96
97
0
    pWin = pWin->GetWindow(GetWindowType::FirstChild);
98
0
    while (pWin)
99
0
    {
100
0
        if (vcl::Window* pSmGraphicWidgetWindow = FindChildSmGraphicWidgetWindow(pWin))
101
0
            return pSmGraphicWidgetWindow;
102
0
        pWin = pWin->GetWindow(GetWindowType::Next);
103
0
    }
104
0
    return nullptr;
105
0
}
106
}
107
108
vcl::Window* LokStarMathHelper::GetGraphicWindow()
109
0
{
110
0
    if (!mpGraphicWindow)
111
0
    {
112
0
        if (mxFrame)
113
0
        {
114
0
            css::uno::Reference<css::awt::XWindow> xDockerWin = mxFrame->getContainerWindow();
115
0
            mpGraphicWindow.reset(FindSmGraphicWindow(VCLUnoHelper::GetWindow(xDockerWin)));
116
0
        }
117
0
    }
118
119
0
    return mpGraphicWindow.get();
120
0
}
121
122
vcl::Window* LokStarMathHelper::GetWidgetWindow()
123
0
{
124
0
    if (!mpWidgetWindow)
125
0
        mpWidgetWindow.reset(FindChildSmGraphicWidgetWindow(GetGraphicWindow()));
126
127
0
    return mpWidgetWindow.get();
128
0
}
129
130
const SfxViewShell* LokStarMathHelper::GetSmViewShell()
131
0
{
132
0
    if (vcl::Window* pGraphWindow = GetGraphicWindow())
133
0
    {
134
0
        return SfxViewShell::GetFirst(false, [pGraphWindow](const SfxViewShell& shell) {
135
0
            return shell.GetWindow() && shell.GetWindow()->IsChild(pGraphWindow);
136
0
        });
137
0
    }
138
0
    return nullptr;
139
0
}
140
141
tools::Rectangle LokStarMathHelper::GetBoundingBox() const
142
0
{
143
0
    if (mpIPClient)
144
0
    {
145
0
        tools::Rectangle r(mpIPClient->GetObjArea());
146
0
        if (SfxObjectShell* pObjShell = const_cast<SfxViewShell*>(mpViewShell)->GetObjectShell())
147
0
        {
148
0
            const o3tl::Length unit = MapToO3tlLength(pObjShell->GetMapUnit());
149
0
            if (unit != o3tl::Length::twip && unit != o3tl::Length::invalid)
150
0
                r = o3tl::convert(r, unit, o3tl::Length::twip);
151
0
        }
152
0
        return r;
153
0
    }
154
0
    return {};
155
0
}
156
157
bool LokStarMathHelper::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons,
158
                                       int nModifier, double fPPTScaleX, double fPPTScaleY)
159
0
{
160
0
    const tools::Rectangle rBBox = GetBoundingBox();
161
0
    if (Point aMousePos(nX, nY); rBBox.Contains(aMousePos))
162
0
    {
163
0
        if (vcl::Window* pWindow = GetWidgetWindow())
164
0
        {
165
0
            aMousePos -= rBBox.TopLeft();
166
167
            // In lok, Math does not convert coordinates (see SmGraphicWidget::SetDrawingArea,
168
            // which disables MapMode), and uses twips internally (see SmDocShell ctor and
169
            // SmMapUnit), but the conversion factor can depend on the client zoom.
170
            // 1. Remove the twip->pixel factor in the passed scales
171
0
            double fScaleX = o3tl::convert(fPPTScaleX, o3tl::Length::px, o3tl::Length::twip);
172
0
            double fScaleY = o3tl::convert(fPPTScaleY, o3tl::Length::px, o3tl::Length::twip);
173
            // 2. Adjust the position according to the scales
174
0
            aMousePos
175
0
                = Point(std::round(aMousePos.X() * fScaleX), std::round(aMousePos.Y() * fScaleY));
176
            // 3. Take window own scaling into account (reverses the conversion done in
177
            // SmGraphicWidget::MouseButtonDown, albeit incompletely - it does not handle
178
            // GetFormulaDrawPos; hopefully, in lok/in-place case, it's always [ 0, 0 ]?)
179
0
            aMousePos = pWindow->LogicToPixel(aMousePos);
180
181
0
            LokMouseEventData aMouseEventData(
182
0
                nType, aMousePos, nCount, MouseEventModifiers::SIMPLECLICK, nButtons, nModifier);
183
0
            SfxLokHelper::postMouseEventAsync(pWindow, aMouseEventData);
184
185
0
            return true;
186
0
        }
187
0
    }
188
0
    return false;
189
0
}
190
191
void LokStarMathHelper::PaintTile(VirtualDevice& rDevice, const tools::Rectangle& rTileRect)
192
0
{
193
0
    const tools::Rectangle aMathRect = GetBoundingBox();
194
0
    if (rTileRect.GetIntersection(aMathRect).IsEmpty())
195
0
        return;
196
197
0
    vcl::Window* pWidgetWindow = GetWidgetWindow();
198
0
    if (!pWidgetWindow)
199
0
        return;
200
201
0
    Point aOffset(aMathRect.Left() - rTileRect.Left(), aMathRect.Top() - rTileRect.Top());
202
203
0
    MapMode newMode = rDevice.GetMapMode();
204
0
    newMode.SetOrigin(aOffset);
205
0
    rDevice.SetMapMode(newMode); // Push/Pop is done in PaintAllInPlaceOnTile
206
207
0
    pWidgetWindow->Paint(rDevice, {}); // SmGraphicWidget::Paint does not use the passed rectangle
208
0
}
209
210
void LokStarMathHelper::PaintAllInPlaceOnTile(VirtualDevice& rDevice, int nOutputWidth,
211
                                              int nOutputHeight, int nTilePosX, int nTilePosY,
212
                                              tools::Long nTileWidth, tools::Long nTileHeight)
213
0
{
214
0
    if (comphelper::LibreOfficeKit::isTiledAnnotations())
215
0
        return;
216
217
0
    SfxViewShell* pCurView = SfxViewShell::Current();
218
0
    if (!pCurView)
219
0
        return;
220
0
    const ViewShellDocId nDocId = pCurView->GetDocId();
221
0
    const int nPartForCurView = pCurView->getPart();
222
223
    // Resizes the virtual device to contain the entries context
224
0
    rDevice.SetOutputSizePixel({ nOutputWidth, nOutputHeight }, /*bErase*/ false);
225
226
0
    auto popIt = rDevice.ScopedPush(vcl::PushFlags::MAPMODE);
227
0
    MapMode aMapMode(rDevice.GetMapMode());
228
229
    // Scaling. Must convert from pixels to twips. We know that VirtualDevices use a DPI of 96.
230
0
    const double scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
231
0
    const double scaleX = double(nOutputWidth) / nTileWidth * scale;
232
0
    const double scaleY = double(nOutputHeight) / nTileHeight * scale;
233
0
    aMapMode.SetScaleX(scaleX);
234
0
    aMapMode.SetScaleY(scaleY);
235
0
    aMapMode.SetMapUnit(MapUnit::MapTwip);
236
0
    rDevice.SetMapMode(aMapMode);
237
238
0
    const tools::Rectangle aTileRect(Point(nTilePosX, nTilePosY), Size(nTileWidth, nTileHeight));
239
240
0
    for (SfxViewShell* pViewShell = SfxViewShell::GetFirst(); pViewShell;
241
0
         pViewShell = SfxViewShell::GetNext(*pViewShell))
242
0
        if (pViewShell->GetDocId() == nDocId && pViewShell->getPart() == nPartForCurView)
243
0
            LokStarMathHelper(pViewShell).PaintTile(rDevice, aTileRect);
244
0
}
245
246
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */