Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/form/fmundo.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 <sal/config.h>
21
22
#include <map>
23
24
#include <sal/macros.h>
25
#include <fmundo.hxx>
26
#include <fmpgeimp.hxx>
27
#include <svx/svditer.hxx>
28
#include <fmobj.hxx>
29
#include <fmprop.hxx>
30
#include <svx/strings.hrc>
31
#include <svx/dialmgr.hxx>
32
#include <svx/fmmodel.hxx>
33
#include <svx/fmpage.hxx>
34
35
#include <com/sun/star/util/XModifyBroadcaster.hpp>
36
#include <com/sun/star/beans/PropertyAttribute.hpp>
37
#include <com/sun/star/container/XContainer.hpp>
38
#include <com/sun/star/container/XContainerListener.hpp>
39
#include <com/sun/star/script/XEventAttacherManager.hpp>
40
#include <com/sun/star/form/binding/XBindableValue.hpp>
41
#include <com/sun/star/form/binding/XListEntrySink.hpp>
42
#include <com/sun/star/sdbc/XConnection.hpp>
43
#include <com/sun/star/uno/XComponentContext.hpp>
44
#include <com/sun/star/script/XScriptListener.hpp>
45
46
#include <svx/fmtools.hxx>
47
#include <tools/debug.hxx>
48
#include <comphelper/diagnose_ex.hxx>
49
#include <sfx2/objsh.hxx>
50
#include <sfx2/event.hxx>
51
#include <osl/mutex.hxx>
52
#include <comphelper/property.hxx>
53
#include <comphelper/types.hxx>
54
#include <connectivity/dbtools.hxx>
55
#include <vcl/svapp.hxx>
56
#include <comphelper/processfactory.hxx>
57
#include <cppuhelper/implbase.hxx>
58
59
60
using namespace ::com::sun::star::uno;
61
using namespace ::com::sun::star::awt;
62
using namespace ::com::sun::star::beans;
63
using namespace ::com::sun::star::container;
64
using namespace ::com::sun::star::script;
65
using namespace ::com::sun::star::lang;
66
using namespace ::com::sun::star::form;
67
using namespace ::com::sun::star::util;
68
using namespace ::com::sun::star::form::binding;
69
using namespace ::com::sun::star::sdbc;
70
using namespace ::svxform;
71
using namespace ::dbtools;
72
73
74
class ScriptEventListenerWrapper : public cppu::WeakImplHelper< XScriptListener >
75
{
76
public:
77
    /// @throws css::uno::RuntimeException
78
    explicit ScriptEventListenerWrapper( FmFormModel& _rModel)
79
125k
        :m_rModel( _rModel )
80
125k
        ,m_attemptedListenerCreation( false )
81
125k
    {
82
83
125k
    }
84
    // XEventListener
85
0
    virtual void SAL_CALL disposing(const EventObject& ) override {}
86
87
    // XScriptListener
88
    virtual void SAL_CALL firing(const  ScriptEvent& evt) override
89
0
    {
90
0
        attemptListenerCreation();
91
0
        if ( m_vbaListener.is() )
92
0
        {
93
0
            m_vbaListener->firing( evt );
94
0
        }
95
0
    }
96
97
    virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override
98
0
    {
99
0
        attemptListenerCreation();
100
0
        if ( m_vbaListener.is() )
101
0
        {
102
0
            return m_vbaListener->approveFiring( evt );
103
0
        }
104
0
        return Any();
105
0
    }
106
107
private:
108
    void attemptListenerCreation()
109
0
    {
110
0
        if ( m_attemptedListenerCreation )
111
0
            return;
112
0
        m_attemptedListenerCreation = true;
113
114
0
        try
115
0
        {
116
0
            const css::uno::Reference<css::uno::XComponentContext>& context(
117
0
                comphelper::getProcessComponentContext());
118
0
            Reference< XScriptListener > const xScriptListener(
119
0
                context->getServiceManager()->createInstanceWithContext(
120
0
                    u"ooo.vba.EventListener"_ustr, context),
121
0
                UNO_QUERY_THROW);
122
0
            Reference< XPropertySet > const xListenerProps( xScriptListener, UNO_QUERY_THROW );
123
            // SfxObjectShellRef is good here since the model controls the lifetime of the shell
124
0
            SfxObjectShellRef const xObjectShell = m_rModel.GetObjectShell();
125
0
            ENSURE_OR_THROW( xObjectShell.is(), "no object shell!" );
126
0
            xListenerProps->setPropertyValue(u"Model"_ustr, Any( xObjectShell->GetModel() ) );
127
128
0
            m_vbaListener = xScriptListener;
129
0
        }
130
0
        catch( Exception const & )
131
0
        {
132
0
            DBG_UNHANDLED_EXCEPTION("svx");
133
0
        }
134
0
    }
135
    FmFormModel&                    m_rModel;
136
    Reference< XScriptListener >    m_vbaListener;
137
    bool                            m_attemptedListenerCreation;
138
139
140
};
141
142
143
namespace {
144
145
// some helper structs for caching property infos
146
147
struct PropertyInfo
148
{
149
    bool    bIsTransientOrReadOnly  : 1;    // the property is transient or read-only, thus we need no undo action for it
150
    bool    bIsValueProperty        : 1;    // the property is the special value property, thus it may be handled
151
                                            // as if it's transient or persistent
152
};
153
154
}
155
156
157
struct PropertySetInfo
158
{
159
    typedef std::map<OUString, PropertyInfo> AllProperties;
160
161
    AllProperties   aProps;                 // all properties of this set which we know so far
162
    bool            bHasEmptyControlSource; // sal_True -> the set has a DataField property, and the current value is an empty string
163
                                            // sal_False -> the set has _no_ such property or its value isn't empty
164
};
165
166
static OUString static_STR_UNDO_PROPERTY;
167
168
169
FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel& _rModel)
170
125k
                   :rModel( _rModel )
171
125k
                   ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel ) )
172
125k
                   ,m_Locks( 0 )
173
125k
                   ,bReadOnly( false )
174
125k
                   ,m_bDisposed( false )
175
125k
{
176
125k
    try
177
125k
    {
178
125k
        m_vbaListener =  new ScriptEventListenerWrapper( _rModel );
179
125k
    }
180
125k
    catch( Exception& )
181
125k
    {
182
0
    }
183
125k
}
184
185
FmXUndoEnvironment::~FmXUndoEnvironment()
186
125k
{
187
125k
    if ( !m_bDisposed )   // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
188
125k
        m_pScriptingEnv->dispose();
189
125k
}
190
191
void FmXUndoEnvironment::dispose()
192
36.3k
{
193
36.3k
    OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::dispose: disposed twice?" );
194
36.3k
    if ( !m_bDisposed )
195
36.3k
        return;
196
197
0
    Lock();
198
199
0
    sal_uInt16 nCount = rModel.GetPageCount();
200
0
    sal_uInt16 i;
201
0
    for (i = 0; i < nCount; i++)
202
0
    {
203
0
        FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i)  );
204
0
        if ( pPage )
205
0
        {
206
0
            Reference< css::form::XForms > xForms = pPage->GetForms( false );
207
0
            if ( xForms.is() )
208
0
                RemoveElement( xForms );
209
0
        }
210
0
    }
211
212
0
    nCount = rModel.GetMasterPageCount();
213
0
    for (i = 0; i < nCount; i++)
214
0
    {
215
0
        FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i)  );
216
0
        if ( pPage )
217
0
        {
218
0
            Reference< css::form::XForms > xForms = pPage->GetForms( false );
219
0
            if ( xForms.is() )
220
0
                RemoveElement( xForms );
221
0
        }
222
0
    }
223
224
0
    UnLock();
225
226
0
    OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
227
0
    if ( rModel.GetObjectShell() )
228
0
        EndListening( *rModel.GetObjectShell() );
229
230
0
    if ( IsListening( rModel ) )
231
0
        EndListening( rModel );
232
233
0
    m_pScriptingEnv->dispose();
234
235
0
    m_bDisposed = true;
236
0
}
237
238
239
void FmXUndoEnvironment::ModeChanged()
240
9.20k
{
241
9.20k
    OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
242
9.20k
    if ( !rModel.GetObjectShell() )
243
0
        return;
244
245
9.20k
    if (bReadOnly == (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
246
0
        return;
247
248
9.20k
    bReadOnly = !bReadOnly;
249
250
9.20k
    sal_uInt16 nCount = rModel.GetPageCount();
251
9.20k
    sal_uInt16 i;
252
18.4k
    for (i = 0; i < nCount; i++)
253
9.20k
    {
254
9.20k
        FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i)  );
255
9.20k
        if ( pPage )
256
9.20k
        {
257
9.20k
            Reference< css::form::XForms > xForms = pPage->GetForms( false );
258
9.20k
            if ( xForms.is() )
259
0
                TogglePropertyListening( xForms );
260
9.20k
        }
261
9.20k
    }
262
263
9.20k
    nCount = rModel.GetMasterPageCount();
264
9.20k
    for (i = 0; i < nCount; i++)
265
0
    {
266
0
        FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i)  );
267
0
        if ( pPage )
268
0
        {
269
0
            Reference< css::form::XForms > xForms = pPage->GetForms( false );
270
0
            if ( xForms.is() )
271
0
                TogglePropertyListening( xForms );
272
0
        }
273
0
    }
274
275
9.20k
    if (!bReadOnly)
276
0
        StartListening(rModel);
277
9.20k
    else
278
9.20k
        EndListening(rModel);
279
9.20k
}
280
281
282
void FmXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
283
10.6M
{
284
10.6M
    if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
285
580k
    {
286
580k
        const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
287
580k
        switch (pSdrHint->GetKind())
288
580k
        {
289
240k
            case SdrHintKind::ObjectInserted:
290
240k
            {
291
240k
                SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
292
240k
                Inserted( pSdrObj );
293
240k
            }   break;
294
89.6k
            case SdrHintKind::ObjectRemoved:
295
89.6k
            {
296
89.6k
                SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
297
89.6k
                Removed( pSdrObj );
298
89.6k
            }
299
89.6k
            break;
300
250k
            default:
301
250k
                break;
302
580k
        }
303
580k
    }
304
10.0M
    else if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
305
100k
    {
306
100k
        switch (static_cast<const SfxEventHint&>(rHint).GetEventId())
307
100k
        {
308
0
            case SfxEventHintId::CreateDoc:
309
0
            case SfxEventHintId::OpenDoc:
310
0
                ModeChanged();
311
0
                break;
312
100k
            default: break;
313
100k
        }
314
100k
    }
315
9.94M
    else if (rHint.GetId() != SfxHintId::NONE)
316
9.94M
    {
317
9.94M
        switch (rHint.GetId())
318
9.94M
        {
319
36.3k
            case SfxHintId::Dying:
320
36.3k
                dispose();
321
36.3k
                rModel.SetObjectShell( nullptr );
322
36.3k
                break;
323
9.20k
            case SfxHintId::ModeChanged:
324
9.20k
                ModeChanged();
325
9.20k
                break;
326
9.90M
            default: break;
327
9.94M
        }
328
9.94M
    }
329
10.6M
}
330
331
void FmXUndoEnvironment::Inserted(SdrObject* pObj)
332
1.97M
{
333
1.97M
    if (pObj->GetObjInventor() == SdrInventor::FmForm)
334
0
    {
335
0
        FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
336
0
        Inserted( pFormObj );
337
0
    }
338
1.97M
    else if (pObj->IsGroupObject())
339
8.87k
    {
340
8.87k
        SdrObjListIter aIter(pObj->GetSubList());
341
32.2k
        while ( aIter.IsMore() )
342
23.3k
            Inserted( aIter.Next() );
343
8.87k
    }
344
1.97M
}
345
346
347
namespace
348
{
349
    bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
350
0
    {
351
0
        if (!xCont.is() || !xElement.is())
352
0
            return false;
353
354
0
        sal_Int32 nCount = xCont->getCount();
355
0
        Reference< XInterface > xComp;
356
0
        for (sal_Int32 i = 0; i < nCount; i++)
357
0
        {
358
0
            try
359
0
            {
360
0
                xCont->getByIndex(i) >>= xComp;
361
0
                if (xComp.is())
362
0
                {
363
0
                    if ( xElement == xComp )
364
0
                        return true;
365
0
                    else
366
0
                    {
367
0
                        Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
368
0
                        if (xCont2.is() && lcl_searchElement(xCont2, xElement))
369
0
                            return true;
370
0
                    }
371
0
                }
372
0
            }
373
0
            catch(const Exception&)
374
0
            {
375
0
                DBG_UNHANDLED_EXCEPTION("svx");
376
0
            }
377
0
        }
378
0
        return false;
379
0
    }
380
}
381
382
383
void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
384
0
{
385
0
    DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
386
0
    if ( !pObj )
387
0
        return;
388
389
    // is the control still assigned to a form
390
0
    Reference< XInterface >  xModel(pObj->GetUnoControlModel(), UNO_QUERY);
391
0
    Reference< XFormComponent >  xContent(xModel, UNO_QUERY);
392
0
    if (!(xContent.is() && pObj->getSdrPageFromSdrObject()))
393
0
        return;
394
395
    // if the component doesn't belong to a form, yet, find one to insert into
396
0
    if (!xContent->getParent().is())
397
0
    {
398
0
        try
399
0
        {
400
0
            const Reference< XIndexContainer >& xObjectParent = pObj->GetOriginalParent();
401
402
0
            FmFormPage& rPage(dynamic_cast< FmFormPage& >( *pObj->getSdrPageFromSdrObject()));
403
0
            Reference< XIndexAccess >  xForms( rPage.GetForms(), UNO_QUERY_THROW );
404
405
0
            Reference< XIndexContainer > xNewParent;
406
0
            Reference< XForm >           xForm;
407
0
            sal_Int32 nPos = -1;
408
0
            if ( lcl_searchElement( xForms, xObjectParent ) )
409
0
            {
410
                // the form which was the parent of the object when it was removed is still
411
                // part of the form component hierarchy of the current page
412
0
                xNewParent = xObjectParent;
413
0
                xForm.set( xNewParent, UNO_QUERY_THROW );
414
0
                nPos = ::std::min( pObj->GetOriginalIndex(), xNewParent->getCount() );
415
0
            }
416
0
            else
417
0
            {
418
0
                xForm.set( rPage.GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
419
0
                xNewParent.set( xForm, UNO_QUERY_THROW );
420
0
                nPos = xNewParent->getCount();
421
0
            }
422
423
0
            FmFormPageImpl::setUniqueName( xContent, xForm );
424
0
            xNewParent->insertByIndex( nPos, Any( xContent ) );
425
426
0
            Reference< XEventAttacherManager >  xManager( xNewParent, UNO_QUERY_THROW );
427
0
            xManager->registerScriptEvents( nPos, pObj->GetOriginalEvents() );
428
0
        }
429
0
        catch( const Exception& )
430
0
        {
431
0
            DBG_UNHANDLED_EXCEPTION("svx");
432
0
        }
433
0
    }
434
435
    // reset FormObject
436
0
    pObj->ClearObjEnv();
437
0
}
438
439
440
void FmXUndoEnvironment::Removed(SdrObject* pObj)
441
528k
{
442
528k
    if ( pObj->IsVirtualObj() )
443
        // for virtual objects, we've already been notified of the removal of the master
444
        // object, which is sufficient here
445
33.7k
        return;
446
447
494k
    if (pObj->GetObjInventor() == SdrInventor::FmForm)
448
0
    {
449
0
        FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
450
0
        Removed(pFormObj);
451
0
    }
452
494k
    else if (pObj->IsGroupObject())
453
6.99k
    {
454
6.99k
        SdrObjListIter aIter(pObj->GetSubList());
455
26.5k
        while ( aIter.IsMore() )
456
19.5k
            Removed( aIter.Next() );
457
6.99k
    }
458
494k
}
459
460
461
void FmXUndoEnvironment::Removed(FmFormObj* pObj)
462
0
{
463
0
    DBG_ASSERT( pObj, "FmXUndoEnvironment::Removed: invalid object!" );
464
0
    if ( !pObj )
465
0
        return;
466
467
    // is the control still assigned to a form
468
0
    Reference< XFormComponent >  xContent(pObj->GetUnoControlModel(), UNO_QUERY);
469
0
    if (!xContent.is())
470
0
        return;
471
472
    // The object is taken out of a list.
473
    // If a father exists, the object is removed at the father and
474
    // noted at the FormObject!
475
476
    // If the object is reinserted and a parent exists, this parent is set though.
477
0
    Reference< XIndexContainer >  xForm(xContent->getParent(), UNO_QUERY);
478
0
    if (!xForm.is())
479
0
        return;
480
481
    // determine which position the child was at
482
0
    const sal_Int32 nPos = getElementPos(xForm, xContent);
483
0
    if (nPos < 0)
484
0
        return;
485
486
0
    Sequence< ScriptEventDescriptor > aEvts;
487
0
    Reference< XEventAttacherManager >  xManager(xForm, UNO_QUERY);
488
0
    if (xManager.is())
489
0
        aEvts = xManager->getScriptEvents(nPos);
490
491
0
    try
492
0
    {
493
0
        pObj->SetObjEnv(xForm, nPos, aEvts);
494
0
        xForm->removeByIndex(nPos);
495
0
    }
496
0
    catch(Exception&)
497
0
    {
498
0
        DBG_UNHANDLED_EXCEPTION("svx");
499
0
    }
500
0
}
501
502
//  XEventListener
503
504
void SAL_CALL FmXUndoEnvironment::disposing(const EventObject& e)
505
0
{
506
    // check if it's an object we have cached information about
507
0
    if (m_pPropertySetCache)
508
0
    {
509
0
        Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
510
0
        if (xSourceSet.is())
511
0
        {
512
0
            PropertySetInfoCache::iterator aSetPos = m_pPropertySetCache->find(xSourceSet);
513
0
            if (aSetPos != m_pPropertySetCache->end())
514
0
                m_pPropertySetCache->erase(aSetPos);
515
0
        }
516
0
    }
517
0
}
518
519
// XPropertyChangeListener
520
521
void SAL_CALL FmXUndoEnvironment::propertyChange(const PropertyChangeEvent& evt)
522
0
{
523
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
524
525
0
    if (!IsLocked())
526
0
    {
527
0
        Reference< XPropertySet >  xSet(evt.Source, UNO_QUERY);
528
0
        if (!xSet.is())
529
0
            return;
530
531
        // if it's a "default value" property of a control model, set the according "value" property
532
0
        static constexpr OUString pDefaultValueProperties[] = {
533
0
            FM_PROP_DEFAULT_TEXT, FM_PROP_DEFAULTCHECKED, FM_PROP_DEFAULT_DATE, FM_PROP_DEFAULT_TIME,
534
0
            FM_PROP_DEFAULT_VALUE, FM_PROP_DEFAULT_SELECT_SEQ, FM_PROP_EFFECTIVE_DEFAULT
535
0
        };
536
0
        static constexpr OUString aValueProperties[] = {
537
0
            FM_PROP_TEXT, FM_PROP_STATE, FM_PROP_DATE, FM_PROP_TIME,
538
0
            FM_PROP_VALUE, FM_PROP_SELECT_SEQ, FM_PROP_EFFECTIVE_VALUE
539
0
        };
540
0
        sal_Int32 nDefaultValueProps = SAL_N_ELEMENTS(pDefaultValueProperties);
541
0
        OSL_ENSURE(SAL_N_ELEMENTS(aValueProperties) == nDefaultValueProps,
542
0
            "FmXUndoEnvironment::propertyChange: inconsistence!");
543
0
        for (sal_Int32 i=0; i<nDefaultValueProps; ++i)
544
0
        {
545
0
            if (evt.PropertyName == pDefaultValueProperties[i])
546
0
            {
547
0
                try
548
0
                {
549
0
                    xSet->setPropertyValue(aValueProperties[i], evt.NewValue);
550
0
                }
551
0
                catch(const Exception&)
552
0
                {
553
0
                    OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
554
0
                }
555
0
            }
556
0
        }
557
558
        // no Undo for transient and readonly props. But unfortunately "transient" is not only that the
559
        // "transient" flag is set for the property in question, instead it is somewhat more complex
560
        // Transience criterions are:
561
        // - the "transient" flag is set for the property
562
        // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
563
        //   to a database column. Note that it doesn't matter here whether the control actually
564
        //   *is* bound to a column
565
        // - OR the control is bound to an external value via XBindableValue/XValueBinding
566
        //   which does not have a "ExternalData" property being <TRUE/>
567
568
0
        if (!m_pPropertySetCache)
569
0
            m_pPropertySetCache = std::make_unique<PropertySetInfoCache>();
570
571
        // let's see if we know something about the set
572
0
        PropertySetInfoCache::iterator aSetPos = m_pPropertySetCache->find(xSet);
573
0
        if (aSetPos == m_pPropertySetCache->end())
574
0
        {
575
0
            PropertySetInfo aNewEntry;
576
0
            if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xSet))
577
0
            {
578
0
                aNewEntry.bHasEmptyControlSource = false;
579
0
            }
580
0
            else
581
0
            {
582
0
                try
583
0
                {
584
0
                    Any aCurrentControlSource = xSet->getPropertyValue(FM_PROP_CONTROLSOURCE);
585
0
                    aNewEntry.bHasEmptyControlSource = !aCurrentControlSource.hasValue() || ::comphelper::getString(aCurrentControlSource).isEmpty();
586
0
                }
587
0
                catch(const Exception&)
588
0
                {
589
0
                    DBG_UNHANDLED_EXCEPTION("svx");
590
0
                }
591
0
            }
592
0
            aSetPos = m_pPropertySetCache->emplace(xSet,aNewEntry).first;
593
0
            DBG_ASSERT(aSetPos != m_pPropertySetCache->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
594
0
        }
595
0
        else
596
0
        {   // is it the DataField property ?
597
0
            if (evt.PropertyName == FM_PROP_CONTROLSOURCE)
598
0
            {
599
0
                aSetPos->second.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
600
0
            }
601
0
        }
602
603
        // now we have access to the cached info about the set
604
        // let's see what we know about the property
605
0
        PropertySetInfo::AllProperties& rPropInfos = aSetPos->second.aProps;
606
0
        PropertySetInfo::AllProperties::iterator aPropertyPos = rPropInfos.find(evt.PropertyName);
607
0
        if (aPropertyPos == rPropInfos.end())
608
0
        {   // nothing 'til now ... have to change this...
609
0
            PropertyInfo aNewEntry;
610
611
            // the attributes
612
0
            sal_Int32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName(evt.PropertyName).Attributes;
613
0
            aNewEntry.bIsTransientOrReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
614
615
            // check if it is the special "DataFieldProperty"
616
0
            aNewEntry.bIsValueProperty = false;
617
0
            try
618
0
            {
619
0
                if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY, xSet))
620
0
                {
621
0
                    Any aControlSourceProperty = xSet->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY);
622
0
                    OUString sControlSourceProperty;
623
0
                    aControlSourceProperty >>= sControlSourceProperty;
624
625
0
                    aNewEntry.bIsValueProperty = (sControlSourceProperty == evt.PropertyName);
626
0
                }
627
0
            }
628
0
            catch(const Exception&)
629
0
            {
630
0
                DBG_UNHANDLED_EXCEPTION("svx");
631
0
            }
632
633
            // insert the new entry
634
0
            aPropertyPos = rPropInfos.emplace(evt.PropertyName,aNewEntry).first;
635
0
            DBG_ASSERT(aPropertyPos != rPropInfos.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
636
0
        }
637
638
        // now we have access to the cached info about the property affected
639
        // and are able to decide whether or not we need an undo action
640
641
0
        bool bAddUndoAction = rModel.IsUndoEnabled();
642
        // no UNDO for transient/readonly properties
643
0
        if ( bAddUndoAction && aPropertyPos->second.bIsTransientOrReadOnly )
644
0
            bAddUndoAction = false;
645
646
0
        if ( bAddUndoAction && aPropertyPos->second.bIsValueProperty )
647
0
        {
648
            // no UNDO when the "value" property changes, but the ControlSource is non-empty
649
            // (in this case the control is intended to be bound to a database column)
650
0
            if ( !aSetPos->second.bHasEmptyControlSource )
651
0
                bAddUndoAction = false;
652
653
            // no UNDO if the control is currently bound to an external value
654
0
            if ( bAddUndoAction )
655
0
            {
656
0
                Reference< XBindableValue > xBindable( evt.Source, UNO_QUERY );
657
0
                Reference< XValueBinding > xBinding;
658
0
                if ( xBindable.is() )
659
0
                    xBinding = xBindable->getValueBinding();
660
661
0
                Reference< XPropertySet > xBindingProps;
662
0
                Reference< XPropertySetInfo > xBindingPropsPSI;
663
0
                if ( xBindable.is() )
664
0
                    xBindingProps.set( xBinding, UNO_QUERY );
665
0
                if ( xBindingProps.is() )
666
0
                    xBindingPropsPSI = xBindingProps->getPropertySetInfo();
667
                // TODO: we should cache all those things, else this might be too expensive.
668
                // However, this requires we're notified of changes in the value binding
669
670
0
                static constexpr OUString s_sExternalData = u"ExternalData"_ustr;
671
0
                if ( xBindingPropsPSI.is() && xBindingPropsPSI->hasPropertyByName( s_sExternalData ) )
672
0
                {
673
0
                    bool bExternalData = true;
674
0
                    OSL_VERIFY( xBindingProps->getPropertyValue( s_sExternalData ) >>= bExternalData );
675
0
                    bAddUndoAction = !bExternalData;
676
0
                }
677
0
                else
678
0
                    bAddUndoAction = !xBinding.is();
679
0
            }
680
0
        }
681
682
0
        if ( bAddUndoAction && ( evt.PropertyName == FM_PROP_STRINGITEMLIST ) )
683
0
        {
684
0
            Reference< XListEntrySink > xSink( evt.Source, UNO_QUERY );
685
0
            if ( xSink.is() && xSink->getListEntrySource().is() )
686
                // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
687
0
                bAddUndoAction = false;
688
0
        }
689
690
0
        if ( bAddUndoAction )
691
0
        {
692
0
            aGuard.clear();
693
            // TODO: this is a potential race condition: two threads here could in theory
694
            // add their undo actions out-of-order
695
696
0
            SolarMutexGuard aSolarGuard;
697
0
            rModel.AddUndo(std::make_unique<FmUndoPropertyAction>(rModel, evt));
698
0
        }
699
0
    }
700
0
    else
701
0
    {
702
        // if it's the DataField property we may have to adjust our cache
703
0
        if (m_pPropertySetCache && evt.PropertyName == FM_PROP_CONTROLSOURCE)
704
0
        {
705
0
            Reference< XPropertySet >  xSet(evt.Source, UNO_QUERY);
706
0
            PropertySetInfo& rSetInfo = (*m_pPropertySetCache)[xSet];
707
0
            rSetInfo.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
708
0
        }
709
0
    }
710
0
}
711
712
// XContainerListener
713
714
void SAL_CALL FmXUndoEnvironment::elementInserted(const ContainerEvent& evt)
715
0
{
716
0
    SolarMutexGuard aSolarGuard;
717
0
    ::osl::MutexGuard aGuard( m_aMutex );
718
719
    // new object for listening
720
0
    Reference< XInterface >  xIface;
721
0
    evt.Element >>= xIface;
722
0
    OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
723
0
    AddElement(xIface);
724
725
0
    implSetModified();
726
0
}
727
728
729
void FmXUndoEnvironment::implSetModified()
730
0
{
731
0
    if ( !IsLocked() && rModel.GetObjectShell() )
732
0
    {
733
0
        rModel.GetObjectShell()->SetModified();
734
0
    }
735
0
}
736
737
738
void SAL_CALL FmXUndoEnvironment::elementReplaced(const ContainerEvent& evt)
739
0
{
740
0
    SolarMutexGuard aSolarGuard;
741
0
    ::osl::MutexGuard aGuard( m_aMutex );
742
743
0
    Reference< XInterface >  xIface;
744
0
    evt.ReplacedElement >>= xIface;
745
0
    OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
746
0
    RemoveElement(xIface);
747
748
0
    evt.Element >>= xIface;
749
0
    AddElement(xIface);
750
751
0
    implSetModified();
752
0
}
753
754
755
void SAL_CALL FmXUndoEnvironment::elementRemoved(const ContainerEvent& evt)
756
0
{
757
0
    SolarMutexGuard aSolarGuard;
758
0
    ::osl::MutexGuard aGuard( m_aMutex );
759
760
0
    Reference< XInterface >  xIface( evt.Element, UNO_QUERY );
761
0
    OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
762
0
    RemoveElement(xIface);
763
764
0
    implSetModified();
765
0
}
766
767
768
void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ )
769
0
{
770
0
    implSetModified();
771
0
}
772
773
774
void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
775
0
{
776
0
    Lock();
777
0
    AddElement(Reference<XInterface>( rForms, UNO_QUERY ));
778
0
    UnLock();
779
0
}
780
781
782
void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
783
0
{
784
0
    Lock();
785
0
    RemoveElement(Reference<XInterface>( rForms, UNO_QUERY ));
786
0
    UnLock();
787
0
}
788
789
790
void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
791
0
{
792
    // listen at the container
793
0
    Reference< XIndexContainer >  xContainer(Element, UNO_QUERY);
794
0
    if (xContainer.is())
795
0
    {
796
0
        sal_uInt32 nCount = xContainer->getCount();
797
0
        Reference< XInterface >  xIface;
798
0
        for (sal_uInt32 i = 0; i < nCount; i++)
799
0
        {
800
0
            xContainer->getByIndex(i) >>= xIface;
801
0
            TogglePropertyListening(xIface);
802
0
        }
803
0
    }
804
805
0
    Reference< XPropertySet >  xSet(Element, UNO_QUERY);
806
0
    if (xSet.is())
807
0
    {
808
0
        if (!bReadOnly)
809
0
            xSet->addPropertyChangeListener( OUString(), this );
810
0
        else
811
0
            xSet->removePropertyChangeListener( OUString(), this );
812
0
    }
813
0
}
814
815
816
void FmXUndoEnvironment::switchListening( const Reference< XIndexContainer >& _rxContainer, bool _bStartListening )
817
0
{
818
0
    OSL_PRECOND( _rxContainer.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
819
0
    if ( !_rxContainer.is() )
820
0
        return;
821
822
0
    try
823
0
    {
824
        // if it's an EventAttacherManager, then we need to listen for
825
        // script events
826
0
        Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY );
827
0
        if ( xManager.is() )
828
0
        {
829
0
            if ( _bStartListening )
830
0
            {
831
0
                m_pScriptingEnv->registerEventAttacherManager( xManager );
832
0
                if ( m_vbaListener.is() )
833
0
                    xManager->addScriptListener( m_vbaListener );
834
0
            }
835
0
            else
836
0
            {
837
0
                m_pScriptingEnv->revokeEventAttacherManager( xManager );
838
0
                if ( m_vbaListener.is() )
839
0
                    xManager->removeScriptListener( m_vbaListener );
840
0
            }
841
0
        }
842
843
        // also handle all children of this element
844
0
        sal_uInt32 nCount = _rxContainer->getCount();
845
0
        Reference< XInterface > xInterface;
846
0
        for ( sal_uInt32 i = 0; i < nCount; ++i )
847
0
        {
848
0
            _rxContainer->getByIndex( i ) >>= xInterface;
849
0
            if ( _bStartListening )
850
0
                AddElement( xInterface );
851
0
            else
852
0
                RemoveElement( xInterface );
853
0
        }
854
855
        // be notified of any changes in the container elements
856
0
        Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
857
0
        OSL_ENSURE( xSimpleContainer.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
858
0
        if ( xSimpleContainer.is() )
859
0
        {
860
0
            if ( _bStartListening )
861
0
                xSimpleContainer->addContainerListener( this );
862
0
            else
863
0
                xSimpleContainer->removeContainerListener( this );
864
0
        }
865
0
    }
866
0
    catch( const Exception& )
867
0
    {
868
0
        TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
869
0
    }
870
0
}
871
872
873
void FmXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening )
874
0
{
875
0
    OSL_PRECOND( _rxObject.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
876
877
0
    try
878
0
    {
879
0
        if ( !bReadOnly )
880
0
        {
881
0
            Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
882
0
            if ( xProps.is() )
883
0
            {
884
0
                if ( _bStartListening )
885
0
                    xProps->addPropertyChangeListener( OUString(), this );
886
0
                else
887
0
                    xProps->removePropertyChangeListener( OUString(), this );
888
0
            }
889
0
        }
890
891
0
        Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
892
0
        if ( xBroadcaster.is() )
893
0
        {
894
0
            if ( _bStartListening )
895
0
                xBroadcaster->addModifyListener( this );
896
0
            else
897
0
                xBroadcaster->removeModifyListener( this );
898
0
        }
899
0
    }
900
0
    catch( const Exception& )
901
0
    {
902
0
        TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
903
0
    }
904
0
}
905
906
907
void FmXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
908
0
{
909
0
    OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
910
911
    // listen at the container
912
0
    Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
913
0
    if ( xContainer.is() )
914
0
        switchListening( xContainer, true );
915
916
0
    switchListening( _rxElement, true );
917
0
}
918
919
920
void FmXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
921
0
{
922
0
    if ( m_bDisposed )
923
0
        return;
924
925
0
    switchListening( _rxElement, false );
926
927
0
    if (!bReadOnly)
928
0
    {
929
        // reset the ActiveConnection if the form is to be removed. This will (should) free the resources
930
        // associated with this connection
931
        // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
932
0
        Reference< XForm > xForm( _rxElement, UNO_QUERY );
933
0
        Reference< XPropertySet > xFormProperties( xForm, UNO_QUERY );
934
0
        if ( xFormProperties.is() )
935
0
        {
936
0
            Reference< XConnection > xDummy;
937
0
            if ( !isEmbeddedInDatabase( _rxElement, xDummy ) )
938
                // (if there is a connection in the context of the component, setting
939
                // a new connection would be vetoed, anyway)
940
                // #i34196#
941
0
                xFormProperties->setPropertyValue( FM_PROP_ACTIVE_CONNECTION, Any() );
942
0
        }
943
0
    }
944
945
0
    Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
946
0
    if ( xContainer.is() )
947
0
        switchListening( xContainer, false );
948
0
}
949
950
951
FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel& rNewMod, const PropertyChangeEvent& evt)
952
0
                     :SdrUndoAction(rNewMod)
953
0
                     ,xObj(evt.Source, UNO_QUERY)
954
0
                     ,aPropertyName(evt.PropertyName)
955
0
                     ,aNewValue(evt.NewValue)
956
0
                     ,aOldValue(evt.OldValue)
957
0
{
958
0
    if (rNewMod.GetObjectShell())
959
0
        rNewMod.GetObjectShell()->SetModified();
960
0
    if(static_STR_UNDO_PROPERTY.isEmpty())
961
0
        static_STR_UNDO_PROPERTY = SvxResId(RID_STR_UNDO_PROPERTY);
962
0
}
963
964
965
void FmUndoPropertyAction::Undo()
966
0
{
967
0
    FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(m_rMod).GetUndoEnv();
968
969
0
    if (!xObj.is() || rEnv.IsLocked())
970
0
        return;
971
972
0
    rEnv.Lock();
973
0
    try
974
0
    {
975
0
        xObj->setPropertyValue( aPropertyName, aOldValue );
976
0
    }
977
0
    catch( const Exception& )
978
0
    {
979
0
        TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
980
0
    }
981
0
    rEnv.UnLock();
982
0
}
983
984
985
void FmUndoPropertyAction::Redo()
986
0
{
987
0
    FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(m_rMod).GetUndoEnv();
988
989
0
    if (!xObj.is() || rEnv.IsLocked())
990
0
        return;
991
992
0
    rEnv.Lock();
993
0
    try
994
0
    {
995
0
        xObj->setPropertyValue( aPropertyName, aNewValue );
996
0
    }
997
0
    catch( const Exception& )
998
0
    {
999
0
        TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
1000
0
    }
1001
0
    rEnv.UnLock();
1002
0
}
1003
1004
1005
OUString FmUndoPropertyAction::GetComment() const
1006
0
{
1007
0
    OUString aStr = static_STR_UNDO_PROPERTY.replaceFirst( "#", aPropertyName );
1008
0
    return aStr;
1009
0
}
1010
1011
1012
FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
1013
                                             Action _eAction,
1014
                                             const Reference< XIndexContainer > & xCont,
1015
                                             const Reference< XInterface > & xElem,
1016
                                             sal_Int32 nIdx)
1017
0
                      :SdrUndoAction( _rMod )
1018
0
                      ,m_xContainer( xCont )
1019
0
                      ,m_nIndex( nIdx )
1020
0
                      ,m_eAction( _eAction )
1021
0
{
1022
0
    OSL_ENSURE( nIdx >= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
1023
        // some old code suggested this could be a valid argument. However, this code was
1024
        // buggy, and it *seemed* that nobody used it - so it was removed.
1025
1026
0
    if ( !(xCont.is() && xElem.is()) )
1027
0
        return;
1028
1029
    // normalize
1030
0
    m_xElement = xElem;
1031
0
    if ( m_eAction != Removed )
1032
0
        return;
1033
1034
0
    if (m_nIndex >= 0)
1035
0
    {
1036
0
        Reference< XEventAttacherManager >  xManager( xCont, UNO_QUERY );
1037
0
        if ( xManager.is() )
1038
0
            m_aEvents = xManager->getScriptEvents(m_nIndex);
1039
0
    }
1040
0
    else
1041
0
        m_xElement = nullptr;
1042
1043
    // we now own the element
1044
0
    m_xOwnElement = m_xElement;
1045
0
}
1046
1047
1048
FmUndoContainerAction::~FmUndoContainerAction()
1049
0
{
1050
    // if we own the object...
1051
0
    DisposeElement( m_xOwnElement );
1052
0
}
1053
1054
1055
void FmUndoContainerAction::DisposeElement( const Reference< XInterface > & xElem )
1056
0
{
1057
0
    Reference< XComponent > xComp( xElem, UNO_QUERY );
1058
0
    if ( xComp.is() )
1059
0
    {
1060
        // and the object does not have a parent
1061
0
        Reference< XChild >  xChild( xElem, UNO_QUERY );
1062
0
        if ( xChild.is() && !xChild->getParent().is() )
1063
            // -> dispose it
1064
0
            xComp->dispose();
1065
0
    }
1066
0
}
1067
1068
1069
void FmUndoContainerAction::implReInsert( )
1070
0
{
1071
0
    if ( m_xContainer->getCount() < m_nIndex )
1072
0
        return;
1073
1074
    // insert the element
1075
0
    Any aVal;
1076
0
    if ( m_xContainer->getElementType() == cppu::UnoType<XFormComponent>::get() )
1077
0
    {
1078
0
        aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
1079
0
    }
1080
0
    else
1081
0
    {
1082
0
        aVal <<= Reference< XForm >( m_xElement, UNO_QUERY );
1083
0
    }
1084
0
    m_xContainer->insertByIndex( m_nIndex, aVal );
1085
1086
0
    OSL_ENSURE( getElementPos( m_xContainer, m_xElement ) == m_nIndex, "FmUndoContainerAction::implReInsert: insertion did not work!" );
1087
1088
    // register the events
1089
0
    Reference< XEventAttacherManager >  xManager( m_xContainer, UNO_QUERY );
1090
0
    if ( xManager.is() )
1091
0
        xManager->registerScriptEvents( m_nIndex, m_aEvents );
1092
1093
    // we don't own the object anymore
1094
0
    m_xOwnElement = nullptr;
1095
0
}
1096
1097
1098
void FmUndoContainerAction::implReRemove( )
1099
0
{
1100
0
    Reference< XInterface > xElement;
1101
0
    if ( ( m_nIndex >= 0 ) && ( m_nIndex < m_xContainer->getCount() ) )
1102
0
        m_xContainer->getByIndex( m_nIndex ) >>= xElement;
1103
1104
0
    if ( xElement != m_xElement )
1105
0
    {
1106
        // the indexes in the container changed. Okay, so go the long way and
1107
        // manually determine the index
1108
0
        m_nIndex = getElementPos( m_xContainer, m_xElement );
1109
0
        if ( m_nIndex != -1 )
1110
0
            xElement = m_xElement;
1111
0
    }
1112
1113
0
    OSL_ENSURE( xElement == m_xElement, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
1114
0
    if ( xElement == m_xElement )
1115
0
    {
1116
0
        Reference< XEventAttacherManager >  xManager( m_xContainer, UNO_QUERY );
1117
0
        if ( xManager.is() )
1118
0
            m_aEvents = xManager->getScriptEvents( m_nIndex );
1119
0
        m_xContainer->removeByIndex( m_nIndex );
1120
        // from now on, we own this object
1121
0
        m_xOwnElement = m_xElement;
1122
0
    }
1123
0
}
1124
1125
1126
void FmUndoContainerAction::Undo()
1127
0
{
1128
0
    FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( m_rMod ).GetUndoEnv();
1129
1130
0
    if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1131
0
        return;
1132
1133
0
    rEnv.Lock();
1134
0
    try
1135
0
    {
1136
0
        switch ( m_eAction )
1137
0
        {
1138
0
        case Inserted:
1139
0
            implReRemove();
1140
0
            break;
1141
1142
0
        case Removed:
1143
0
            implReInsert();
1144
0
            break;
1145
0
        }
1146
0
    }
1147
0
    catch( const Exception& )
1148
0
    {
1149
0
        TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
1150
0
    }
1151
0
    rEnv.UnLock();
1152
0
}
1153
1154
1155
void FmUndoContainerAction::Redo()
1156
0
{
1157
0
    FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( m_rMod ).GetUndoEnv();
1158
0
    if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
1159
0
        return;
1160
1161
0
    rEnv.Lock();
1162
0
    try
1163
0
    {
1164
0
        switch ( m_eAction )
1165
0
        {
1166
0
        case Inserted:
1167
0
            implReInsert();
1168
0
            break;
1169
1170
0
        case Removed:
1171
0
            implReRemove();
1172
0
            break;
1173
0
        }
1174
0
    }
1175
0
    catch( const Exception& )
1176
0
    {
1177
0
        TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
1178
0
    }
1179
0
    rEnv.UnLock();
1180
0
}
1181
1182
1183
FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel& _rMod, SdrUnoObj* _pObject, const Reference< XControlModel > & _xReplaced)
1184
0
    :SdrUndoAction(_rMod)
1185
0
    ,m_xReplaced(_xReplaced)
1186
0
    ,m_pObject(_pObject)
1187
0
{
1188
0
}
1189
1190
1191
FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
1192
0
{
1193
    // dispose our element if nobody else is responsible for
1194
0
    DisposeElement(m_xReplaced);
1195
0
}
1196
1197
1198
void FmUndoModelReplaceAction::DisposeElement( const css::uno::Reference< css::awt::XControlModel>& xReplaced )
1199
0
{
1200
0
    Reference< XComponent >  xComp(xReplaced, UNO_QUERY);
1201
0
    if (xComp.is())
1202
0
    {
1203
0
        Reference< XChild >  xChild(xReplaced, UNO_QUERY);
1204
0
        if (!xChild.is() || !xChild->getParent().is())
1205
0
            xComp->dispose();
1206
0
    }
1207
0
}
1208
1209
1210
void FmUndoModelReplaceAction::Undo()
1211
0
{
1212
0
    try
1213
0
    {
1214
0
        Reference< XControlModel > xCurrentModel( m_pObject->GetUnoControlModel() );
1215
1216
        // replace the model within the parent
1217
0
        Reference< XChild > xCurrentAsChild( xCurrentModel, UNO_QUERY );
1218
0
        Reference< XNameContainer > xCurrentsParent;
1219
0
        if ( xCurrentAsChild.is() )
1220
0
            xCurrentsParent.set(xCurrentAsChild->getParent(), css::uno::UNO_QUERY);
1221
0
        DBG_ASSERT( xCurrentsParent.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
1222
1223
0
        if ( xCurrentsParent.is() )
1224
0
        {
1225
            // the form container works with FormComponents
1226
0
            Reference< XFormComponent > xComponent( m_xReplaced, UNO_QUERY );
1227
0
            DBG_ASSERT( xComponent.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
1228
1229
0
            Reference< XPropertySet > xCurrentAsSet( xCurrentModel, UNO_QUERY );
1230
0
            DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME, xCurrentAsSet ), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
1231
1232
0
            OUString sName;
1233
0
            xCurrentAsSet->getPropertyValue( FM_PROP_NAME ) >>= sName;
1234
0
            xCurrentsParent->replaceByName( sName, Any( xComponent ) );
1235
1236
0
            m_pObject->SetUnoControlModel(m_xReplaced);
1237
0
            m_pObject->SetChanged();
1238
1239
0
            m_xReplaced = std::move(xCurrentModel);
1240
0
        }
1241
0
    }
1242
0
    catch(Exception&)
1243
0
    {
1244
0
        OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
1245
0
    }
1246
0
}
1247
1248
1249
OUString FmUndoModelReplaceAction::GetComment() const
1250
0
{
1251
0
    return SvxResId(RID_STR_UNDO_MODEL_REPLACE);
1252
0
}
1253
1254
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */