Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sd/source/ui/func/fuconbez.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 <com/sun/star/presentation/EffectNodeType.hpp>
21
22
#include <fuconbez.hxx>
23
#include <svx/svdopath.hxx>
24
#include <svl/intitem.hxx>
25
#include <sfx2/dispatch.hxx>
26
#include <svx/svdobj.hxx>
27
#include <sfx2/bindings.hxx>
28
#include <sfx2/request.hxx>
29
#include <sfx2/viewfrm.hxx>
30
#include <osl/diagnose.h>
31
32
#include <svx/svxids.hrc>
33
#include <svx/svdpagv.hxx>
34
#include <svx/xlnclit.hxx>
35
#include <svx/xlntrit.hxx>
36
#include <svx/xlnwtit.hxx>
37
38
#include <app.hrc>
39
#include <ViewShell.hxx>
40
#include <ViewShellBase.hxx>
41
#include <View.hxx>
42
#include <Window.hxx>
43
#include <ToolBarManager.hxx>
44
#include <drawdoc.hxx>
45
#include <sdpage.hxx>
46
47
#include <basegfx/polygon/b2dpolygon.hxx>
48
#include <basegfx/polygon/b2dpolygontools.hxx>
49
50
#include <CustomAnimationEffect.hxx>
51
52
using namespace ::com::sun::star::uno;
53
54
namespace sd {
55
56
/*//Extra attributes coming from parameters
57
    sal_uInt16  mnTransparence;  // Default: 0
58
    OUString    msColor;         // Default: ""
59
    sal_uInt16  mnWidth;         // Default: 0
60
    OUString    msShapeName;     // Default: ""*/
61
FuConstructBezierPolygon::FuConstructBezierPolygon (
62
    ViewShell& rViewSh,
63
    ::sd::Window* pWin,
64
    ::sd::View* pView,
65
    SdDrawDocument& rDoc,
66
    SfxRequest& rReq)
67
0
    : FuConstruct(rViewSh, pWin, pView, rDoc, rReq),
68
0
      nEditMode(SID_BEZIER_MOVE),
69
0
      mnTransparence(0),
70
0
      mnWidth(0)
71
0
{
72
0
}
73
74
namespace{
75
76
/// Checks to see if the request has a parameter of IsSticky:bool=true
77
/// It means that the selected command/button will stay selected after use
78
bool isSticky(const SfxRequest& rReq)
79
0
{
80
0
    const SfxItemSet *pArgs = rReq.GetArgs ();
81
0
    if (pArgs)
82
0
    {
83
0
        const SfxBoolItem* pIsSticky = rReq.GetArg<SfxBoolItem>(FN_PARAM_4);
84
0
        if (pIsSticky && pIsSticky->GetValue())
85
0
            return true;
86
0
    }
87
88
0
    return false;
89
0
}
90
91
}
92
93
rtl::Reference<FuPoor> FuConstructBezierPolygon::Create( ViewShell& rViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument& rDoc, SfxRequest& rReq, bool bPermanent )
94
0
{
95
0
    FuConstructBezierPolygon* pFunc;
96
0
    rtl::Reference<FuPoor> xFunc( pFunc = new FuConstructBezierPolygon( rViewSh, pWin, pView, rDoc, rReq ) );
97
0
    xFunc->DoExecute(rReq);
98
0
    pFunc->SetPermanent(bPermanent || isSticky(rReq));
99
0
    return xFunc;
100
0
}
101
102
void FuConstructBezierPolygon::DoExecute( SfxRequest& rReq )
103
0
{
104
0
    FuConstruct::DoExecute( rReq );
105
106
0
    const SfxItemSet* pArgs = rReq.GetArgs();
107
108
0
    if( !pArgs )
109
0
        return;
110
111
0
    const SfxUnoAnyItem* pPoolItem = pArgs->GetItemIfSet( SID_ADD_MOTION_PATH );
112
0
    if( pPoolItem )
113
0
        maTargets = pPoolItem->GetValue();
114
115
0
    if (nSlotId != SID_DRAW_FREELINE_NOFILL)
116
0
        return;
117
118
0
    const SfxUInt16Item* pTransparence  = rReq.GetArg<SfxUInt16Item>(FN_PARAM_1);
119
0
    const SfxStringItem* pColor         = rReq.GetArg<SfxStringItem>(FN_PARAM_2);
120
0
    const SfxUInt16Item* pWidth         = rReq.GetArg<SfxUInt16Item>(FN_PARAM_3);
121
0
    const SfxStringItem* pShapeName     = rReq.GetArg<SfxStringItem>(SID_SHAPE_NAME);
122
123
0
    if (pTransparence && pTransparence->GetValue() > 0)
124
0
    {
125
0
        mnTransparence = pTransparence->GetValue();
126
0
    }
127
0
    if (pColor && !pColor->GetValue().isEmpty())
128
0
    {
129
0
        msColor = pColor->GetValue();
130
0
    }
131
0
    if (pWidth && pWidth->GetValue() > 0)
132
0
    {
133
0
        mnWidth = pWidth->GetValue();
134
0
    }
135
0
    if (pShapeName && !pShapeName->GetValue().isEmpty())
136
0
    {
137
0
        msShapeName = pShapeName->GetValue();
138
0
    }
139
0
}
140
141
bool FuConstructBezierPolygon::MouseButtonDown(const MouseEvent& rMEvt)
142
0
{
143
0
    bool bReturn = FuConstruct::MouseButtonDown(rMEvt);
144
145
0
    SdrViewEvent aVEvt;
146
0
    SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt);
147
148
0
    if (eHit == SdrHitKind::Handle || rMEvt.IsMod1())
149
0
    {
150
0
        mpView->SetEditMode(SdrViewEditMode::Edit);
151
0
    }
152
0
    else
153
0
    {
154
0
        mpView->SetEditMode(SdrViewEditMode::Create);
155
0
    }
156
157
0
    if (aVEvt.meEvent == SdrEventKind::BeginTextEdit)
158
0
    {
159
        // here, we do not allow text input
160
0
        aVEvt.meEvent = SdrEventKind::BeginDragObj;
161
0
        mpView->EnableExtendedMouseEventDispatcher(false);
162
0
    }
163
0
    else
164
0
    {
165
0
        mpView->EnableExtendedMouseEventDispatcher(true);
166
0
    }
167
168
0
    if (eHit == SdrHitKind::MarkedObject && nEditMode == SID_BEZIER_INSERT)
169
0
    {
170
        // insert gluepoint
171
0
        mpView->BegInsObjPoint(aMDPos, rMEvt.IsMod1());
172
0
    }
173
0
    else
174
0
    {
175
0
        mpView->MouseButtonDown(rMEvt, mpWindow->GetOutDev());
176
177
0
        SdrObject* pObj = mpView->GetCreateObj();
178
179
0
        if (pObj)
180
0
        {
181
0
            SfxItemSet aAttr(mrDoc.GetPool());
182
0
            SetStyleSheet(aAttr, pObj);
183
0
            SetAttributes(aAttr, pObj);
184
0
            pObj->SetMergedItemSet(aAttr);
185
0
        }
186
0
    }
187
188
0
    return bReturn;
189
0
}
190
191
bool FuConstructBezierPolygon::MouseButtonUp(const MouseEvent& rMEvt )
192
0
{
193
0
    if (rMEvt.IsLeft() && IsIgnoreUnexpectedMouseButtonUp())
194
0
        return false;
195
196
0
    bool bReturn = false;
197
0
    bool bCreated = false;
198
199
0
    SdrViewEvent aVEvt;
200
0
    mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aVEvt);
201
202
0
    const size_t nCount = mpView->GetSdrPageView()->GetObjList()->GetObjCount();
203
204
0
    if (mpView->IsInsObjPoint())
205
0
    {
206
0
        mpView->EndInsObjPoint(SdrCreateCmd::ForceEnd);
207
0
    }
208
0
    else
209
0
    {
210
0
        mpView->MouseButtonUp(rMEvt, mpWindow->GetOutDev());
211
0
    }
212
213
0
    if (aVEvt.meEvent == SdrEventKind::EndCreate)
214
0
    {
215
0
        bReturn = true;
216
217
0
        if (nCount+1 == mpView->GetSdrPageView()->GetObjList()->GetObjCount())
218
0
        {
219
0
            bCreated = true;
220
0
        }
221
222
        // trick to suppress FuDraw::DoubleClick
223
0
        bMBDown = false;
224
225
0
    }
226
227
0
    bReturn = FuConstruct::MouseButtonUp(rMEvt) || bReturn;
228
229
0
    bool bDeleted = false;
230
0
    if( bCreated && maTargets.hasValue() )
231
0
    {
232
0
        SdrPathObj* pPathObj = dynamic_cast< SdrPathObj* >( mpView->GetSdrPageView()->GetObjList()->GetObj( nCount ) );
233
0
        SdPage* pPage = dynamic_cast< SdPage* >( pPathObj ? pPathObj->getSdrPageFromSdrObject() : nullptr );
234
0
        if( pPage )
235
0
        {
236
0
            std::shared_ptr< sd::MainSequence > pMainSequence( pPage->getMainSequence() );
237
0
            if( pMainSequence )
238
0
            {
239
0
                Sequence< Any > aTargets;
240
0
                maTargets >>= aTargets;
241
242
0
                sal_Int32 nTCount = aTargets.getLength();
243
0
                if( nTCount > 1 )
244
0
                {
245
0
                    const Any* pTarget = aTargets.getConstArray();
246
0
                    double fDuration = 0.0;
247
0
                    *pTarget++ >>= fDuration;
248
0
                    bool bFirst = true;
249
250
0
                    OUString sPresetId;
251
0
                    switch(nSlotId)
252
0
                    {
253
0
                        case SID_DRAW_BEZIER_NOFILL:
254
0
                            sPresetId = "libo-motionpath-curve";
255
0
                            break;
256
0
                        case SID_DRAW_POLYGON_NOFILL:
257
0
                            sPresetId = "libo-motionpath-polygon";
258
0
                            break;
259
0
                        case SID_DRAW_FREELINE_NOFILL:
260
0
                            sPresetId = "libo-motionpath-freeform-line";
261
0
                            break;
262
0
                    }
263
264
0
                    while( --nTCount )
265
0
                    {
266
0
                        CustomAnimationEffectPtr pCreated( pMainSequence->append( *pPathObj, *pTarget++, fDuration, sPresetId) );
267
0
                        if( bFirst )
268
0
                            bFirst = false;
269
0
                        else
270
0
                            pCreated->setNodeType( css::presentation::EffectNodeType::WITH_PREVIOUS );
271
0
                    }
272
0
                }
273
0
            }
274
0
        }
275
0
        mpView->DeleteMarked();
276
0
        bDeleted = true;
277
0
    }
278
279
0
    if ((!bPermanent && bCreated) || bDeleted)
280
0
    {
281
0
        mrViewShell.GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON);
282
0
    }
283
284
0
    return bReturn;
285
0
}
286
287
void FuConstructBezierPolygon::Activate()
288
0
{
289
0
    mpView->EnableExtendedMouseEventDispatcher(true);
290
291
0
    SdrObjKind eKind;
292
293
0
    switch (nSlotId)
294
0
    {
295
0
        case SID_DRAW_POLYGON_NOFILL:
296
0
        case SID_DRAW_XPOLYGON_NOFILL:
297
0
        {
298
0
            eKind = SdrObjKind::PolyLine;
299
0
        }
300
0
        break;
301
302
0
        case SID_DRAW_POLYGON:
303
0
        case SID_DRAW_XPOLYGON:
304
0
        {
305
0
            eKind = SdrObjKind::Polygon;
306
0
        }
307
0
        break;
308
309
0
        case SID_DRAW_BEZIER_NOFILL:
310
0
        {
311
0
            eKind = SdrObjKind::PathLine;
312
0
        }
313
0
        break;
314
315
0
        case SID_DRAW_BEZIER_FILL:
316
0
        {
317
0
            eKind = SdrObjKind::PathFill;
318
0
        }
319
0
        break;
320
321
0
        case SID_DRAW_FREELINE_NOFILL:
322
0
        {
323
0
            eKind = SdrObjKind::FreehandLine;
324
0
        }
325
0
        break;
326
327
0
        case SID_DRAW_FREELINE:
328
0
        {
329
0
            eKind = SdrObjKind::FreehandFill;
330
0
        }
331
0
        break;
332
333
0
        default:
334
0
        {
335
0
            eKind = SdrObjKind::PathLine;
336
0
        }
337
0
        break;
338
0
    }
339
340
0
    mpView->SetCurrentObj(eKind);
341
342
0
    FuConstruct::Activate();
343
0
}
344
345
void FuConstructBezierPolygon::Deactivate()
346
0
{
347
0
    mpView->EnableExtendedMouseEventDispatcher(false);
348
349
0
    FuConstruct::Deactivate();
350
0
}
351
352
void FuConstructBezierPolygon::SelectionHasChanged()
353
0
{
354
0
    FuDraw::SelectionHasChanged();
355
356
0
    mrViewShell.GetViewShellBase().GetToolBarManager()->SelectionHasChanged(
357
0
        mrViewShell,
358
0
        *mpView);
359
0
}
360
361
namespace {
362
/// Returns the color based on the color names listed in core/include/tools/color.hxx
363
/// Feel free to extend with more color names from color.hxx
364
Color strToColor(std::u16string_view sColor)
365
0
{
366
0
    Color aColor = COL_AUTO;
367
368
0
    if (sColor == u"COL_GRAY")
369
0
        aColor = COL_GRAY;
370
0
    else if (sColor == u"COL_GRAY3")
371
0
        aColor = COL_GRAY3;
372
0
    else if (sColor == u"COL_GRAY7")
373
0
        aColor = COL_GRAY7;
374
375
0
    return aColor;
376
0
}
377
}
378
379
void FuConstructBezierPolygon::SetAttributes(SfxItemSet& rAttr, SdrObject *pObj)
380
0
{
381
0
    if (nSlotId == SID_DRAW_FREELINE_NOFILL)
382
0
    {
383
0
        if (mnTransparence > 0 && mnTransparence <= 100)
384
0
            rAttr.Put(XLineTransparenceItem(mnTransparence));
385
0
        if (!msColor.isEmpty())
386
0
            rAttr.Put(XLineColorItem(OUString(), strToColor(msColor)));
387
0
        if (mnWidth > 0)
388
0
            rAttr.Put(XLineWidthItem(mnWidth));
389
0
        if (!msShapeName.isEmpty())
390
0
            pObj->SetName(msShapeName);
391
0
    }
392
0
}
393
394
/**
395
 * Set current bezier edit mode
396
 */
397
void FuConstructBezierPolygon::SetEditMode(sal_uInt16 nMode)
398
0
{
399
0
    nEditMode = nMode;
400
0
    ForcePointer();
401
402
0
    SfxBindings& rBindings = mrViewShell.GetViewFrame()->GetBindings();
403
0
    rBindings.Invalidate(SID_BEZIER_MOVE);
404
0
    rBindings.Invalidate(SID_BEZIER_INSERT);
405
0
}
406
407
rtl::Reference<SdrObject> FuConstructBezierPolygon::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle)
408
0
{
409
    // case SID_DRAW_POLYGON:
410
    // case SID_DRAW_POLYGON_NOFILL:
411
    // case SID_DRAW_XPOLYGON:
412
    // case SID_DRAW_XPOLYGON_NOFILL:
413
    // case SID_DRAW_FREELINE:
414
    // case SID_DRAW_FREELINE_NOFILL:
415
    // case SID_DRAW_BEZIER_FILL:          // BASIC
416
    // case SID_DRAW_BEZIER_NOFILL:        // BASIC
417
418
0
    rtl::Reference<SdrObject> pObj(SdrObjFactory::MakeNewObject(
419
0
        mpView->getSdrModelFromSdrView(),
420
0
        mpView->GetCurrentObjInventor(),
421
0
        mpView->GetCurrentObjIdentifier()));
422
423
0
    if(pObj)
424
0
    {
425
0
        if( auto pPathObj = dynamic_cast< SdrPathObj *>( pObj.get() ) )
426
0
        {
427
0
            basegfx::B2DPolyPolygon aPoly;
428
429
0
            switch(nID)
430
0
            {
431
0
                case SID_DRAW_BEZIER_FILL:
432
0
                {
433
0
                    const sal_Int32 nWdt(rRectangle.GetWidth() / 2);
434
0
                    const sal_Int32 nHgt(rRectangle.GetHeight() / 2);
435
0
                    const basegfx::B2DPolygon aInnerPoly(basegfx::utils::createPolygonFromEllipse(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()), nWdt, nHgt));
436
437
0
                    aPoly.append(aInnerPoly);
438
0
                    break;
439
0
                }
440
0
                case SID_DRAW_BEZIER_NOFILL:
441
0
                {
442
0
                    basegfx::B2DPolygon aInnerPoly;
443
444
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
445
446
0
                    const basegfx::B2DPoint aCenterBottom(rRectangle.Center().X(), rRectangle.Bottom());
447
0
                    aInnerPoly.appendBezierSegment(
448
0
                        aCenterBottom,
449
0
                        aCenterBottom,
450
0
                        basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()));
451
452
0
                    const basegfx::B2DPoint aCenterTop(rRectangle.Center().X(), rRectangle.Top());
453
0
                    aInnerPoly.appendBezierSegment(
454
0
                        aCenterTop,
455
0
                        aCenterTop,
456
0
                        basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top()));
457
458
0
                    aPoly.append(aInnerPoly);
459
0
                    break;
460
0
                }
461
0
                case SID_DRAW_FREELINE:
462
0
                case SID_DRAW_FREELINE_NOFILL:
463
0
                {
464
0
                    basegfx::B2DPolygon aInnerPoly;
465
466
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
467
468
0
                    aInnerPoly.appendBezierSegment(
469
0
                        basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()),
470
0
                        basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top()),
471
0
                        basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()));
472
473
0
                    aInnerPoly.appendBezierSegment(
474
0
                        basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()),
475
0
                        basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()),
476
0
                        basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top()));
477
478
0
                    if(SID_DRAW_FREELINE == nID)
479
0
                    {
480
0
                        aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()));
481
0
                    }
482
0
                    else
483
0
                    {
484
0
                        aInnerPoly.setClosed(true);
485
0
                    }
486
487
0
                    aPoly.append(aInnerPoly);
488
0
                    break;
489
0
                }
490
0
                case SID_DRAW_XPOLYGON:
491
0
                case SID_DRAW_XPOLYGON_NOFILL:
492
0
                {
493
0
                    basegfx::B2DPolygon aInnerPoly;
494
495
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
496
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()));
497
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top()));
498
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()));
499
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Center().Y()));
500
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()));
501
502
0
                    if(SID_DRAW_XPOLYGON_NOFILL == nID)
503
0
                    {
504
0
                        aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()));
505
0
                    }
506
0
                    else
507
0
                    {
508
0
                        aInnerPoly.setClosed(true);
509
0
                    }
510
511
0
                    aPoly.append(aInnerPoly);
512
0
                    break;
513
0
                }
514
0
                case SID_DRAW_POLYGON:
515
0
                case SID_DRAW_POLYGON_NOFILL:
516
0
                {
517
0
                    basegfx::B2DPolygon aInnerPoly;
518
0
                    const sal_Int32 nWdt(rRectangle.GetWidth());
519
0
                    const sal_Int32 nHgt(rRectangle.GetHeight());
520
521
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
522
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 30) / 100, rRectangle.Top() + (nHgt * 70) / 100));
523
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top() + (nHgt * 15) / 100));
524
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 65) / 100, rRectangle.Top()));
525
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + nWdt, rRectangle.Top() + (nHgt * 30) / 100));
526
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 50) / 100));
527
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 75) / 100));
528
0
                    aInnerPoly.append(basegfx::B2DPoint(rRectangle.Bottom(), rRectangle.Right()));
529
530
0
                    if(SID_DRAW_POLYGON_NOFILL == nID)
531
0
                    {
532
0
                        aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()));
533
0
                    }
534
0
                    else
535
0
                    {
536
0
                        aInnerPoly.setClosed(true);
537
0
                    }
538
539
0
                    aPoly.append(aInnerPoly);
540
0
                    break;
541
0
                }
542
0
            }
543
544
0
            pPathObj->SetPathPoly(aPoly);
545
0
        }
546
0
        else
547
0
        {
548
0
            OSL_FAIL("Object is NO path object");
549
0
        }
550
551
0
        pObj->SetLogicRect(rRectangle);
552
0
    }
553
554
0
    return pObj;
555
0
}
556
557
} // end of namespace sd
558
559
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */