Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/form/formcontroller.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
21
#include <fmcontrolbordermanager.hxx>
22
#include <fmcontrollayout.hxx>
23
#include <formcontroller.hxx>
24
#include <formfeaturedispatcher.hxx>
25
#include <fmdocumentclassification.hxx>
26
#include <formcontrolling.hxx>
27
#include <fmprop.hxx>
28
#include <svx/dialmgr.hxx>
29
#include <svx/strings.hrc>
30
#include <fmservs.hxx>
31
#include <svx/fmtools.hxx>
32
#include <fmurl.hxx>
33
34
#include <com/sun/star/awt/FocusChangeReason.hpp>
35
#include <com/sun/star/awt/XCheckBox.hpp>
36
#include <com/sun/star/awt/XComboBox.hpp>
37
#include <com/sun/star/awt/XListBox.hpp>
38
#include <com/sun/star/awt/XVclWindowPeer.hpp>
39
#include <com/sun/star/awt/TabController.hpp>
40
#include <com/sun/star/beans/PropertyAttribute.hpp>
41
#include <com/sun/star/container/XIdentifierReplace.hpp>
42
#include <com/sun/star/form/TabulatorCycle.hpp>
43
#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
44
#include <com/sun/star/form/XBoundComponent.hpp>
45
#include <com/sun/star/form/XBoundControl.hpp>
46
#include <com/sun/star/form/XGridControl.hpp>
47
#include <com/sun/star/form/XLoadable.hpp>
48
#include <com/sun/star/form/XReset.hpp>
49
#include <com/sun/star/form/control/FilterControl.hpp>
50
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
51
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
52
#include <com/sun/star/lang/NoSupportException.hpp>
53
#include <com/sun/star/sdb/ParametersRequest.hpp>
54
#include <com/sun/star/sdb/RowChangeAction.hpp>
55
#include <com/sun/star/sdb/SQLFilterOperator.hpp>
56
#include <com/sun/star/sdb/XInteractionSupplyParameters.hpp>
57
#include <com/sun/star/sdbc/ColumnValue.hpp>
58
#include <com/sun/star/task/InteractionHandler.hpp>
59
#include <com/sun/star/form/runtime/FormOperations.hpp>
60
#include <com/sun/star/form/runtime/FormFeature.hpp>
61
#include <com/sun/star/container/XContainer.hpp>
62
#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
63
#include <com/sun/star/util/NumberFormatter.hpp>
64
#include <com/sun/star/sdb/SQLContext.hpp>
65
#include <com/sun/star/sdb/XColumn.hpp>
66
67
#include <comphelper/enumhelper.hxx>
68
#include <comphelper/interaction.hxx>
69
#include <comphelper/processfactory.hxx>
70
#include <comphelper/property.hxx>
71
#include <comphelper/sequence.hxx>
72
#include <comphelper/flagguard.hxx>
73
#include <comphelper/types.hxx>
74
#include <cppuhelper/supportsservice.hxx>
75
#include <connectivity/IParseContext.hxx>
76
#include <connectivity/dbtools.hxx>
77
#include <connectivity/sqlparse.hxx>
78
#include <toolkit/controls/unocontrol.hxx>
79
#include <toolkit/helper/vclunohelper.hxx>
80
#include <tools/debug.hxx>
81
#include <comphelper/diagnose_ex.hxx>
82
#include <unotools/localedatawrapper.hxx>
83
#include <vcl/svapp.hxx>
84
#include <vcl/settings.hxx>
85
#include <o3tl/safeint.hxx>
86
#include <osl/mutex.hxx>
87
#include <sal/log.hxx>
88
89
#include <algorithm>
90
#include <iterator>
91
92
using namespace ::com::sun::star;
93
using namespace ::comphelper;
94
using namespace ::connectivity;
95
using namespace ::dbtools;
96
97
98
css::uno::Reference< css::uno::XInterface >
99
    FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
100
0
{
101
0
    return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
102
0
}
103
104
namespace svxform
105
{
106
107
    using ::com::sun::star::sdb::XColumn;
108
    using ::com::sun::star::awt::XControl;
109
    using ::com::sun::star::awt::TabController;
110
    using ::com::sun::star::awt::XToolkit;
111
    using ::com::sun::star::awt::XWindowPeer;
112
    using ::com::sun::star::form::XGrid;
113
    using ::com::sun::star::beans::XPropertySet;
114
    using ::com::sun::star::uno::UNO_SET_THROW;
115
    using ::com::sun::star::uno::UNO_QUERY_THROW;
116
    using ::com::sun::star::container::XIndexAccess;
117
    using ::com::sun::star::uno::Exception;
118
    using ::com::sun::star::uno::XInterface;
119
    using ::com::sun::star::uno::UNO_QUERY;
120
    using ::com::sun::star::uno::Sequence;
121
    using ::com::sun::star::uno::Reference;
122
    using ::com::sun::star::beans::XPropertySetInfo;
123
    using ::com::sun::star::beans::PropertyValue;
124
    using ::com::sun::star::lang::IndexOutOfBoundsException;
125
    using ::com::sun::star::sdb::XInteractionSupplyParameters;
126
    using ::com::sun::star::awt::XTextComponent;
127
    using ::com::sun::star::awt::XTextListener;
128
    using ::com::sun::star::uno::Any;
129
    using ::com::sun::star::frame::XDispatch;
130
    using ::com::sun::star::lang::XMultiServiceFactory;
131
    using ::com::sun::star::uno::Type;
132
    using ::com::sun::star::lang::IllegalArgumentException;
133
    using ::com::sun::star::sdbc::XConnection;
134
    using ::com::sun::star::sdbc::XRowSet;
135
    using ::com::sun::star::sdbc::XDatabaseMetaData;
136
    using ::com::sun::star::util::XNumberFormatsSupplier;
137
    using ::com::sun::star::util::NumberFormatter;
138
    using ::com::sun::star::util::XNumberFormatter;
139
    using ::com::sun::star::sdbcx::XColumnsSupplier;
140
    using ::com::sun::star::container::XNameAccess;
141
    using ::com::sun::star::lang::EventObject;
142
    using ::com::sun::star::beans::Property;
143
    using ::com::sun::star::container::XEnumeration;
144
    using ::com::sun::star::form::XFormComponent;
145
    using ::com::sun::star::form::runtime::XFormOperations;
146
    using ::com::sun::star::form::runtime::FilterEvent;
147
    using ::com::sun::star::form::runtime::XFilterControllerListener;
148
    using ::com::sun::star::awt::XControlContainer;
149
    using ::com::sun::star::container::XIdentifierReplace;
150
    using ::com::sun::star::form::XFormControllerListener;
151
    using ::com::sun::star::awt::XWindow;
152
    using ::com::sun::star::sdbc::XResultSet;
153
    using ::com::sun::star::awt::XControlModel;
154
    using ::com::sun::star::awt::XTabControllerModel;
155
    using ::com::sun::star::beans::PropertyChangeEvent;
156
    using ::com::sun::star::form::validation::XValidatableFormComponent;
157
    using ::com::sun::star::form::XLoadable;
158
    using ::com::sun::star::form::XBoundControl;
159
    using ::com::sun::star::beans::XPropertyChangeListener;
160
    using ::com::sun::star::awt::TextEvent;
161
    using ::com::sun::star::form::XBoundComponent;
162
    using ::com::sun::star::awt::XCheckBox;
163
    using ::com::sun::star::awt::XComboBox;
164
    using ::com::sun::star::awt::XListBox;
165
    using ::com::sun::star::awt::ItemEvent;
166
    using ::com::sun::star::util::XModifyListener;
167
    using ::com::sun::star::form::XReset;
168
    using ::com::sun::star::frame::XDispatchProviderInterception;
169
    using ::com::sun::star::form::XGridControl;
170
    using ::com::sun::star::awt::XVclWindowPeer;
171
    using ::com::sun::star::form::validation::XValidator;
172
    using ::com::sun::star::awt::FocusEvent;
173
    using ::com::sun::star::sdb::SQLContext;
174
    using ::com::sun::star::container::XChild;
175
    using ::com::sun::star::form::TabulatorCycle_RECORDS;
176
    using ::com::sun::star::container::ContainerEvent;
177
    using ::com::sun::star::lang::DisposedException;
178
    using ::com::sun::star::lang::Locale;
179
    using ::com::sun::star::lang::NoSupportException;
180
    using ::com::sun::star::sdb::RowChangeEvent;
181
    using ::com::sun::star::frame::XStatusListener;
182
    using ::com::sun::star::frame::XDispatchProviderInterceptor;
183
    using ::com::sun::star::sdb::SQLErrorEvent;
184
    using ::com::sun::star::form::DatabaseParameterEvent;
185
    using ::com::sun::star::sdb::ParametersRequest;
186
    using ::com::sun::star::task::XInteractionRequest;
187
    using ::com::sun::star::util::URL;
188
    using ::com::sun::star::frame::FeatureStateEvent;
189
    using ::com::sun::star::form::runtime::XFormControllerContext;
190
    using ::com::sun::star::task::InteractionHandler;
191
    using ::com::sun::star::task::XInteractionHandler;
192
    using ::com::sun::star::form::runtime::FormOperations;
193
    using ::com::sun::star::container::XContainer;
194
    using ::com::sun::star::sdbc::SQLWarning;
195
196
    namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
197
    namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
198
    namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason;
199
    namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
200
    namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
201
202
namespace {
203
204
struct ColumnInfo
205
{
206
    // information about the column itself
207
    Reference< XColumn >    xColumn;
208
    sal_Int32               nNullable;
209
    bool                bAutoIncrement;
210
    bool                bReadOnly;
211
    OUString         sName;
212
213
    // information about the control(s) bound to this column
214
215
    /// the first control which is bound to the given column, and which requires input
216
    Reference< XControl >   xFirstControlWithInputRequired;
217
    /** the first grid control which contains a column which is bound to the given database column, and requires
218
        input
219
    */
220
    Reference< XGrid >      xFirstGridWithInputRequiredColumn;
221
    /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position
222
        of the grid column which is actually bound
223
    */
224
    sal_Int32               nRequiredGridColumn;
225
226
    ColumnInfo()
227
0
        :nNullable( ColumnValue::NULLABLE_UNKNOWN )
228
0
        ,bAutoIncrement( false )
229
0
        ,bReadOnly( false )
230
0
        ,nRequiredGridColumn( -1 )
231
0
    {
232
0
    }
233
};
234
235
}
236
237
class ColumnInfoCache
238
{
239
public:
240
    explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );
241
242
0
    size_t        getColumnCount() const { return m_aColumns.size(); }
243
    const ColumnInfo&   getColumnInfo( size_t _pos );
244
245
0
    bool    controlsInitialized() const { return m_bControlsInitialized; }
246
    void    initializeControls( const Sequence< Reference< XControl > >& _rControls );
247
    void    deinitializeControls();
248
249
private:
250
    typedef ::std::vector< ColumnInfo > ColumnInfos;
251
    ColumnInfos                         m_aColumns;
252
    bool                                m_bControlsInitialized;
253
};
254
255
256
ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
257
0
    :m_bControlsInitialized( false )
258
0
{
259
0
    try
260
0
    {
261
0
        m_aColumns.clear();
262
263
0
        Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
264
0
        sal_Int32 nColumnCount = xColumns->getCount();
265
0
        m_aColumns.reserve( nColumnCount );
266
267
0
        Reference< XPropertySet >   xColumnProps;
268
0
        for ( sal_Int32 i = 0; i < nColumnCount; ++i )
269
0
        {
270
0
            ColumnInfo aColInfo;
271
0
            aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );
272
273
0
            xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
274
0
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
275
0
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
276
0
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
277
0
            OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );
278
279
0
            m_aColumns.push_back( aColInfo );
280
0
        }
281
0
    }
282
0
    catch( const Exception& )
283
0
    {
284
0
        DBG_UNHANDLED_EXCEPTION("svx");
285
0
    }
286
0
}
287
288
289
namespace
290
{
291
    bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
292
0
    {
293
0
        Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
294
0
        return ( xNormBoundField == _rxNormDBField );
295
0
    }
296
297
    bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
298
0
    {
299
0
        bool bInputRequired = false;
300
0
        OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
301
0
        return bInputRequired;
302
0
    }
303
304
    void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
305
0
    {
306
0
        _rColInfo.xFirstControlWithInputRequired.clear();
307
0
        _rColInfo.xFirstGridWithInputRequiredColumn.clear();
308
0
        _rColInfo.nRequiredGridColumn = -1;
309
0
    }
310
}
311
312
313
void ColumnInfoCache::deinitializeControls()
314
0
{
315
0
    for (auto& rCol : m_aColumns)
316
0
    {
317
0
        lcl_resetColumnControlInfo( rCol );
318
0
    }
319
0
    m_bControlsInitialized = false;
320
0
}
321
322
323
void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
324
0
{
325
0
    try
326
0
    {
327
        // for every of our known columns, find the controls which are bound to this column
328
0
        for (auto& rCol : m_aColumns)
329
0
        {
330
0
            OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
331
0
                && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );
332
333
0
            lcl_resetColumnControlInfo( rCol );
334
335
0
            Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );
336
337
0
            const Reference< XControl >* pControl( _rControls.getConstArray() );
338
0
            const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
339
0
            for ( ; pControl != pControlEnd; ++pControl )
340
0
            {
341
0
                if ( !pControl->is() )
342
0
                    continue;
343
344
0
                Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
345
0
                Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
346
347
                // special handling for grid controls
348
0
                Reference< XGrid > xGrid( *pControl, UNO_QUERY );
349
0
                if ( xGrid.is() )
350
0
                {
351
0
                    Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
352
0
                    sal_Int32 gridColCount = xGridColAccess->getCount();
353
0
                    sal_Int32 gridCol = 0;
354
0
                    for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
355
0
                    {
356
0
                        Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );
357
358
0
                        if  (   !lcl_isBoundTo( xGridColumnModel, xNormColumn )
359
0
                            ||  !lcl_isInputRequired( xGridColumnModel )
360
0
                            )
361
0
                            continue;   // with next grid column
362
363
0
                        break;
364
0
                    }
365
366
0
                    if ( gridCol < gridColCount )
367
0
                    {
368
                        // found a grid column which is bound to the given
369
0
                        rCol.xFirstGridWithInputRequiredColumn = std::move(xGrid);
370
0
                        rCol.nRequiredGridColumn = gridCol;
371
0
                        break;
372
0
                    }
373
374
0
                    continue;   // with next control
375
0
                }
376
377
0
                if  (   !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
378
0
                    ||  !lcl_isBoundTo( xModel, xNormColumn )
379
0
                    ||  !lcl_isInputRequired( xModel )
380
0
                    )
381
0
                    continue;   // with next control
382
383
0
                break;
384
0
            }
385
386
0
            if ( pControl == pControlEnd )
387
                // did not find a control which is bound to this particular column, and for which the input is required
388
0
                continue;   // with next DB column
389
390
0
            rCol.xFirstControlWithInputRequired = *pControl;
391
0
        }
392
0
    }
393
0
    catch( const Exception& )
394
0
    {
395
0
        DBG_UNHANDLED_EXCEPTION("svx");
396
0
    }
397
398
0
    m_bControlsInitialized = true;
399
0
}
400
401
402
const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
403
0
{
404
0
    if ( _pos >= m_aColumns.size() )
405
0
        throw IndexOutOfBoundsException();
406
407
0
    return m_aColumns[ _pos ];
408
0
}
409
410
namespace {
411
412
class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
413
{
414
    Sequence< PropertyValue >       m_aValues;
415
416
public:
417
0
    OParameterContinuation() { }
418
419
0
    const Sequence< PropertyValue >&   getValues() const { return m_aValues; }
420
421
// XInteractionSupplyParameters
422
    virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override;
423
};
424
425
}
426
427
void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues )
428
0
{
429
0
    m_aValues = _rValues;
430
0
}
431
432
433
// FmXAutoControl
434
435
struct FmFieldInfo
436
{
437
    OUString       aFieldName;
438
    Reference< XPropertySet >   xField;
439
    Reference< XTextComponent >  xText;
440
441
    FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >& _xText)
442
0
        :xField(_xField)
443
0
        ,xText(_xText)
444
0
    {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;}
445
};
446
447
namespace {
448
449
class FmXAutoControl: public UnoControl
450
451
{
452
public:
453
    FmXAutoControl()
454
0
    {
455
0
    }
456
457
0
    virtual OUString GetComponentServiceName() const override {return u"Edit"_ustr;}
458
    virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer >  & rParentPeer ) override;
459
460
protected:
461
    virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
462
};
463
464
}
465
466
void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer >  & rParentPeer )
467
0
{
468
0
    UnoControl::createPeer( rxToolkit, rParentPeer );
469
470
0
    Reference< XTextComponent >  xText(getPeer() , UNO_QUERY);
471
0
    if (xText.is())
472
0
    {
473
0
        xText->setText(SvxResId(RID_STR_AUTOFIELD));
474
0
        xText->setEditable(false);
475
0
    }
476
0
}
477
478
479
void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
480
0
{
481
    // these properties are ignored
482
0
    if (rPropName == FM_PROP_TEXT)
483
0
        return;
484
485
0
    UnoControl::ImplSetPeerProperty( rPropName, rVal );
486
0
}
487
488
489
IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
490
0
{
491
0
    activateTabOrder();
492
0
}
493
494
namespace {
495
496
struct UpdateAllListeners
497
{
498
    bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
499
0
    {
500
0
        static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
501
        // the return is a dummy only so we can use this struct in a lambda expression
502
0
        return true;
503
0
    }
504
};
505
506
}
507
508
IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
509
0
{
510
0
    ::osl::MutexGuard aGuard( m_aMutex );
511
0
    for (const auto& rFeature : m_aInvalidFeatures)
512
0
    {
513
0
        DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
514
0
        if ( aDispatcherPos != m_aFeatureDispatchers.end() )
515
0
        {
516
            // TODO: for the real and actual listener notifications, we should release
517
            // our mutex
518
0
            UpdateAllListeners( )( aDispatcherPos->second );
519
0
        }
520
0
    }
521
0
}
522
523
FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
524
0
                  :FormController_BASE( m_aMutex )
525
0
                  ,OPropertySetHelper( FormController_BASE::rBHelper )
526
0
                  ,OSQLParserClient( _rxORB )
527
0
                  ,m_xComponentContext( _rxORB )
528
0
                  ,m_aActivateListeners(m_aMutex)
529
0
                  ,m_aModifyListeners(m_aMutex)
530
0
                  ,m_aErrorListeners(m_aMutex)
531
0
                  ,m_aDeleteListeners(m_aMutex)
532
0
                  ,m_aRowSetApproveListeners(m_aMutex)
533
0
                  ,m_aParameterListeners(m_aMutex)
534
0
                  ,m_aFilterListeners(m_aMutex)
535
0
                  ,m_aTabActivationIdle("svx FormController m_aTabActivationIdle")
536
0
                  ,m_aFeatureInvalidationTimer("svx FormController m_aFeatureInvalidationTimer")
537
0
                  ,m_aMode( u"DataMode"_ustr )
538
0
                  ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
539
0
                  ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
540
0
                  ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
541
0
                  ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
542
0
                  ,m_nCurrentFilterPosition(-1)
543
0
                  ,m_bCurrentRecordModified(false)
544
0
                  ,m_bCurrentRecordNew(false)
545
0
                  ,m_bLocked(false)
546
0
                  ,m_bDBConnection(false)
547
0
                  ,m_bCycle(false)
548
0
                  ,m_bCanInsert(false)
549
0
                  ,m_bCanUpdate(false)
550
0
                  ,m_bCommitLock(false)
551
0
                  ,m_bModified(false)
552
0
                  ,m_bControlsSorted(false)
553
0
                  ,m_bFiltering(false)
554
0
                  ,m_bAttachEvents(true)
555
0
                  ,m_bDetachEvents(true)
556
0
                  ,m_bAttemptedHandlerCreation( false )
557
0
                  ,m_bSuspendFilterTextListening( false )
558
0
{
559
560
0
    osl_atomic_increment(&m_refCount);
561
0
    {
562
0
        m_xTabController = TabController::create( m_xComponentContext );
563
0
        m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
564
0
        m_xAggregate->setDelegator( *this );
565
0
    }
566
0
    osl_atomic_decrement(&m_refCount);
567
568
0
    m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
569
0
    m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );
570
571
0
    m_aFeatureInvalidationTimer.SetTimeout( 200 );
572
0
    m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
573
0
}
574
575
576
FormController::~FormController()
577
0
{
578
0
    {
579
0
        ::osl::MutexGuard aGuard( m_aMutex );
580
581
0
        m_aLoadEvent.CancelPendingCall();
582
0
        m_aToggleEvent.CancelPendingCall();
583
0
        m_aActivationEvent.CancelPendingCall();
584
0
        m_aDeactivationEvent.CancelPendingCall();
585
586
0
        if ( m_aTabActivationIdle.IsActive() )
587
0
            m_aTabActivationIdle.Stop();
588
0
    }
589
590
0
    if ( m_aFeatureInvalidationTimer.IsActive() )
591
0
        m_aFeatureInvalidationTimer.Stop();
592
593
0
    disposeAllFeaturesAndDispatchers();
594
595
0
    if ( m_xFormOperations.is() )
596
0
        m_xFormOperations->dispose();
597
0
    m_xFormOperations.clear();
598
599
    // release of aggregation
600
0
    if ( m_xAggregate.is() )
601
0
    {
602
0
        m_xAggregate->setDelegator( nullptr );
603
0
        m_xAggregate.clear();
604
0
    }
605
0
}
606
607
608
void SAL_CALL FormController::acquire() noexcept
609
0
{
610
0
    FormController_BASE::acquire();
611
0
}
612
613
614
void SAL_CALL FormController::release() noexcept
615
0
{
616
0
    FormController_BASE::release();
617
0
}
618
619
620
Any SAL_CALL FormController::queryInterface( const Type& _rType )
621
0
{
622
0
    Any aRet = FormController_BASE::queryInterface( _rType );
623
0
    if ( !aRet.hasValue() )
624
0
        aRet = OPropertySetHelper::queryInterface( _rType );
625
0
    if ( !aRet.hasValue() )
626
0
        aRet = m_xAggregate->queryAggregation( _rType );
627
0
    return aRet;
628
0
}
629
630
631
Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId()
632
0
{
633
0
    return css::uno::Sequence<sal_Int8>();
634
0
}
635
636
Sequence< Type > SAL_CALL FormController::getTypes(  )
637
0
{
638
0
    return comphelper::concatSequences(
639
0
        FormController_BASE::getTypes(),
640
0
        ::cppu::OPropertySetHelper::getTypes()
641
0
    );
642
0
}
643
644
// XServiceInfo
645
sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName)
646
0
{
647
0
    return cppu::supportsService(this, ServiceName);
648
0
}
649
650
OUString SAL_CALL FormController::getImplementationName()
651
0
{
652
0
    return u"org.openoffice.comp.svx.FormController"_ustr;
653
0
}
654
655
Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
656
0
{
657
    // service names which are supported only, but cannot be used to created an
658
    // instance at a service factory
659
0
    static constexpr OUString aNonCreatableServiceNames[] { u"com.sun.star.form.FormControllerDispatcher"_ustr };
660
661
    // services which can be used to created an instance at a service factory
662
0
    Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
663
0
    return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
664
0
}
665
666
667
sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
668
0
{
669
0
    return true;
670
0
}
671
672
673
void SAL_CALL FormController::resetted(const EventObject& rEvent)
674
0
{
675
0
    ::osl::MutexGuard aGuard(m_aMutex);
676
0
    if (getCurrentControl().is() &&  (getCurrentControl()->getModel() == rEvent.Source))
677
0
        m_bModified = false;
678
0
}
679
680
681
Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
682
0
{
683
0
    static Sequence< OUString> const aServices
684
0
    {
685
0
        u"com.sun.star.form.runtime.FormController"_ustr,
686
0
        u"com.sun.star.awt.control.TabController"_ustr
687
0
    };
688
0
    return aServices;
689
0
}
690
691
692
namespace
693
{
694
    struct ResetComponentText
695
    {
696
        void operator()( const Reference< XTextComponent >& _rxText )
697
0
        {
698
0
            _rxText->setText( OUString() );
699
0
        }
700
    };
701
702
    struct RemoveComponentTextListener
703
    {
704
        explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
705
0
            :m_xListener( _rxListener )
706
0
        {
707
0
        }
708
709
        void operator()( const Reference< XTextComponent >& _rxText )
710
0
        {
711
0
            _rxText->removeTextListener( m_xListener );
712
0
        }
713
714
    private:
715
        Reference< XTextListener >  m_xListener;
716
    };
717
}
718
719
720
void FormController::impl_setTextOnAllFilter_throw()
721
0
{
722
0
    m_bSuspendFilterTextListening = true;
723
0
    ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );
724
725
    // reset the text for all controls
726
0
    ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );
727
728
0
    if ( m_aFilterRows.empty() )
729
        // nothing to do anymore
730
0
        return;
731
732
0
    if ( m_nCurrentFilterPosition < 0 )
733
0
        return;
734
735
    // set the text for all filters
736
0
    OSL_ENSURE( m_aFilterRows.size() > o3tl::make_unsigned(m_nCurrentFilterPosition),
737
0
        "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );
738
739
0
    if ( o3tl::make_unsigned(m_nCurrentFilterPosition) < m_aFilterRows.size() )
740
0
    {
741
0
        FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
742
0
        for (const auto& rEntry : rRow)
743
0
        {
744
0
            rEntry.first->setText( rEntry.second );
745
0
        }
746
0
    }
747
0
}
748
// OPropertySetHelper
749
750
sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/,
751
                                            sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
752
0
{
753
0
    return false;
754
0
}
755
756
757
void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
758
0
{
759
0
}
760
761
762
void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
763
0
{
764
0
    switch (nHandle)
765
0
    {
766
0
        case FM_ATTR_FILTER:
767
0
        {
768
0
            OUStringBuffer aFilter;
769
0
            Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
770
0
            if (xConnection.is())
771
0
            {
772
0
                Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
773
0
                Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
774
0
                xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
775
776
                // now add the filter rows
777
0
                try
778
0
                {
779
0
                    for (const FmFilterRow& rRow : m_aFilterRows)
780
0
                    {
781
0
                        if ( rRow.empty() )
782
0
                            continue;
783
784
0
                        OUStringBuffer aRowFilter;
785
0
                        for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
786
0
                        {
787
                            // get the field of the controls map
788
0
                            Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
789
0
                            Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
790
0
                            Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
791
792
0
                            OUString sFilterValue( condition->second );
793
794
0
                            OUString sErrorMsg;
795
0
                            const std::unique_ptr< OSQLParseNode > pParseNode =
796
0
                                predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
797
0
                            OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
798
0
                            if ( pParseNode != nullptr )
799
0
                            {
800
0
                                OUString sCriteria;
801
                                // don't use a parse context here, we need it unlocalized
802
0
                                pParseNode->parseNodeToStr( sCriteria, xConnection );
803
0
                                if ( condition != rRow.begin() )
804
0
                                    aRowFilter.append( " AND " );
805
0
                                aRowFilter.append( sCriteria );
806
0
                            }
807
0
                        }
808
0
                        if ( !aRowFilter.isEmpty() )
809
0
                        {
810
0
                            if ( !aFilter.isEmpty() )
811
0
                                aFilter.append( " OR " );
812
813
0
                            aFilter.append( "( " + aRowFilter + " )" );
814
0
                        }
815
0
                    }
816
0
                }
817
0
                catch( const Exception& )
818
0
                {
819
0
                    DBG_UNHANDLED_EXCEPTION("svx");
820
0
                    aFilter.setLength(0);
821
0
                }
822
0
            }
823
0
            rValue <<= aFilter.makeStringAndClear();
824
0
        }
825
0
        break;
826
827
0
        case FM_ATTR_FORM_OPERATIONS:
828
0
            rValue <<= m_xFormOperations;
829
0
            break;
830
0
    }
831
0
}
832
833
834
Reference< XPropertySetInfo >  FormController::getPropertySetInfo()
835
0
{
836
0
    static Reference< XPropertySetInfo >  xInfo( createPropertySetInfo( getInfoHelper() ) );
837
0
    return xInfo;
838
0
}
839
840
841
void FormController::fillProperties(
842
        Sequence< Property >& /* [out] */ _rProps,
843
        Sequence< Property >& /* [out] */ /*_rAggregateProps*/
844
        ) const
845
0
{
846
0
    _rProps.realloc(2);
847
0
    sal_Int32 nPos = 0;
848
0
    Property* pDesc = _rProps.getArray();
849
850
0
    pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
851
0
                             cppu::UnoType<OUString>::get(),
852
0
                             PropertyAttribute::READONLY);
853
0
    pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS,
854
0
                             cppu::UnoType<XFormOperations>::get(),
855
0
                             PropertyAttribute::READONLY);
856
0
}
857
858
859
::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
860
0
{
861
0
    return *getArrayHelper();
862
0
}
863
864
// XFilterController
865
866
void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
867
0
{
868
0
    m_aFilterListeners.addInterface( Listener );
869
0
}
870
871
872
void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
873
0
{
874
0
    m_aFilterListeners.removeInterface( Listener );
875
0
}
876
877
878
::sal_Int32 SAL_CALL FormController::getFilterComponents()
879
0
{
880
0
    ::osl::MutexGuard aGuard( m_aMutex );
881
0
    impl_checkDisposed_throw();
882
883
0
    return m_aFilterComponents.size();
884
0
}
885
886
887
::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
888
0
{
889
0
    ::osl::MutexGuard aGuard( m_aMutex );
890
0
    impl_checkDisposed_throw();
891
892
0
    return m_aFilterRows.size();
893
0
}
894
895
896
void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
897
0
{
898
0
    ::osl::MutexGuard aGuard( m_aMutex );
899
0
    impl_checkDisposed_throw();
900
901
0
    if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
902
0
        throw IndexOutOfBoundsException( OUString(), *this );
903
904
0
    Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
905
0
    xText->setText( PredicateExpression );
906
907
0
    FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
908
0
    if ( !PredicateExpression.isEmpty() )
909
0
        rFilterRow[ xText ] = PredicateExpression;
910
0
    else
911
0
        rFilterRow.erase( xText );
912
0
}
913
914
915
Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
916
0
{
917
0
    ::osl::MutexGuard aGuard( m_aMutex );
918
0
    impl_checkDisposed_throw();
919
920
0
    if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
921
0
        throw IndexOutOfBoundsException( OUString(), *this );
922
923
0
    return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
924
0
}
925
926
927
Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
928
0
{
929
0
    ::osl::MutexGuard aGuard( m_aMutex );
930
0
    impl_checkDisposed_throw();
931
932
0
    Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
933
0
    auto aExpressionsRange = asNonConstRange(aExpressions);
934
0
    sal_Int32 termIndex = 0;
935
0
    for (const FmFilterRow& rRow : m_aFilterRows)
936
0
    {
937
0
        Sequence< OUString > aConjunction( m_aFilterComponents.size() );
938
0
        auto aConjunctionRange = asNonConstRange(aConjunction);
939
0
        sal_Int32 componentIndex = 0;
940
0
        for (const auto& rComp : m_aFilterComponents)
941
0
        {
942
0
            FmFilterRow::const_iterator predicate = rRow.find( rComp );
943
0
            if ( predicate != rRow.end() )
944
0
                aConjunctionRange[ componentIndex ] = predicate->second;
945
0
            ++componentIndex;
946
0
        }
947
948
0
        aExpressionsRange[ termIndex ] = std::move(aConjunction);
949
0
        ++termIndex;
950
0
    }
951
952
0
    return aExpressions;
953
0
}
954
955
956
void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
957
0
{
958
    // SYNCHRONIZED -->
959
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
960
0
    impl_checkDisposed_throw();
961
962
0
    if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
963
0
        throw IndexOutOfBoundsException( OUString(), *this );
964
965
    // if the to-be-deleted row is our current row, we need to shift
966
0
    if ( Term == m_nCurrentFilterPosition )
967
0
    {
968
0
        if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
969
0
            ++m_nCurrentFilterPosition;
970
0
        else
971
0
            --m_nCurrentFilterPosition;
972
0
    }
973
974
0
    FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
975
0
    m_aFilterRows.erase( pos );
976
977
    // adjust m_nCurrentFilterPosition if the removed row preceded it
978
0
    if ( Term < m_nCurrentFilterPosition )
979
0
        --m_nCurrentFilterPosition;
980
981
0
    SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
982
0
        "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" );
983
984
    // update the texts in the filter controls
985
0
    impl_setTextOnAllFilter_throw();
986
987
0
    FilterEvent aEvent;
988
0
    aEvent.Source = *this;
989
0
    aEvent.DisjunctiveTerm = Term;
990
0
    aGuard.clear();
991
    // <-- SYNCHRONIZED
992
993
0
    m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
994
0
}
995
996
997
void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
998
0
{
999
    // SYNCHRONIZED -->
1000
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
1001
0
    impl_checkDisposed_throw();
1002
1003
0
    impl_appendEmptyFilterRow( aGuard );
1004
    // <-- SYNCHRONIZED
1005
0
}
1006
1007
1008
::sal_Int32 SAL_CALL FormController::getActiveTerm()
1009
0
{
1010
0
    ::osl::MutexGuard aGuard( m_aMutex );
1011
0
    impl_checkDisposed_throw();
1012
1013
0
    return m_nCurrentFilterPosition;
1014
0
}
1015
1016
1017
void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
1018
0
{
1019
0
    ::osl::MutexGuard aGuard( m_aMutex );
1020
0
    impl_checkDisposed_throw();
1021
1022
0
    if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
1023
0
        throw IndexOutOfBoundsException( OUString(), *this );
1024
1025
0
    if ( ActiveTerm == getActiveTerm() )
1026
0
        return;
1027
1028
0
    m_nCurrentFilterPosition = ActiveTerm;
1029
0
    impl_setTextOnAllFilter_throw();
1030
0
}
1031
1032
// XElementAccess
1033
1034
sal_Bool SAL_CALL FormController::hasElements()
1035
0
{
1036
0
    ::osl::MutexGuard aGuard( m_aMutex );
1037
0
    return !m_aChildren.empty();
1038
0
}
1039
1040
1041
Type SAL_CALL  FormController::getElementType()
1042
0
{
1043
0
    return cppu::UnoType<XFormController>::get();
1044
1045
0
}
1046
1047
// XEnumerationAccess
1048
1049
Reference< XEnumeration > SAL_CALL  FormController::createEnumeration()
1050
0
{
1051
0
    ::osl::MutexGuard aGuard( m_aMutex );
1052
0
    return new ::comphelper::OEnumerationByIndex(this);
1053
0
}
1054
1055
// XIndexAccess
1056
1057
sal_Int32 SAL_CALL FormController::getCount()
1058
0
{
1059
0
    ::osl::MutexGuard aGuard( m_aMutex );
1060
0
    return m_aChildren.size();
1061
0
}
1062
1063
1064
Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
1065
0
{
1066
0
    ::osl::MutexGuard aGuard( m_aMutex );
1067
0
    if (Index < 0 ||
1068
0
        o3tl::make_unsigned(Index) >= m_aChildren.size())
1069
0
        throw IndexOutOfBoundsException();
1070
1071
0
    return Any( m_aChildren[ Index ] );
1072
0
}
1073
1074
//  EventListener
1075
1076
void SAL_CALL FormController::disposing(const EventObject& e)
1077
0
{
1078
    // has the container been disposed
1079
0
    ::osl::MutexGuard aGuard( m_aMutex );
1080
0
    Reference< XControlContainer >  xContainer(e.Source, UNO_QUERY);
1081
0
    if (xContainer.is())
1082
0
    {
1083
0
        setContainer(Reference< XControlContainer > ());
1084
0
    }
1085
0
    else
1086
0
    {
1087
        // has a control been disposed
1088
0
        Reference< XControl >  xControl(e.Source, UNO_QUERY);
1089
0
        if (xControl.is())
1090
0
        {
1091
0
            if (getContainer().is())
1092
0
                removeControl(xControl);
1093
0
        }
1094
0
    }
1095
0
}
1096
1097
// OComponentHelper
1098
1099
void FormController::disposeAllFeaturesAndDispatchers()
1100
0
{
1101
0
    for (auto& rDispatcher : m_aFeatureDispatchers)
1102
0
    {
1103
0
        try
1104
0
        {
1105
0
            ::comphelper::disposeComponent( rDispatcher.second );
1106
0
        }
1107
0
        catch( const Exception& )
1108
0
        {
1109
0
            DBG_UNHANDLED_EXCEPTION("svx");
1110
0
        }
1111
0
    }
1112
0
    m_aFeatureDispatchers.clear();
1113
0
}
1114
1115
1116
void FormController::disposing()
1117
0
{
1118
0
    EventObject aEvt( *this );
1119
1120
    // if we're still active, simulate a "deactivated" event
1121
0
    if ( m_xActiveControl.is() )
1122
0
        m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );
1123
1124
    // notify all our listeners
1125
0
    m_aActivateListeners.disposeAndClear(aEvt);
1126
0
    m_aModifyListeners.disposeAndClear(aEvt);
1127
0
    m_aErrorListeners.disposeAndClear(aEvt);
1128
0
    m_aDeleteListeners.disposeAndClear(aEvt);
1129
0
    m_aRowSetApproveListeners.disposeAndClear(aEvt);
1130
0
    m_aParameterListeners.disposeAndClear(aEvt);
1131
0
    m_aFilterListeners.disposeAndClear(aEvt);
1132
1133
0
    removeBoundFieldListener();
1134
0
    stopFiltering();
1135
1136
0
    m_aControlBorderManager.restoreAll();
1137
1138
0
    m_aFilterRows.clear();
1139
1140
0
    ::osl::MutexGuard aGuard( m_aMutex );
1141
0
    m_xActiveControl = nullptr;
1142
0
    implSetCurrentControl( nullptr );
1143
1144
    // clean up our children
1145
0
    for (const auto& rpChild : m_aChildren)
1146
0
    {
1147
        // search the position of the model within the form
1148
0
        Reference< XFormComponent >  xForm(rpChild->getModel(), UNO_QUERY);
1149
0
        sal_uInt32 nPos = m_xModelAsIndex->getCount();
1150
0
        Reference< XFormComponent > xTemp;
1151
0
        for( ; nPos; )
1152
0
        {
1153
1154
0
            m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
1155
0
            if ( xForm.get() == xTemp.get() )
1156
0
            {
1157
0
                Reference< XInterface > xIfc( rpChild, UNO_QUERY );
1158
0
                m_xModelAsManager->detach( nPos, xIfc );
1159
0
                break;
1160
0
            }
1161
0
        }
1162
1163
0
        Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
1164
0
    }
1165
0
    m_aChildren.clear();
1166
1167
0
    disposeAllFeaturesAndDispatchers();
1168
1169
0
    if ( m_xFormOperations.is() )
1170
0
        m_xFormOperations->dispose();
1171
0
    m_xFormOperations.clear();
1172
1173
0
    if (m_bDBConnection)
1174
0
        unload();
1175
1176
0
    setContainer( nullptr );
1177
0
    setModel( nullptr );
1178
0
    setParent( nullptr );
1179
1180
0
    ::comphelper::disposeComponent( m_xComposer );
1181
1182
0
    m_bDBConnection = false;
1183
0
}
1184
1185
1186
namespace
1187
{
1188
    bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
1189
0
    {
1190
0
        bool bDoUse = false;
1191
0
        if ( !( _rDynamicColorProp >>= bDoUse ) )
1192
0
        {
1193
0
            DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
1194
0
            return ControlLayouter::useDynamicBorderColor( eDocType );
1195
0
        }
1196
0
        return bDoUse;
1197
0
    }
1198
}
1199
1200
1201
void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
1202
0
{
1203
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1204
0
    if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
1205
0
    {
1206
0
        Reference<XPropertySet> xOldBound;
1207
0
        evt.OldValue >>= xOldBound;
1208
0
        if ( !xOldBound.is() && evt.NewValue.hasValue() )
1209
0
        {
1210
0
            Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
1211
0
            Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
1212
0
            if ( xControl.is() )
1213
0
            {
1214
0
                startControlModifyListening( xControl );
1215
0
                Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
1216
0
                if ( xProp.is() )
1217
0
                    xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
1218
0
            }
1219
0
        }
1220
0
    }
1221
0
    else
1222
0
    {
1223
0
        bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
1224
0
        bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
1225
0
        if (bModifiedChanged || bNewChanged)
1226
0
        {
1227
0
            ::osl::MutexGuard aGuard( m_aMutex );
1228
0
            if (bModifiedChanged)
1229
0
                m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
1230
0
            else
1231
0
                m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);
1232
1233
            // toggle the locking
1234
0
            if (m_bLocked != determineLockState())
1235
0
            {
1236
0
                m_bLocked = !m_bLocked;
1237
0
                setLocks();
1238
0
                if (isListeningForChanges())
1239
0
                    startListening();
1240
0
                else
1241
0
                    stopListening();
1242
0
            }
1243
1244
0
            if ( bNewChanged )
1245
0
                m_aToggleEvent.Call();
1246
1247
0
            if (!m_bCurrentRecordModified)
1248
0
                m_bModified = false;
1249
0
        }
1250
0
        else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
1251
0
        {
1252
0
            bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
1253
0
            if ( bEnable )
1254
0
            {
1255
0
                m_aControlBorderManager.enableDynamicBorderColor();
1256
0
                if ( m_xActiveControl.is() )
1257
0
                    m_aControlBorderManager.focusGained( m_xActiveControl );
1258
0
            }
1259
0
            else
1260
0
            {
1261
0
                m_aControlBorderManager.disableDynamicBorderColor();
1262
0
            }
1263
0
        }
1264
0
    }
1265
0
}
1266
1267
1268
bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl )
1269
0
{
1270
0
    bool bSuccess = false;
1271
0
    try
1272
0
    {
1273
0
        Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
1274
0
        DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
1275
0
        if ( xContainer.is() )
1276
0
        {
1277
            // look up the ID of _rxExistentControl
1278
0
            const Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
1279
0
            const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
1280
0
                [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
1281
0
                    Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
1282
0
                    return xCheck == _rxExistentControl;
1283
0
                });
1284
0
            DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
1285
0
            if ( pIdentifiers != aIdentifiers.end() )
1286
0
            {
1287
0
                bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
1288
0
                bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );
1289
1290
0
                if ( bReplacedWasActive )
1291
0
                {
1292
0
                    m_xActiveControl = nullptr;
1293
0
                    implSetCurrentControl( nullptr );
1294
0
                }
1295
0
                else if ( bReplacedWasCurrent )
1296
0
                {
1297
0
                    implSetCurrentControl( _rxNewControl );
1298
0
                }
1299
1300
                // carry over the model
1301
0
                _rxNewControl->setModel( _rxExistentControl->getModel() );
1302
1303
0
                xContainer->replaceByIdentifer( *pIdentifiers, Any( _rxNewControl ) );
1304
0
                bSuccess = true;
1305
1306
0
                if ( bReplacedWasActive )
1307
0
                {
1308
0
                    Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
1309
0
                    if ( xControlWindow.is() )
1310
0
                        xControlWindow->setFocus();
1311
0
                }
1312
0
            }
1313
0
        }
1314
0
    }
1315
0
    catch( const Exception& )
1316
0
    {
1317
0
        DBG_UNHANDLED_EXCEPTION("svx");
1318
0
    }
1319
1320
0
    Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
1321
0
    ::comphelper::disposeComponent( xDisposeIt );
1322
0
    return bSuccess;
1323
0
}
1324
1325
1326
void FormController::toggleAutoFields(bool bAutoFields)
1327
0
{
1328
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1329
1330
1331
0
    Sequence< Reference< XControl > > aControlsCopy( m_aControls );
1332
0
    const Reference< XControl >* pControls = aControlsCopy.getConstArray();
1333
0
    sal_Int32 nControls = aControlsCopy.getLength();
1334
1335
0
    if (bAutoFields)
1336
0
    {
1337
        // as we don't want new controls to be attached to the scripting environment
1338
        // we change attach flags
1339
0
        m_bAttachEvents = false;
1340
0
        for (sal_Int32 i = nControls; i > 0;)
1341
0
        {
1342
0
            Reference< XControl > xControl = pControls[--i];
1343
0
            if (xControl.is())
1344
0
            {
1345
0
                Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
1346
0
                if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1347
0
                {
1348
                    // does the model use a bound field ?
1349
0
                    Reference< XPropertySet >  xField;
1350
0
                    xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1351
1352
                    // is it an autofield?
1353
0
                    if  (   xField.is()
1354
0
                        &&  ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1355
0
                        &&  ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
1356
0
                        )
1357
0
                    {
1358
0
                        replaceControl( xControl, new FmXAutoControl() );
1359
0
                    }
1360
0
                }
1361
0
            }
1362
0
        }
1363
0
        m_bAttachEvents = true;
1364
0
    }
1365
0
    else
1366
0
    {
1367
0
        m_bDetachEvents = false;
1368
0
        for (sal_Int32 i = nControls; i > 0;)
1369
0
        {
1370
0
            Reference< XControl > xControl = pControls[--i];
1371
0
            if (xControl.is())
1372
0
            {
1373
0
                Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
1374
0
                if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
1375
0
                {
1376
                    // does the model use a bound field ?
1377
0
                    Reference< XPropertySet >  xField;
1378
0
                    xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1379
1380
                    // is it an autofield?
1381
0
                    if  (   xField.is()
1382
0
                        &&  ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
1383
0
                        &&  ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
1384
0
                        )
1385
0
                    {
1386
0
                        OUString sServiceName;
1387
0
                        OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
1388
0
                        Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
1389
0
                        replaceControl( xControl, xNewControl );
1390
0
                    }
1391
0
                }
1392
0
            }
1393
0
        }
1394
0
        m_bDetachEvents = true;
1395
0
    }
1396
0
}
1397
1398
1399
IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
1400
0
{
1401
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1402
1403
0
    toggleAutoFields(m_bCurrentRecordNew);
1404
0
}
1405
1406
// XTextListener
1407
void SAL_CALL FormController::textChanged(const TextEvent& e)
1408
0
{
1409
    // SYNCHRONIZED -->
1410
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
1411
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1412
0
    if ( !m_bFiltering )
1413
0
    {
1414
0
        impl_onModify();
1415
0
        return;
1416
0
    }
1417
1418
0
    if ( m_bSuspendFilterTextListening )
1419
0
        return;
1420
1421
0
    Reference< XTextComponent >  xText(e.Source,UNO_QUERY);
1422
0
    OUString aText = xText->getText();
1423
1424
0
    if ( m_aFilterRows.empty() )
1425
0
        appendEmptyDisjunctiveTerm();
1426
1427
    // find the current row
1428
0
    if ( ( m_nCurrentFilterPosition < 0 ) || ( o3tl::make_unsigned(m_nCurrentFilterPosition) >= m_aFilterRows.size() )  )
1429
0
    {
1430
0
        OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
1431
0
        return;
1432
0
    }
1433
1434
0
    FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
1435
1436
    // do we have a new filter
1437
0
    if (!aText.isEmpty())
1438
0
        rRow[xText] = aText;
1439
0
    else
1440
0
    {
1441
        // do we have the control in the row
1442
0
        FmFilterRow::iterator iter = rRow.find(xText);
1443
        // erase the entry out of the row
1444
0
        if (iter != rRow.end())
1445
0
            rRow.erase(iter);
1446
0
    }
1447
1448
    // multiplex the event to our FilterControllerListeners
1449
0
    FilterEvent aEvent;
1450
0
    aEvent.Source = *this;
1451
0
    aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
1452
0
    aEvent.DisjunctiveTerm = getActiveTerm();
1453
0
    aEvent.PredicateExpression = aText;
1454
1455
0
    aGuard.clear();
1456
    // <-- SYNCHRONIZED
1457
1458
    // notify the changed filter expression
1459
0
    m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
1460
0
}
1461
1462
// XItemListener
1463
void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
1464
0
{
1465
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1466
0
    impl_onModify();
1467
0
}
1468
1469
// XModificationBroadcaster
1470
void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
1471
0
{
1472
0
    ::osl::MutexGuard aGuard( m_aMutex );
1473
0
    impl_checkDisposed_throw();
1474
0
    m_aModifyListeners.addInterface( l );
1475
0
}
1476
1477
void FormController::removeModifyListener(const Reference< XModifyListener > & l)
1478
0
{
1479
0
    ::osl::MutexGuard aGuard( m_aMutex );
1480
0
    impl_checkDisposed_throw();
1481
0
    m_aModifyListeners.removeInterface( l );
1482
0
}
1483
1484
// XModificationListener
1485
void FormController::modified( const EventObject& _rEvent )
1486
0
{
1487
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1488
1489
0
    try
1490
0
    {
1491
0
        if ( _rEvent.Source != m_xActiveControl )
1492
0
        {   // let this control grab the focus
1493
            // (this case may happen if somebody moves the scroll wheel of the mouse over a control
1494
            // which does not have the focus)
1495
            // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com
1496
1497
            // also, it happens when an image control gets a new image by double-clicking it
1498
            // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
1499
0
            Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
1500
0
            xControlWindow->setFocus();
1501
0
        }
1502
0
    }
1503
0
    catch( const Exception& )
1504
0
    {
1505
0
        DBG_UNHANDLED_EXCEPTION("svx");
1506
0
    }
1507
1508
0
    impl_onModify();
1509
0
}
1510
1511
void FormController::impl_checkDisposed_throw() const
1512
0
{
1513
0
    if ( impl_isDisposed_nofail() )
1514
0
        throw DisposedException( OUString(), *const_cast< FormController* >( this ) );
1515
0
}
1516
1517
void FormController::impl_onModify()
1518
0
{
1519
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1520
1521
0
    {
1522
0
        ::osl::MutexGuard aGuard( m_aMutex );
1523
0
        if ( !m_bModified )
1524
0
            m_bModified = true;
1525
0
    }
1526
1527
0
    EventObject aEvt(getXWeak());
1528
0
    m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
1529
0
}
1530
1531
void FormController::impl_addFilterRow( const FmFilterRow& _row )
1532
0
{
1533
0
    m_aFilterRows.push_back( _row );
1534
1535
0
    if ( m_aFilterRows.size() == 1 )
1536
0
    {   // that's the first row ever
1537
0
        OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
1538
0
        m_nCurrentFilterPosition = 0;
1539
0
    }
1540
0
}
1541
1542
void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
1543
0
{
1544
    // SYNCHRONIZED -->
1545
0
    impl_addFilterRow( FmFilterRow() );
1546
1547
    // notify the listeners
1548
0
    FilterEvent aEvent;
1549
0
    aEvent.Source = *this;
1550
0
    aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
1551
0
    _rClearBeforeNotify.clear();
1552
    // <-- SYNCHRONIZED
1553
0
    m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
1554
0
}
1555
1556
bool FormController::determineLockState() const
1557
0
{
1558
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1559
    // a.) in filter mode we are always locked
1560
    // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
1561
    // c.) if we are inserting everything is OK and we are not locked
1562
    // d.) if are not updatable or on invalid position
1563
0
    Reference< XResultSet >  xResultSet(m_xModelAsIndex, UNO_QUERY);
1564
0
    if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
1565
0
        return true;
1566
0
    else
1567
0
        return !(m_bCanInsert && m_bCurrentRecordNew)
1568
0
            && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
1569
0
}
1570
1571
//  FocusListener
1572
void FormController::focusGained(const FocusEvent& e)
1573
0
{
1574
    // SYNCHRONIZED -->
1575
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
1576
0
    impl_checkDisposed_throw();
1577
1578
0
    m_aControlBorderManager.focusGained( e.Source );
1579
1580
0
    Reference< XControl >  xControl(e.Source, UNO_QUERY);
1581
0
    if (m_bDBConnection)
1582
0
    {
1583
        // do we need to keep the locking of the commit
1584
        // we hold the lock as long as the control differs from the current
1585
        // otherwise we disabled the lock
1586
0
        m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
1587
0
        if (m_bCommitLock)
1588
0
            return;
1589
1590
        // when do we have to commit a value to form or a filter
1591
        // a.) if the current value is modified
1592
        // b.) there must be a current control
1593
        // c.) and it must be different from the new focus owning control or
1594
        // d.) the focus is moving around (so we have only one control)
1595
1596
0
        if  (   ( m_bModified || m_bFiltering )
1597
0
            &&  m_xCurrentControl.is()
1598
0
            &&  (   ( xControl.get() != m_xCurrentControl.get() )
1599
0
                ||  (   ( e.FocusFlags & FocusChangeReason::AROUND )
1600
0
                    &&  ( m_bCycle || m_bFiltering )
1601
0
                    )
1602
0
                )
1603
0
            )
1604
0
        {
1605
            // check the old control if the content is ok
1606
#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
1607
            Reference< XBoundControl >  xLockingTest(m_xCurrentControl, UNO_QUERY);
1608
            bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
1609
            assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
1610
            // normally, a locked control should not be modified, so probably my bModified must
1611
            // have been set from a different context, which I would not understand ...
1612
#endif
1613
0
            DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
1614
            // first the control ask if it supports the IFace
1615
0
            Reference< XBoundComponent >  xBound(m_xCurrentControl, UNO_QUERY);
1616
0
            if (!xBound.is() && m_xCurrentControl.is())
1617
0
                xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);
1618
1619
            // lock if we lose the focus during commit
1620
0
            m_bCommitLock = true;
1621
1622
            // commit unsuccessful, reset focus
1623
0
            if (xBound.is() && !xBound->commit())
1624
0
            {
1625
                // the commit failed and we don't commit again until the current control
1626
                // which couldn't be commit gains the focus again
1627
0
                Reference< XWindow >  xWindow(m_xCurrentControl, UNO_QUERY);
1628
0
                if (xWindow.is())
1629
0
                    xWindow->setFocus();
1630
0
                return;
1631
0
            }
1632
0
            else
1633
0
            {
1634
0
                m_bModified = false;
1635
0
                m_bCommitLock = false;
1636
0
            }
1637
0
        }
1638
1639
0
        if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
1640
0
        {
1641
0
            OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
1642
                // should have been created in setModel
1643
0
            try
1644
0
            {
1645
0
                if ( e.FocusFlags & FocusChangeReason::FORWARD )
1646
0
                {
1647
0
                    if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
1648
0
                        m_xFormOperations->execute( FormFeature::MoveToNext );
1649
0
                }
1650
0
                else // backward
1651
0
                {
1652
0
                    if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
1653
0
                        m_xFormOperations->execute( FormFeature::MoveToPrevious );
1654
0
                }
1655
0
            }
1656
0
            catch ( const Exception& )
1657
0
            {
1658
                // don't handle this any further. That's an ... admissible error.
1659
0
                DBG_UNHANDLED_EXCEPTION("svx");
1660
0
            }
1661
0
        }
1662
0
    }
1663
1664
    // still one and the same control
1665
0
    if  (   ( m_xActiveControl == xControl )
1666
0
        &&  ( xControl == m_xCurrentControl )
1667
0
        )
1668
0
    {
1669
0
        DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
1670
0
        return;
1671
0
    }
1672
1673
0
    bool bActivated = !m_xActiveControl.is() && xControl.is();
1674
1675
0
    m_xActiveControl  = xControl;
1676
1677
0
    implSetCurrentControl( xControl );
1678
0
    SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" );
1679
1680
0
    if ( bActivated )
1681
0
    {
1682
        // (asynchronously) call activation handlers
1683
0
        m_aActivationEvent.Call();
1684
1685
        // call modify listeners
1686
0
        if ( m_bModified )
1687
0
            m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
1688
0
    }
1689
1690
    // invalidate all features which depend on the currently focused control
1691
0
    if ( m_bDBConnection && !m_bFiltering )
1692
0
        implInvalidateCurrentControlDependentFeatures();
1693
1694
0
    if ( !m_xCurrentControl.is() )
1695
0
        return;
1696
1697
    // control gets focus, then possibly in the visible range
1698
0
    Reference< XFormControllerContext > xContext( m_xFormControllerContext );
1699
0
    Reference< XControl > xCurrentControl( m_xCurrentControl );
1700
0
    aGuard.clear();
1701
    // <-- SYNCHRONIZED
1702
1703
0
    if ( xContext.is() )
1704
0
        xContext->makeVisible( xCurrentControl );
1705
0
}
1706
1707
IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
1708
0
{
1709
0
    EventObject aEvent;
1710
0
    aEvent.Source = *this;
1711
0
    m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
1712
0
}
1713
1714
IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
1715
0
{
1716
0
    EventObject aEvent;
1717
0
    aEvent.Source = *this;
1718
0
    m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
1719
0
}
1720
1721
void FormController::focusLost(const FocusEvent& e)
1722
0
{
1723
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1724
1725
0
    m_aControlBorderManager.focusLost( e.Source );
1726
1727
0
    Reference< XWindowPeer >  xNext(e.NextFocus, UNO_QUERY);
1728
    // if focus hasn't passed to some other window, e.g. focus in a welded item, don't deactivate
1729
0
    if (!xNext)
1730
0
        return;
1731
0
    Reference< XControl >  xNextControl = isInList(xNext);
1732
0
    if (!xNextControl.is())
1733
0
    {
1734
0
        m_xActiveControl = nullptr;
1735
0
        m_aDeactivationEvent.Call();
1736
0
    }
1737
0
}
1738
1739
void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
1740
0
{
1741
    // not interested in
1742
0
}
1743
1744
void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
1745
0
{
1746
    // not interested in
1747
0
}
1748
1749
void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
1750
0
{
1751
0
    m_aControlBorderManager.mouseEntered( _rEvent.Source );
1752
0
}
1753
1754
void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
1755
0
{
1756
0
    m_aControlBorderManager.mouseExited( _rEvent.Source );
1757
0
}
1758
1759
void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
1760
0
{
1761
0
    Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) );
1762
0
    Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );
1763
1764
0
    OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );
1765
1766
0
    if ( xControl.is() && xValidatable.is() )
1767
0
        m_aControlBorderManager.validityChanged( xControl, xValidatable );
1768
0
}
1769
1770
1771
void FormController::setModel(const Reference< XTabControllerModel > & Model)
1772
0
{
1773
0
    ::osl::MutexGuard aGuard( m_aMutex );
1774
0
    impl_checkDisposed_throw();
1775
1776
0
    DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");
1777
1778
0
    try
1779
0
    {
1780
        // disconnect from the old model
1781
0
        if (m_xModelAsIndex.is())
1782
0
        {
1783
0
            if (m_bDBConnection)
1784
0
            {
1785
                // we are currently working on the model
1786
0
                EventObject aEvt(m_xModelAsIndex);
1787
0
                unloaded(aEvt);
1788
0
            }
1789
1790
0
            Reference< XLoadable >  xForm(m_xModelAsIndex, UNO_QUERY);
1791
0
            if (xForm.is())
1792
0
                xForm->removeLoadListener(this);
1793
1794
0
            Reference< XSQLErrorBroadcaster >  xBroadcaster(m_xModelAsIndex, UNO_QUERY);
1795
0
            if (xBroadcaster.is())
1796
0
                xBroadcaster->removeSQLErrorListener(this);
1797
1798
0
            Reference< XDatabaseParameterBroadcaster >  xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
1799
0
            if (xParamBroadcaster.is())
1800
0
                xParamBroadcaster->removeParameterListener(this);
1801
1802
0
        }
1803
1804
0
        disposeAllFeaturesAndDispatchers();
1805
1806
0
        if ( m_xFormOperations.is() )
1807
0
            m_xFormOperations->dispose();
1808
0
        m_xFormOperations.clear();
1809
1810
        // set the new model wait for the load event
1811
0
        if (m_xTabController.is())
1812
0
            m_xTabController->setModel(Model);
1813
0
        m_xModelAsIndex.set(Model, UNO_QUERY);
1814
0
        m_xModelAsManager.set(Model, UNO_QUERY);
1815
1816
        // only if both ifaces exit, the controller will work successful
1817
0
        if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
1818
0
        {
1819
0
            m_xModelAsManager = nullptr;
1820
0
            m_xModelAsIndex = nullptr;
1821
0
        }
1822
1823
0
        if (m_xModelAsIndex.is())
1824
0
        {
1825
            // re-create m_xFormOperations
1826
0
            m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
1827
0
            m_xFormOperations->setFeatureInvalidation( this );
1828
1829
            // adding load and ui interaction listeners
1830
0
            Reference< XLoadable >  xForm(Model, UNO_QUERY);
1831
0
            if (xForm.is())
1832
0
                xForm->addLoadListener(this);
1833
1834
0
            Reference< XSQLErrorBroadcaster >  xBroadcaster(Model, UNO_QUERY);
1835
0
            if (xBroadcaster.is())
1836
0
                xBroadcaster->addSQLErrorListener(this);
1837
1838
0
            Reference< XDatabaseParameterBroadcaster >  xParamBroadcaster(Model, UNO_QUERY);
1839
0
            if (xParamBroadcaster.is())
1840
0
                xParamBroadcaster->addParameterListener(this);
1841
1842
            // well, is the database already loaded?
1843
            // then we have to simulate a load event
1844
0
            Reference< XLoadable >  xCursor(m_xModelAsIndex, UNO_QUERY);
1845
0
            if (xCursor.is() && xCursor->isLoaded())
1846
0
            {
1847
0
                EventObject aEvt(xCursor);
1848
0
                loaded(aEvt);
1849
0
            }
1850
1851
0
            Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
1852
0
            Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
1853
0
            if (  xPropInfo.is()
1854
0
               && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
1855
0
               && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
1856
0
               && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
1857
0
               && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
1858
0
               )
1859
0
            {
1860
0
                bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
1861
0
                    xModelProps, xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
1862
0
                if ( bEnableDynamicControlBorder )
1863
0
                    m_aControlBorderManager.enableDynamicBorderColor();
1864
0
                else
1865
0
                    m_aControlBorderManager.disableDynamicBorderColor();
1866
1867
0
                Color nColor;
1868
0
                if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
1869
0
                    m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
1870
0
                if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
1871
0
                    m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
1872
0
                if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
1873
0
                    m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
1874
0
            }
1875
0
        }
1876
0
    }
1877
0
    catch( const Exception& )
1878
0
    {
1879
0
        DBG_UNHANDLED_EXCEPTION("svx");
1880
0
    }
1881
0
}
1882
1883
1884
Reference< XTabControllerModel >  FormController::getModel()
1885
0
{
1886
0
    ::osl::MutexGuard aGuard( m_aMutex );
1887
0
    impl_checkDisposed_throw();
1888
1889
0
    DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
1890
0
    if (!m_xTabController.is())
1891
0
        return Reference< XTabControllerModel > ();
1892
0
    return m_xTabController->getModel();
1893
0
}
1894
1895
1896
void FormController::addToEventAttacher(const Reference< XControl > & xControl)
1897
0
{
1898
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1899
0
    OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
1900
0
    if ( !xControl.is() )
1901
0
        return; /* throw IllegalArgumentException(); */
1902
1903
    // register at the event attacher
1904
0
    Reference< XFormComponent >  xComp(xControl->getModel(), UNO_QUERY);
1905
0
    if (!(xComp.is() && m_xModelAsIndex.is()))
1906
0
        return;
1907
1908
    // and look for the position of the ControlModel in it
1909
0
    sal_uInt32 nPos = m_xModelAsIndex->getCount();
1910
0
    Reference< XFormComponent > xTemp;
1911
0
    for( ; nPos; )
1912
0
    {
1913
0
        m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1914
0
        if (xComp.get() == xTemp.get())
1915
0
        {
1916
0
            m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), Any(xControl) );
1917
0
            break;
1918
0
        }
1919
0
    }
1920
0
}
1921
1922
1923
void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
1924
0
{
1925
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1926
0
    OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
1927
0
    if ( !xControl.is() )
1928
0
        return; /* throw IllegalArgumentException(); */
1929
1930
    // register at the event attacher
1931
0
    Reference< XFormComponent >  xComp(xControl->getModel(), UNO_QUERY);
1932
0
    if ( !(xComp.is() && m_xModelAsIndex.is()) )
1933
0
        return;
1934
1935
    // and look for the position of the ControlModel in it
1936
0
    sal_uInt32 nPos = m_xModelAsIndex->getCount();
1937
0
    Reference< XFormComponent > xTemp;
1938
0
    for( ; nPos; )
1939
0
    {
1940
0
        m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
1941
0
        if (xComp.get() == xTemp.get())
1942
0
        {
1943
0
            m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
1944
0
            break;
1945
0
        }
1946
0
    }
1947
0
}
1948
1949
1950
void FormController::setContainer(const Reference< XControlContainer > & xContainer)
1951
0
{
1952
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
1953
0
    Reference< XTabControllerModel >  xTabModel(getModel());
1954
0
    DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
1955
        // if we have a new container we need a model
1956
0
    DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
1957
1958
0
    ::osl::MutexGuard aGuard( m_aMutex );
1959
0
    Reference< XContainer >  xCurrentContainer;
1960
0
    if (m_xTabController.is())
1961
0
        xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
1962
0
    if (xCurrentContainer.is())
1963
0
    {
1964
0
        xCurrentContainer->removeContainerListener(this);
1965
1966
0
        if ( m_aTabActivationIdle.IsActive() )
1967
0
            m_aTabActivationIdle.Stop();
1968
1969
        // clear the filter map
1970
0
        ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
1971
0
        m_aFilterComponents.clear();
1972
1973
        // collecting the controls
1974
0
        for (const Reference<XControl>& rControl : m_aControls)
1975
0
            implControlRemoved( rControl, true );
1976
1977
        // make database-specific things
1978
0
        if (m_bDBConnection && isListeningForChanges())
1979
0
            stopListening();
1980
1981
0
        m_aControls.realloc( 0 );
1982
0
    }
1983
1984
0
    if (m_xTabController.is())
1985
0
        m_xTabController->setContainer(xContainer);
1986
1987
    // What controls belong to the container?
1988
0
    if (xContainer.is() && xTabModel.is())
1989
0
    {
1990
0
        const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
1991
0
        Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
1992
1993
0
        sal_Int32 nCount = aModels.getLength();
1994
0
        m_aControls = Sequence< Reference< XControl > >( nCount );
1995
0
        Reference< XControl > * pControls = m_aControls.getArray();
1996
1997
        // collecting the controls
1998
0
        sal_Int32 j = 0;
1999
0
        for (const Reference< XControlModel >& rModel : aModels )
2000
0
        {
2001
0
            Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
2002
0
            if ( xControl.is() )
2003
0
            {
2004
0
                pControls[j++] = xControl;
2005
0
                implControlInserted( xControl, true );
2006
0
            }
2007
0
        }
2008
2009
        // not every model had an associated control
2010
0
        if (j != nCount)
2011
0
            m_aControls.realloc(j);
2012
2013
        // listen at the container
2014
0
        Reference< XContainer >  xNewContainer(xContainer, UNO_QUERY);
2015
0
        if (xNewContainer.is())
2016
0
            xNewContainer->addContainerListener(this);
2017
2018
        // make database-specific things
2019
0
        if (m_bDBConnection)
2020
0
        {
2021
0
            m_bLocked = determineLockState();
2022
0
            setLocks();
2023
0
            if (!isLocked())
2024
0
                startListening();
2025
0
        }
2026
0
    }
2027
    // the controls are in the right order
2028
0
    m_bControlsSorted = true;
2029
0
}
2030
2031
2032
Reference< XControlContainer >  FormController::getContainer()
2033
0
{
2034
0
    ::osl::MutexGuard aGuard( m_aMutex );
2035
0
    impl_checkDisposed_throw();
2036
2037
0
    DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
2038
0
    if (!m_xTabController.is())
2039
0
        return Reference< XControlContainer > ();
2040
0
    return m_xTabController->getContainer();
2041
0
}
2042
2043
2044
Sequence< Reference< XControl > > FormController::getControls()
2045
0
{
2046
0
    ::osl::MutexGuard aGuard( m_aMutex );
2047
0
    impl_checkDisposed_throw();
2048
2049
0
    if (!m_bControlsSorted)
2050
0
    {
2051
0
        Reference< XTabControllerModel >  xModel = getModel();
2052
0
        if (!xModel.is())
2053
0
            return m_aControls;
2054
2055
0
        const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
2056
0
        sal_Int32 nModels = aControlModels.getLength();
2057
2058
0
        Sequence< Reference< XControl > > aNewControls(nModels);
2059
2060
0
        Reference< XControl > * pControls = aNewControls.getArray();
2061
0
        Reference< XControl >  xControl;
2062
2063
        // rearrange the controls according to the tab order sequence
2064
0
        sal_Int32 j = 0;
2065
0
        for ( const Reference< XControlModel >& rModel : aControlModels )
2066
0
        {
2067
0
            xControl = findControl( m_aControls, rModel, true, true );
2068
0
            if ( xControl.is() )
2069
0
                pControls[j++] = xControl;
2070
0
        }
2071
2072
        // not every model had an associated control
2073
0
        if ( j != nModels )
2074
0
            aNewControls.realloc( j );
2075
2076
0
        m_aControls = std::move(aNewControls);
2077
0
        m_bControlsSorted = true;
2078
0
    }
2079
0
    return m_aControls;
2080
0
}
2081
2082
2083
void FormController::autoTabOrder()
2084
0
{
2085
0
    ::osl::MutexGuard aGuard( m_aMutex );
2086
0
    impl_checkDisposed_throw();
2087
2088
0
    DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
2089
0
    if (m_xTabController.is())
2090
0
        m_xTabController->autoTabOrder();
2091
0
}
2092
2093
2094
void FormController::activateTabOrder()
2095
0
{
2096
0
    ::osl::MutexGuard aGuard( m_aMutex );
2097
0
    impl_checkDisposed_throw();
2098
2099
0
    DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
2100
0
    if (m_xTabController.is())
2101
0
        m_xTabController->activateTabOrder();
2102
0
}
2103
2104
2105
void FormController::setControlLock(const Reference< XControl > & xControl)
2106
0
{
2107
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2108
0
    bool bLocked = isLocked();
2109
2110
    // It is locked
2111
    // a. if the entire record is locked
2112
    // b. if the associated field is locked
2113
0
    Reference< XBoundControl >  xBound(xControl, UNO_QUERY);
2114
0
    if (!(xBound.is() &&
2115
0
        ( (bLocked && bLocked != bool(xBound->getLock())) ||
2116
0
          !bLocked)))    // always uncheck individual fields when unlocking
2117
0
        return;
2118
2119
    // there is a data source
2120
0
    Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
2121
0
    if (!(xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)))
2122
0
        return;
2123
2124
    // what about the ReadOnly and Enable properties
2125
0
    bool bTouch = true;
2126
0
    if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet))
2127
0
        bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
2128
0
    if (bTouch && ::comphelper::hasProperty(FM_PROP_READONLY, xSet))
2129
0
        bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
2130
2131
0
    if (!bTouch)
2132
0
        return;
2133
2134
0
    Reference< XPropertySet >  xField;
2135
0
    xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2136
0
    if (!xField.is())
2137
0
        return;
2138
2139
0
    if (bLocked)
2140
0
        xBound->setLock(bLocked);
2141
0
    else
2142
0
    {
2143
0
        try
2144
0
        {
2145
0
            Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
2146
0
            if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
2147
0
                xBound->setLock(true);
2148
0
            else
2149
0
                xBound->setLock(bLocked);
2150
0
        }
2151
0
        catch( const Exception& )
2152
0
        {
2153
0
            DBG_UNHANDLED_EXCEPTION("svx");
2154
0
        }
2155
2156
0
    }
2157
0
}
2158
2159
2160
void FormController::setLocks()
2161
0
{
2162
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2163
    // lock/unlock all controls connected to a data source
2164
0
    for (const Reference<XControl>& rControl : m_aControls)
2165
0
        setControlLock( rControl );
2166
0
}
2167
2168
2169
namespace
2170
{
2171
    bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
2172
0
    {
2173
0
        bool bShould = false;
2174
2175
0
        Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
2176
0
        if ( xBound.is() )
2177
0
        {
2178
0
            bShould = true;
2179
0
        }
2180
0
        else if ( _rxControl.is() )
2181
0
        {
2182
0
            Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
2183
0
            if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
2184
0
            {
2185
0
                Reference< XPropertySet > xField;
2186
0
                xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
2187
0
                bShould = xField.is();
2188
2189
0
                if ( !bShould && _rxBoundFieldListener.is() )
2190
0
                    xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
2191
0
            }
2192
0
        }
2193
2194
0
        return bShould;
2195
0
    }
2196
}
2197
2198
2199
void FormController::startControlModifyListening(const Reference< XControl > & xControl)
2200
0
{
2201
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2202
2203
0
    bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
2204
2205
    // artificial while
2206
0
    while ( bModifyListening )
2207
0
    {
2208
0
        Reference< XModifyBroadcaster >  xMod(xControl, UNO_QUERY);
2209
0
        if (xMod.is())
2210
0
        {
2211
0
            xMod->addModifyListener(this);
2212
0
            break;
2213
0
        }
2214
2215
        // all the text to prematurely recognize a modified
2216
0
        Reference< XTextComponent >  xText(xControl, UNO_QUERY);
2217
0
        if (xText.is())
2218
0
        {
2219
0
            xText->addTextListener(this);
2220
0
            break;
2221
0
        }
2222
2223
0
        Reference< XCheckBox >  xBox(xControl, UNO_QUERY);
2224
0
        if (xBox.is())
2225
0
        {
2226
0
            xBox->addItemListener(this);
2227
0
            break;
2228
0
        }
2229
2230
0
        Reference< XComboBox >  xCbBox(xControl, UNO_QUERY);
2231
0
        if (xCbBox.is())
2232
0
        {
2233
0
            xCbBox->addItemListener(this);
2234
0
            break;
2235
0
        }
2236
2237
0
        Reference< XListBox >  xListBox(xControl, UNO_QUERY);
2238
0
        if (xListBox.is())
2239
0
        {
2240
0
            xListBox->addItemListener(this);
2241
0
            break;
2242
0
        }
2243
0
        break;
2244
0
    }
2245
0
}
2246
2247
2248
void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
2249
0
{
2250
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2251
2252
0
    bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
2253
2254
    // artificial while
2255
0
    while (bModifyListening)
2256
0
    {
2257
0
        Reference< XModifyBroadcaster >  xMod(xControl, UNO_QUERY);
2258
0
        if (xMod.is())
2259
0
        {
2260
0
            xMod->removeModifyListener(this);
2261
0
            break;
2262
0
        }
2263
        // all the text to prematurely recognize a modified
2264
0
        Reference< XTextComponent >  xText(xControl, UNO_QUERY);
2265
0
        if (xText.is())
2266
0
        {
2267
0
            xText->removeTextListener(this);
2268
0
            break;
2269
0
        }
2270
2271
0
        Reference< XCheckBox >  xBox(xControl, UNO_QUERY);
2272
0
        if (xBox.is())
2273
0
        {
2274
0
            xBox->removeItemListener(this);
2275
0
            break;
2276
0
        }
2277
2278
0
        Reference< XComboBox >  xCbBox(xControl, UNO_QUERY);
2279
0
        if (xCbBox.is())
2280
0
        {
2281
0
            xCbBox->removeItemListener(this);
2282
0
            break;
2283
0
        }
2284
2285
0
        Reference< XListBox >  xListBox(xControl, UNO_QUERY);
2286
0
        if (xListBox.is())
2287
0
        {
2288
0
            xListBox->removeItemListener(this);
2289
0
            break;
2290
0
        }
2291
0
        break;
2292
0
    }
2293
0
}
2294
2295
2296
void FormController::startListening()
2297
0
{
2298
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2299
0
    m_bModified  = false;
2300
2301
    // now register at bound fields
2302
0
    for (const Reference<XControl>& rControl : m_aControls)
2303
0
        startControlModifyListening( rControl );
2304
0
}
2305
2306
2307
void FormController::stopListening()
2308
0
{
2309
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2310
0
    m_bModified  = false;
2311
2312
    // now register at bound fields
2313
0
    for (const Reference<XControl>& rControl : m_aControls)
2314
0
        stopControlModifyListening( rControl );
2315
0
}
2316
2317
2318
Reference< XControl >  FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
2319
0
{
2320
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2321
0
    DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
2322
2323
0
    const Reference< XControl >* pControls = std::find_if(std::cbegin(_rControls), std::cend(_rControls),
2324
0
        [&xCtrlModel](const Reference< XControl >& rControl) {
2325
0
            return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
2326
0
    if (pControls != std::cend(_rControls))
2327
0
    {
2328
0
        Reference< XControl > xControl( *pControls );
2329
0
        auto i = static_cast<sal_Int32>(std::distance(std::cbegin(_rControls), pControls));
2330
0
        if ( _bRemove )
2331
0
            ::comphelper::removeElementAt( _rControls, i );
2332
0
        else if ( _bOverWrite )
2333
0
            _rControls.getArray()[i].clear();
2334
0
        return xControl;
2335
0
    }
2336
0
    return Reference< XControl > ();
2337
0
}
2338
2339
2340
void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
2341
0
{
2342
0
    Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2343
0
    if ( xWindow.is() )
2344
0
    {
2345
0
        xWindow->addFocusListener( this );
2346
0
        xWindow->addMouseListener( this );
2347
2348
0
        if ( _bAddToEventAttacher )
2349
0
            addToEventAttacher( _rxControl );
2350
0
    }
2351
2352
    // add a dispatch interceptor to the control (if supported)
2353
0
    Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
2354
0
    if ( xInterception.is() )
2355
0
        createInterceptor( xInterception );
2356
2357
0
    if ( !_rxControl.is() )
2358
0
        return;
2359
2360
0
    Reference< XControlModel > xModel( _rxControl->getModel() );
2361
2362
    // we want to know about the reset of the model of our controls
2363
    // (for correctly resetting m_bModified)
2364
0
    Reference< XReset >  xReset( xModel, UNO_QUERY );
2365
0
    if ( xReset.is() )
2366
0
        xReset->addResetListener( this );
2367
2368
    // and we want to know about the validity, to visually indicate it
2369
0
    Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2370
0
    if ( xValidatable.is() )
2371
0
    {
2372
0
        xValidatable->addFormComponentValidityListener( this );
2373
0
        m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
2374
0
    }
2375
2376
0
}
2377
2378
2379
void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
2380
0
{
2381
0
    Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
2382
0
    if ( xWindow.is() )
2383
0
    {
2384
0
        xWindow->removeFocusListener( this );
2385
0
        xWindow->removeMouseListener( this );
2386
2387
0
        if ( _bRemoveFromEventAttacher )
2388
0
            removeFromEventAttacher( _rxControl );
2389
0
    }
2390
2391
0
    Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
2392
0
    if ( xInterception.is() )
2393
0
        deleteInterceptor( xInterception );
2394
2395
0
    if ( _rxControl.is() )
2396
0
    {
2397
0
        Reference< XControlModel > xModel( _rxControl->getModel() );
2398
2399
0
        Reference< XReset >  xReset( xModel, UNO_QUERY );
2400
0
        if ( xReset.is() )
2401
0
            xReset->removeResetListener( this );
2402
2403
0
        Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
2404
0
        if ( xValidatable.is() )
2405
0
            xValidatable->removeFormComponentValidityListener( this );
2406
0
    }
2407
0
}
2408
2409
2410
void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
2411
0
{
2412
0
    if ( m_xCurrentControl.get() == _rxControl.get() )
2413
0
        return;
2414
2415
0
    Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
2416
0
    if ( xGridControl.is() )
2417
0
        xGridControl->removeGridControlListener( this );
2418
2419
0
    m_xCurrentControl = _rxControl;
2420
2421
0
    xGridControl.set( m_xCurrentControl, UNO_QUERY );
2422
0
    if ( xGridControl.is() )
2423
0
        xGridControl->addGridControlListener( this );
2424
0
}
2425
2426
2427
void FormController::insertControl(const Reference< XControl > & xControl)
2428
0
{
2429
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2430
0
    m_bControlsSorted = false;
2431
0
    m_aControls.realloc(m_aControls.getLength() + 1);
2432
0
    m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
2433
2434
0
    if (m_pColumnInfoCache)
2435
0
        m_pColumnInfoCache->deinitializeControls();
2436
2437
0
    implControlInserted( xControl, m_bAttachEvents );
2438
2439
0
    if (m_bDBConnection && !m_bFiltering)
2440
0
        setControlLock(xControl);
2441
2442
0
    if (isListeningForChanges() && m_bAttachEvents)
2443
0
        startControlModifyListening( xControl );
2444
0
}
2445
2446
2447
void FormController::removeControl(const Reference< XControl > & xControl)
2448
0
{
2449
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2450
0
    auto pControl = std::find_if(std::cbegin(m_aControls), std::cend(m_aControls),
2451
0
        [&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
2452
0
    if (pControl != std::cend(m_aControls))
2453
0
    {
2454
0
        auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(m_aControls), pControl));
2455
0
        ::comphelper::removeElementAt( m_aControls, nIndex );
2456
0
    }
2457
2458
0
    FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2459
0
    if ( componentPos != m_aFilterComponents.end() )
2460
0
        m_aFilterComponents.erase( componentPos );
2461
2462
0
    implControlRemoved( xControl, m_bDetachEvents );
2463
2464
0
    if ( isListeningForChanges() && m_bDetachEvents )
2465
0
        stopControlModifyListening( xControl );
2466
0
}
2467
2468
// XLoadListener
2469
2470
void FormController::loaded(const EventObject& rEvent)
2471
0
{
2472
0
    OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
2473
2474
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2475
0
    ::osl::MutexGuard aGuard( m_aMutex );
2476
0
    Reference< XRowSet >  xForm(rEvent.Source, UNO_QUERY);
2477
    // do we have a connected data source
2478
0
    if (xForm.is() && getConnection(xForm).is())
2479
0
    {
2480
0
        Reference< XPropertySet >  xSet(xForm, UNO_QUERY);
2481
0
        if (xSet.is())
2482
0
        {
2483
0
            Any aVal        = xSet->getPropertyValue(FM_PROP_CYCLE);
2484
0
            sal_Int32 aVal2 = 0;
2485
0
            ::cppu::enum2int(aVal2,aVal);
2486
0
            m_bCycle        = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
2487
0
            m_bCanUpdate    = canUpdate(xSet);
2488
0
            m_bCanInsert    = canInsert(xSet);
2489
0
            m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
2490
0
            m_bCurrentRecordNew      = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
2491
2492
0
            startFormListening( xSet, false );
2493
2494
            // set the locks for the current controls
2495
0
            if (getContainer().is())
2496
0
            {
2497
0
                m_aLoadEvent.Call();
2498
0
            }
2499
0
        }
2500
0
        else
2501
0
        {
2502
0
            m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2503
0
            m_bCurrentRecordModified = false;
2504
0
            m_bCurrentRecordNew = false;
2505
0
            m_bLocked = false;
2506
0
        }
2507
0
        m_bDBConnection = true;
2508
0
    }
2509
0
    else
2510
0
    {
2511
0
        m_bDBConnection = false;
2512
0
        m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2513
0
        m_bCurrentRecordModified = false;
2514
0
        m_bCurrentRecordNew = false;
2515
0
        m_bLocked = false;
2516
0
    }
2517
2518
0
    Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
2519
0
    m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
2520
2521
0
    updateAllDispatchers();
2522
0
}
2523
2524
2525
void FormController::updateAllDispatchers() const
2526
0
{
2527
0
    ::std::for_each(
2528
0
        m_aFeatureDispatchers.begin(),
2529
0
        m_aFeatureDispatchers.end(),
2530
0
        [] (const DispatcherContainer::value_type& dispatcher) {
2531
0
            UpdateAllListeners()(dispatcher.second);
2532
0
        });
2533
0
}
2534
2535
2536
IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
2537
0
{
2538
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2539
0
    m_bLocked = determineLockState();
2540
2541
0
    setLocks();
2542
2543
0
    if (!m_bLocked)
2544
0
        startListening();
2545
2546
    // just one exception toggle the auto values
2547
0
    if (m_bCurrentRecordNew)
2548
0
        toggleAutoFields(true);
2549
0
}
2550
2551
2552
void FormController::unloaded(const EventObject& /*rEvent*/)
2553
0
{
2554
0
    ::osl::MutexGuard aGuard( m_aMutex );
2555
0
    impl_checkDisposed_throw();
2556
2557
0
    updateAllDispatchers();
2558
0
}
2559
2560
2561
void FormController::reloading(const EventObject& /*aEvent*/)
2562
0
{
2563
0
    ::osl::MutexGuard aGuard( m_aMutex );
2564
0
    impl_checkDisposed_throw();
2565
2566
    // do the same like in unloading
2567
    // just one exception toggle the auto values
2568
0
    m_aToggleEvent.CancelPendingCall();
2569
0
    unload();
2570
0
}
2571
2572
2573
void FormController::reloaded(const EventObject& aEvent)
2574
0
{
2575
0
    ::osl::MutexGuard aGuard( m_aMutex );
2576
0
    impl_checkDisposed_throw();
2577
2578
0
    loaded(aEvent);
2579
0
}
2580
2581
2582
void FormController::unloading(const EventObject& /*aEvent*/)
2583
0
{
2584
0
    ::osl::MutexGuard aGuard( m_aMutex );
2585
0
    impl_checkDisposed_throw();
2586
2587
0
    unload();
2588
0
}
2589
2590
2591
void FormController::unload()
2592
0
{
2593
0
    ::osl::MutexGuard aGuard( m_aMutex );
2594
0
    impl_checkDisposed_throw();
2595
2596
0
    m_aLoadEvent.CancelPendingCall();
2597
2598
    // be sure not to have autofields
2599
0
    if (m_bCurrentRecordNew)
2600
0
        toggleAutoFields(false);
2601
2602
    // remove bound field listing again
2603
0
    removeBoundFieldListener();
2604
2605
0
    if (m_bDBConnection && isListeningForChanges())
2606
0
        stopListening();
2607
2608
0
    Reference< XPropertySet >  xSet( m_xModelAsIndex, UNO_QUERY );
2609
0
    if ( m_bDBConnection && xSet.is() )
2610
0
        stopFormListening( xSet, false );
2611
2612
0
    m_bDBConnection = false;
2613
0
    m_bCanInsert = m_bCanUpdate = m_bCycle = false;
2614
0
    m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
2615
2616
0
    m_pColumnInfoCache.reset();
2617
0
}
2618
2619
2620
void FormController::removeBoundFieldListener()
2621
0
{
2622
0
    for (const Reference<XControl>& rControl : m_aControls)
2623
0
    {
2624
0
        Reference< XPropertySet > xProp( rControl, UNO_QUERY );
2625
0
        if ( xProp.is() )
2626
0
            xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
2627
0
    }
2628
0
}
2629
2630
2631
void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2632
0
{
2633
0
    try
2634
0
    {
2635
0
        if ( m_bCanInsert || m_bCanUpdate )   // form can be modified
2636
0
        {
2637
0
            _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
2638
0
            _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
2639
2640
0
            if ( !_bPropertiesOnly )
2641
0
            {
2642
                // set the Listener for UI interaction
2643
0
                Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2644
0
                if ( xApprove.is() )
2645
0
                    xApprove->addRowSetApproveListener( this );
2646
2647
                // listener for row set changes
2648
0
                Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2649
0
                if ( xRowSet.is() )
2650
0
                    xRowSet->addRowSetListener( this );
2651
0
            }
2652
0
        }
2653
2654
0
        Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2655
0
        if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2656
0
            _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2657
0
    }
2658
0
    catch( const Exception& )
2659
0
    {
2660
0
        DBG_UNHANDLED_EXCEPTION("svx");
2661
0
    }
2662
0
}
2663
2664
2665
void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
2666
0
{
2667
0
    try
2668
0
    {
2669
0
        if ( m_bCanInsert || m_bCanUpdate )
2670
0
        {
2671
0
            _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
2672
0
            _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
2673
2674
0
            if ( !_bPropertiesOnly )
2675
0
            {
2676
0
                Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
2677
0
                if (xApprove.is())
2678
0
                    xApprove->removeRowSetApproveListener(this);
2679
2680
0
                Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
2681
0
                if ( xRowSet.is() )
2682
0
                    xRowSet->removeRowSetListener( this );
2683
0
            }
2684
0
        }
2685
2686
0
        Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
2687
0
        if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
2688
0
            _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
2689
0
    }
2690
0
    catch( const Exception& )
2691
0
    {
2692
0
        DBG_UNHANDLED_EXCEPTION("svx");
2693
0
    }
2694
0
}
2695
2696
// css::sdbc::XRowSetListener
2697
2698
void FormController::cursorMoved(const EventObject& /*event*/)
2699
0
{
2700
0
    ::osl::MutexGuard aGuard( m_aMutex );
2701
0
    impl_checkDisposed_throw();
2702
2703
    // toggle the locking ?
2704
0
    if (m_bLocked != determineLockState())
2705
0
    {
2706
0
        m_bLocked = !m_bLocked;
2707
0
        setLocks();
2708
0
        if (isListeningForChanges())
2709
0
            startListening();
2710
0
        else
2711
0
            stopListening();
2712
0
    }
2713
2714
    // neither the current control nor the current record are modified anymore
2715
0
    m_bCurrentRecordModified = m_bModified = false;
2716
0
}
2717
2718
2719
void FormController::rowChanged(const EventObject& /*event*/)
2720
0
{
2721
    // not interested in ...
2722
0
}
2723
2724
void FormController::rowSetChanged(const EventObject& /*event*/)
2725
0
{
2726
    // not interested in ...
2727
0
}
2728
2729
2730
// XContainerListener
2731
2732
void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
2733
0
{
2734
0
    ::osl::MutexGuard aGuard( m_aMutex );
2735
0
    impl_checkDisposed_throw();
2736
2737
0
    Reference< XControl > xControl( evt.Element, UNO_QUERY );
2738
0
    if ( !xControl.is() )
2739
0
        return;
2740
2741
0
    Reference< XFormComponent >  xModel(xControl->getModel(), UNO_QUERY);
2742
0
    if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2743
0
    {
2744
0
        insertControl(xControl);
2745
2746
0
        if ( m_aTabActivationIdle.IsActive() )
2747
0
            m_aTabActivationIdle.Stop();
2748
2749
0
        m_aTabActivationIdle.Start();
2750
0
    }
2751
    // are we in filtermode and a XModeSelector has inserted an element
2752
0
    else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2753
0
    {
2754
0
        xModel.set(evt.Source, UNO_QUERY);
2755
0
        if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2756
0
        {
2757
0
            Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
2758
0
            if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
2759
0
            {
2760
                // does the model use a bound field ?
2761
0
                Reference< XPropertySet >  xField;
2762
0
                xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
2763
2764
0
                Reference< XTextComponent >  xText(xControl, UNO_QUERY);
2765
                // may we filter the field?
2766
0
                if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
2767
0
                    ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
2768
0
                {
2769
0
                    m_aFilterComponents.push_back( xText );
2770
0
                    xText->addTextListener( this );
2771
0
                }
2772
0
            }
2773
0
        }
2774
0
    }
2775
0
}
2776
2777
2778
void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
2779
0
{
2780
    // simulate an elementRemoved
2781
0
    ContainerEvent aRemoveEvent( evt );
2782
0
    aRemoveEvent.Element = evt.ReplacedElement;
2783
0
    aRemoveEvent.ReplacedElement = Any();
2784
0
    elementRemoved( aRemoveEvent );
2785
2786
    // simulate an elementInserted
2787
0
    ContainerEvent aInsertEvent( evt );
2788
0
    aInsertEvent.ReplacedElement = Any();
2789
0
    elementInserted( aInsertEvent );
2790
0
}
2791
2792
2793
void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
2794
0
{
2795
0
    ::osl::MutexGuard aGuard( m_aMutex );
2796
0
    impl_checkDisposed_throw();
2797
2798
0
    Reference< XControl >  xControl;
2799
0
    evt.Element >>= xControl;
2800
0
    if (!xControl.is())
2801
0
        return;
2802
2803
0
    Reference< XFormComponent >  xModel(xControl->getModel(), UNO_QUERY);
2804
0
    if (xModel.is() && m_xModelAsIndex == xModel->getParent())
2805
0
    {
2806
0
        removeControl(xControl);
2807
        // Do not recalculate TabOrder, because it must already work internally!
2808
0
    }
2809
    // are we in filtermode and a XModeSelector has inserted an element
2810
0
    else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
2811
0
    {
2812
0
        FilterComponents::iterator componentPos = ::std::find(
2813
0
            m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
2814
0
        if ( componentPos != m_aFilterComponents.end() )
2815
0
            m_aFilterComponents.erase( componentPos );
2816
0
    }
2817
0
}
2818
2819
2820
Reference< XControl >  FormController::isInList(const Reference< XWindowPeer > & xPeer) const
2821
0
{
2822
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2823
0
    const Reference< XControl >* pControls = m_aControls.getConstArray();
2824
2825
0
    sal_uInt32 nCtrls = m_aControls.getLength();
2826
0
    for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
2827
0
    {
2828
0
        if ( pControls->is() )
2829
0
        {
2830
0
            Reference< XVclWindowPeer >  xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
2831
0
            if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
2832
0
                return *pControls;
2833
0
        }
2834
0
    }
2835
0
    return Reference< XControl > ();
2836
0
}
2837
2838
2839
void FormController::activateFirst()
2840
0
{
2841
0
    ::osl::MutexGuard aGuard( m_aMutex );
2842
0
    impl_checkDisposed_throw();
2843
2844
0
    DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
2845
0
    if (m_xTabController.is())
2846
0
        m_xTabController->activateFirst();
2847
0
}
2848
2849
2850
void FormController::activateLast()
2851
0
{
2852
0
    ::osl::MutexGuard aGuard( m_aMutex );
2853
0
    impl_checkDisposed_throw();
2854
2855
0
    DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
2856
0
    if (m_xTabController.is())
2857
0
        m_xTabController->activateLast();
2858
0
}
2859
2860
// XFormController
2861
2862
Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
2863
0
{
2864
0
    ::osl::MutexGuard aGuard( m_aMutex );
2865
0
    impl_checkDisposed_throw();
2866
2867
0
    return m_xFormOperations;
2868
0
}
2869
2870
2871
Reference< XControl> SAL_CALL FormController::getCurrentControl()
2872
0
{
2873
0
    ::osl::MutexGuard aGuard( m_aMutex );
2874
0
    impl_checkDisposed_throw();
2875
0
    return m_xCurrentControl;
2876
0
}
2877
2878
2879
void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
2880
0
{
2881
0
    ::osl::MutexGuard aGuard( m_aMutex );
2882
0
    impl_checkDisposed_throw();
2883
0
    m_aActivateListeners.addInterface(l);
2884
0
}
2885
2886
void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
2887
0
{
2888
0
    ::osl::MutexGuard aGuard( m_aMutex );
2889
0
    impl_checkDisposed_throw();
2890
0
    m_aActivateListeners.removeInterface(l);
2891
0
}
2892
2893
2894
void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
2895
0
{
2896
0
    ::osl::MutexGuard aGuard( m_aMutex );
2897
0
    impl_checkDisposed_throw();
2898
2899
0
    if ( !ChildController.is() )
2900
0
        throw IllegalArgumentException( OUString(), *this, 1 );
2901
        // TODO: (localized) error message
2902
2903
    // the parent of our (to-be-)child must be our own model
2904
0
    Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
2905
0
    if ( !xFormOfChild.is() )
2906
0
        throw IllegalArgumentException( OUString(), *this, 1 );
2907
        // TODO: (localized) error message
2908
2909
0
    if ( xFormOfChild->getParent() != m_xModelAsIndex )
2910
0
        throw IllegalArgumentException( OUString(), *this, 1 );
2911
        // TODO: (localized) error message
2912
2913
0
    m_aChildren.push_back( ChildController );
2914
0
    ChildController->setParent( *this );
2915
2916
    // search the position of the model within the form
2917
0
    sal_uInt32 nPos = m_xModelAsIndex->getCount();
2918
0
    Reference< XFormComponent > xTemp;
2919
0
    for( ; nPos; )
2920
0
    {
2921
0
        m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
2922
0
        if ( xFormOfChild == xTemp )
2923
0
        {
2924
0
            m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), Any( ChildController) );
2925
0
            break;
2926
0
        }
2927
0
    }
2928
0
}
2929
2930
2931
Reference< XFormControllerContext > SAL_CALL FormController::getContext()
2932
0
{
2933
0
    ::osl::MutexGuard aGuard( m_aMutex );
2934
0
    impl_checkDisposed_throw();
2935
0
    return m_xFormControllerContext;
2936
0
}
2937
2938
2939
void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
2940
0
{
2941
0
    ::osl::MutexGuard aGuard( m_aMutex );
2942
0
    impl_checkDisposed_throw();
2943
0
    m_xFormControllerContext = _context;
2944
0
}
2945
2946
2947
Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
2948
0
{
2949
0
    ::osl::MutexGuard aGuard( m_aMutex );
2950
0
    impl_checkDisposed_throw();
2951
0
    return m_xInteractionHandler;
2952
0
}
2953
2954
2955
void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
2956
0
{
2957
0
    ::osl::MutexGuard aGuard( m_aMutex );
2958
0
    impl_checkDisposed_throw();
2959
0
    m_xInteractionHandler = _interactionHandler;
2960
0
}
2961
2962
2963
void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
2964
0
{
2965
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
2966
    // create the composer
2967
0
    Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
2968
0
    Reference< XConnection > xConnection(getConnection(xForm));
2969
0
    if (xForm.is())
2970
0
    {
2971
0
        try
2972
0
        {
2973
0
            Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
2974
0
            m_xComposer.set(
2975
0
                xFactory->createInstance(u"com.sun.star.sdb.SingleSelectQueryComposer"_ustr),
2976
0
                UNO_QUERY_THROW );
2977
2978
0
            Reference< XPropertySet > xSet( xForm, UNO_QUERY );
2979
0
            OUString sStatement  = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
2980
0
            OUString sFilter     = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
2981
0
            m_xComposer->setElementaryQuery( sStatement );
2982
0
            m_xComposer->setFilter( sFilter );
2983
0
        }
2984
0
        catch( const Exception& )
2985
0
        {
2986
0
            DBG_UNHANDLED_EXCEPTION("svx");
2987
0
        }
2988
0
    }
2989
2990
0
    if (m_xComposer.is())
2991
0
    {
2992
0
        const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
2993
2994
        // ok, we receive the list of filters as sequence of fieldnames, value
2995
        // now we have to transform the fieldname into UI names, that could be a label of the field or
2996
        // an aliasname or the fieldname itself
2997
2998
        // first adjust the field names if necessary
2999
0
        Reference< XNameAccess > xQueryColumns =
3000
0
            Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
3001
3002
0
        for (auto& rFieldInfo : rFieldInfos)
3003
0
        {
3004
0
            if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
3005
0
            {
3006
0
                if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
3007
0
                    rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
3008
0
            }
3009
0
        }
3010
3011
0
        Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
3012
        // now transfer the filters into Value/TextComponent pairs
3013
0
        ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
3014
3015
        // need to parse criteria localized
3016
0
        Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
3017
0
        Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
3018
0
        xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3019
0
        Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
3020
0
        const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
3021
0
        OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
3022
3023
        // retrieving the filter
3024
0
        for (const Sequence < PropertyValue >& rRow : aFilterRows)
3025
0
        {
3026
0
            FmFilterRow aRow;
3027
3028
            // search a field for the given name
3029
0
            for (const PropertyValue& rRefValue : rRow)
3030
0
            {
3031
                // look for the text component
3032
0
                Reference< XPropertySet > xField;
3033
0
                try
3034
0
                {
3035
0
                    Reference< XPropertySet > xSet;
3036
0
                    OUString aRealName;
3037
3038
                    // first look with the given name
3039
0
                    if (xQueryColumns->hasByName(rRefValue.Name))
3040
0
                    {
3041
0
                        xQueryColumns->getByName(rRefValue.Name) >>= xSet;
3042
3043
                        // get the RealName
3044
0
                        xSet->getPropertyValue(u"RealName"_ustr) >>= aRealName;
3045
3046
                        // compare the condition field name and the RealName
3047
0
                        if (aCompare(aRealName, rRefValue.Name))
3048
0
                            xField = xSet;
3049
0
                    }
3050
0
                    if (!xField.is())
3051
0
                    {
3052
                        // no we have to check every column to find the realname
3053
0
                        Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
3054
0
                        for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
3055
0
                        {
3056
0
                            xColumnsByIndex->getByIndex(n) >>= xSet;
3057
0
                            xSet->getPropertyValue(u"RealName"_ustr) >>= aRealName;
3058
0
                            if (aCompare(aRealName, rRefValue.Name))
3059
0
                            {
3060
                                // get the column by its alias
3061
0
                                xField = xSet;
3062
0
                                break;
3063
0
                            }
3064
0
                        }
3065
0
                    }
3066
0
                    if (!xField.is())
3067
0
                        continue;
3068
0
                }
3069
0
                catch (const Exception&)
3070
0
                {
3071
0
                    continue;
3072
0
                }
3073
3074
                // find the text component
3075
0
                for (const auto& rFieldInfo : rFieldInfos)
3076
0
                {
3077
                    // we found the field so insert a new entry to the filter row
3078
0
                    if (rFieldInfo.xField == xField)
3079
0
                    {
3080
                        // do we already have the control ?
3081
0
                        if (aRow.find(rFieldInfo.xText) != aRow.end())
3082
0
                        {
3083
0
                            OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
3084
0
                            aRow[rFieldInfo.xText] = aRow[rFieldInfo.xText] + " "  +
3085
0
                                                        OStringToOUString(aVal, RTL_TEXTENCODING_ASCII_US) + " " +
3086
0
                                                        ::comphelper::getString(rRefValue.Value);
3087
0
                        }
3088
0
                        else
3089
0
                        {
3090
0
                            OUString sPredicate,sErrorMsg;
3091
0
                            rRefValue.Value >>= sPredicate;
3092
0
                            std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
3093
0
                            if ( pParseNode != nullptr )
3094
0
                            {
3095
0
                                OUString sCriteria;
3096
0
                                switch (rRefValue.Handle)
3097
0
                                {
3098
0
                                    case css::sdb::SQLFilterOperator::EQUAL:
3099
0
                                        sCriteria += "=";
3100
0
                                        break;
3101
0
                                    case css::sdb::SQLFilterOperator::NOT_EQUAL:
3102
0
                                        sCriteria += "!=";
3103
0
                                        break;
3104
0
                                    case css::sdb::SQLFilterOperator::LESS:
3105
0
                                        sCriteria += "<";
3106
0
                                        break;
3107
0
                                    case css::sdb::SQLFilterOperator::GREATER:
3108
0
                                        sCriteria += ">";
3109
0
                                        break;
3110
0
                                    case css::sdb::SQLFilterOperator::LESS_EQUAL:
3111
0
                                        sCriteria += "<=";
3112
0
                                        break;
3113
0
                                    case css::sdb::SQLFilterOperator::GREATER_EQUAL:
3114
0
                                        sCriteria += ">=";
3115
0
                                        break;
3116
0
                                    case css::sdb::SQLFilterOperator::LIKE:
3117
0
                                        sCriteria += "LIKE ";
3118
0
                                        break;
3119
0
                                    case css::sdb::SQLFilterOperator::NOT_LIKE:
3120
0
                                        sCriteria += "NOT LIKE ";
3121
0
                                        break;
3122
0
                                    case css::sdb::SQLFilterOperator::SQLNULL:
3123
0
                                        sCriteria += "IS NULL";
3124
0
                                        break;
3125
0
                                    case css::sdb::SQLFilterOperator::NOT_SQLNULL:
3126
0
                                        sCriteria += "IS NOT NULL";
3127
0
                                        break;
3128
0
                                }
3129
0
                                pParseNode->parseNodeToPredicateStr( sCriteria
3130
0
                                                                    ,xConnection
3131
0
                                                                    ,xFormatter
3132
0
                                                                    ,xField
3133
0
                                                                    ,OUString()
3134
0
                                                                    ,aAppLocale
3135
0
                                                                    ,strDecimalSeparator
3136
0
                                                                    ,getParseContext());
3137
0
                                aRow[rFieldInfo.xText] = sCriteria;
3138
0
                            }
3139
0
                        }
3140
0
                    }
3141
0
                }
3142
0
            }
3143
3144
0
            if (aRow.empty())
3145
0
                continue;
3146
3147
0
            impl_addFilterRow( aRow );
3148
0
        }
3149
0
    }
3150
3151
    // now set the filter controls
3152
0
    for (const auto& rFieldInfo : rFieldInfos)
3153
0
    {
3154
0
        m_aFilterComponents.push_back( rFieldInfo.xText );
3155
0
    }
3156
0
}
3157
3158
3159
void FormController::startFiltering()
3160
0
{
3161
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3162
3163
0
    Reference< XConnection >  xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
3164
0
    if ( !xConnection.is() )
3165
        // nothing to do - can't filter a form which is not connected
3166
0
        return;
3167
3168
    // stop listening for controls
3169
0
    if (isListeningForChanges())
3170
0
        stopListening();
3171
3172
0
    m_bFiltering = true;
3173
3174
    // as we don't want new controls to be attached to the scripting environment
3175
    // we change attach flags
3176
0
    m_bAttachEvents = false;
3177
3178
    // exchanging the controls for the current form
3179
0
    Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3180
0
    const Reference< XControl >* pControls = aControlsCopy.getConstArray();
3181
0
    sal_Int32 nControlCount = aControlsCopy.getLength();
3182
3183
    // the control we have to activate after replacement
3184
0
    Reference< XNumberFormatsSupplier >  xFormatSupplier = getNumberFormats(xConnection, true);
3185
0
    Reference< XNumberFormatter >  xFormatter = NumberFormatter::create(m_xComponentContext);
3186
0
    xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
3187
3188
    // structure for storing the field info
3189
0
    ::std::vector<FmFieldInfo> aFieldInfos;
3190
3191
0
    for (sal_Int32 i = nControlCount; i > 0;)
3192
0
    {
3193
0
        Reference< XControl > xControl = pControls[--i];
3194
0
        if (xControl.is())
3195
0
        {
3196
            // no events for the control anymore
3197
0
            removeFromEventAttacher(xControl);
3198
3199
            // do we have a mode selector
3200
0
            Reference< XModeSelector >  xSelector(xControl, UNO_QUERY);
3201
0
            if (xSelector.is())
3202
0
            {
3203
0
                xSelector->setMode( u"FilterMode"_ustr );
3204
3205
                // listening for new controls of the selector
3206
0
                Reference< XContainer >  xContainer(xSelector, UNO_QUERY);
3207
0
                if (xContainer.is())
3208
0
                    xContainer->addContainerListener(this);
3209
3210
0
                Reference< XEnumerationAccess >  xElementAccess(xSelector, UNO_QUERY);
3211
0
                if (xElementAccess.is())
3212
0
                {
3213
0
                    Reference< XEnumeration >  xEnumeration(xElementAccess->createEnumeration());
3214
0
                    Reference< XControl >  xSubControl;
3215
0
                    while (xEnumeration->hasMoreElements())
3216
0
                    {
3217
0
                        xEnumeration->nextElement() >>= xSubControl;
3218
0
                        if (xSubControl.is())
3219
0
                        {
3220
0
                            Reference< XPropertySet >  xSet(xSubControl->getModel(), UNO_QUERY);
3221
0
                            if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3222
0
                            {
3223
                                // does the model use a bound field ?
3224
0
                                Reference< XPropertySet >  xField;
3225
0
                                xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3226
3227
0
                                Reference< XTextComponent >  xText(xSubControl, UNO_QUERY);
3228
                                // may we filter the field?
3229
0
                                if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
3230
0
                                    ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
3231
0
                                {
3232
0
                                    aFieldInfos.emplace_back(xField, xText);
3233
0
                                    xText->addTextListener(this);
3234
0
                                }
3235
0
                            }
3236
0
                        }
3237
0
                    }
3238
0
                }
3239
0
                continue;
3240
0
            }
3241
3242
0
            Reference< XPropertySet >  xModel( xControl->getModel(), UNO_QUERY );
3243
0
            if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
3244
0
            {
3245
                // does the model use a bound field ?
3246
0
                Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
3247
0
                Reference< XPropertySet >  xField;
3248
0
                aVal >>= xField;
3249
3250
                // may we filter the field?
3251
3252
0
                if  (   xField.is()
3253
0
                    &&  ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3254
0
                    && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3255
0
                    )
3256
0
                {
3257
                    // create a filter control
3258
0
                    Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
3259
0
                        m_xComponentContext,
3260
0
                        getDialogParentWindow(this),
3261
0
                        xFormatter,
3262
0
                        xModel);
3263
3264
0
                    if ( replaceControl( xControl, xFilterControl ) )
3265
0
                    {
3266
0
                        Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
3267
0
                        aFieldInfos.emplace_back( xField, xFilterText );
3268
0
                        xFilterText->addTextListener(this);
3269
0
                    }
3270
0
                }
3271
0
            }
3272
0
            else
3273
0
            {
3274
                // unsubscribe from EventManager
3275
0
            }
3276
0
        }
3277
0
    }
3278
3279
    // we have all filter controls now, so the next step is to read the filters from the form
3280
    // resolve all aliases and set the current filter to the according structure
3281
0
    setFilter(aFieldInfos);
3282
3283
0
    Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
3284
0
    if ( xSet.is() )
3285
0
        stopFormListening( xSet, true );
3286
3287
0
    impl_setTextOnAllFilter_throw();
3288
3289
    // lock all controls which are not used for filtering
3290
0
    m_bLocked = determineLockState();
3291
0
    setLocks();
3292
0
    m_bAttachEvents = true;
3293
0
}
3294
3295
3296
void FormController::stopFiltering()
3297
0
{
3298
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3299
0
    if ( !m_bFiltering ) // #104693# OJ
3300
0
    {   // nothing to do
3301
0
        return;
3302
0
    }
3303
3304
0
    m_bFiltering = false;
3305
0
    m_bDetachEvents = false;
3306
3307
0
    ::comphelper::disposeComponent(m_xComposer);
3308
3309
    // exchanging the controls for the current form
3310
0
    Sequence< Reference< XControl > > aControlsCopy( m_aControls );
3311
0
    const Reference< XControl > * pControls = aControlsCopy.getConstArray();
3312
0
    sal_Int32 nControlCount = aControlsCopy.getLength();
3313
3314
    // clear the filter control map
3315
0
    ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
3316
0
    m_aFilterComponents.clear();
3317
3318
0
    for ( sal_Int32 i = nControlCount; i > 0; )
3319
0
    {
3320
0
        Reference< XControl > xControl = pControls[--i];
3321
0
        if (xControl.is())
3322
0
        {
3323
            // now enable event handling again
3324
0
            addToEventAttacher(xControl);
3325
3326
0
            Reference< XModeSelector >  xSelector(xControl, UNO_QUERY);
3327
0
            if (xSelector.is())
3328
0
            {
3329
0
                xSelector->setMode( u"DataMode"_ustr );
3330
3331
                // listening for new controls of the selector
3332
0
                Reference< XContainer >  xContainer(xSelector, UNO_QUERY);
3333
0
                if (xContainer.is())
3334
0
                    xContainer->removeContainerListener(this);
3335
0
                continue;
3336
0
            }
3337
3338
0
            Reference< XPropertySet >  xSet(xControl->getModel(), UNO_QUERY);
3339
0
            if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
3340
0
            {
3341
                // does the model use a bound field ?
3342
0
                Reference< XPropertySet >  xField;
3343
0
                xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3344
3345
                // may we filter the field?
3346
0
                if  (   xField.is()
3347
0
                    &&  ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
3348
0
                    &&  ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
3349
0
                    )
3350
0
                {
3351
0
                    OUString sServiceName;
3352
0
                    OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
3353
0
                    Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
3354
0
                    replaceControl( xControl, xNewControl );
3355
0
                }
3356
0
            }
3357
0
        }
3358
0
    }
3359
3360
0
    Reference< XPropertySet >  xSet( m_xModelAsIndex, UNO_QUERY );
3361
0
    if ( xSet.is() )
3362
0
        startFormListening( xSet, true );
3363
3364
0
    m_bDetachEvents = true;
3365
3366
0
    m_aFilterRows.clear();
3367
0
    m_nCurrentFilterPosition = -1;
3368
3369
    // release the locks if possible
3370
    // lock all controls which are not used for filtering
3371
0
    m_bLocked = determineLockState();
3372
0
    setLocks();
3373
3374
    // restart listening for control modifications
3375
0
    if (isListeningForChanges())
3376
0
        startListening();
3377
0
}
3378
3379
// XModeSelector
3380
3381
void FormController::setMode(const OUString& Mode)
3382
0
{
3383
0
    ::osl::MutexGuard aGuard( m_aMutex );
3384
0
    impl_checkDisposed_throw();
3385
3386
0
    if (!supportsMode(Mode))
3387
0
        throw NoSupportException();
3388
3389
0
    if (Mode == m_aMode)
3390
0
        return;
3391
3392
0
    m_aMode = Mode;
3393
3394
0
    if ( Mode == "FilterMode" )
3395
0
        startFiltering();
3396
0
    else
3397
0
        stopFiltering();
3398
3399
0
    for (const auto& rChild : m_aChildren)
3400
0
    {
3401
0
        Reference< XModeSelector > xMode(rChild, UNO_QUERY);
3402
0
        if ( xMode.is() )
3403
0
            xMode->setMode(Mode);
3404
0
    }
3405
0
}
3406
3407
3408
OUString SAL_CALL FormController::getMode()
3409
0
{
3410
0
    ::osl::MutexGuard aGuard( m_aMutex );
3411
0
    impl_checkDisposed_throw();
3412
3413
0
    return m_aMode;
3414
0
}
3415
3416
3417
Sequence< OUString > SAL_CALL FormController::getSupportedModes()
3418
0
{
3419
0
    ::osl::MutexGuard aGuard( m_aMutex );
3420
0
    impl_checkDisposed_throw();
3421
3422
0
    static Sequence< OUString > const aModes
3423
0
    {
3424
0
        u"DataMode"_ustr,
3425
0
        u"FilterMode"_ustr
3426
0
    };
3427
0
    return aModes;
3428
0
}
3429
3430
sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
3431
0
{
3432
0
    ::osl::MutexGuard aGuard( m_aMutex );
3433
0
    impl_checkDisposed_throw();
3434
3435
0
    Sequence< OUString > aModes(getSupportedModes());
3436
0
    return comphelper::findValue(aModes, Mode) != -1;
3437
0
}
3438
3439
css::uno::Reference<css::awt::XWindow> FormController::getDialogParentWindow(const css::uno::Reference<css::form::runtime::XFormController> & xFormController)
3440
0
{
3441
0
    try
3442
0
    {
3443
0
        Reference< XControl > xContainerControl( xFormController->getContainer(), UNO_QUERY_THROW );
3444
0
        Reference<XWindow> xContainerWindow(xContainerControl->getPeer(), UNO_QUERY_THROW);
3445
0
        return xContainerWindow;
3446
0
    }
3447
0
    catch( const Exception& )
3448
0
    {
3449
0
        DBG_UNHANDLED_EXCEPTION("svx");
3450
0
    }
3451
0
    return nullptr;
3452
0
}
3453
3454
bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
3455
0
{
3456
0
    try
3457
0
    {
3458
0
        Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
3459
0
        Reference< XEnumeration > xControlEnumeration;
3460
0
        if ( xControlEnumAcc.is() )
3461
0
            xControlEnumeration = xControlEnumAcc->createEnumeration();
3462
0
        OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
3463
0
        if ( !xControlEnumeration.is() )
3464
            // assume all valid
3465
0
            return true;
3466
3467
0
        Reference< XValidatableFormComponent > xValidatable;
3468
0
        while ( xControlEnumeration->hasMoreElements() )
3469
0
        {
3470
0
            if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
3471
                // control does not support validation
3472
0
                continue;
3473
3474
0
            if ( xValidatable->isValid() )
3475
0
                continue;
3476
3477
0
            Reference< XValidator > xValidator( xValidatable->getValidator() );
3478
0
            OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
3479
0
            if ( !xValidator.is() )
3480
                // this violates the interface definition of css.form.validation.XValidatableFormComponent ...
3481
0
                continue;
3482
3483
0
            _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
3484
0
            _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
3485
0
            return false;
3486
0
        }
3487
0
    }
3488
0
    catch( const Exception& )
3489
0
    {
3490
0
        DBG_UNHANDLED_EXCEPTION("svx");
3491
0
    }
3492
0
    return true;
3493
0
}
3494
3495
3496
Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
3497
0
{
3498
0
    try
3499
0
    {
3500
0
        const Sequence< Reference< XControl > > aControls( getControls() );
3501
3502
0
        for ( auto const & control : aControls )
3503
0
        {
3504
0
            OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
3505
0
            if ( control.is() )
3506
0
            {
3507
0
                if ( control->getModel() == _rxModel )
3508
0
                    return control;
3509
0
            }
3510
0
        }
3511
0
        OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
3512
0
    }
3513
0
    catch( const Exception& )
3514
0
    {
3515
0
        DBG_UNHANDLED_EXCEPTION("svx");
3516
0
    }
3517
0
    return nullptr;
3518
0
}
3519
3520
3521
namespace
3522
{
3523
    void displayErrorSetFocus(const OUString& _rMessage, const Reference<XControl>& _rxFocusControl,
3524
                              const css::uno::Reference<css::awt::XWindow>& rDialogParent)
3525
0
    {
3526
0
        SQLContext aError(SvxResId(RID_STR_WRITEERROR), {}, {}, 0, {}, _rMessage);
3527
0
        displayException(aError, rDialogParent);
3528
3529
0
        if ( _rxFocusControl.is() )
3530
0
        {
3531
0
            Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
3532
0
            OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
3533
0
            if ( xControlWindow.is() )
3534
0
                xControlWindow->setFocus();
3535
0
        }
3536
0
    }
3537
3538
    bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
3539
0
    {
3540
0
        try
3541
0
        {
3542
0
            static constexpr OUString s_sFormsCheckRequiredFields = u"FormsCheckRequiredFields"_ustr;
3543
3544
            // first, check whether the form has a property telling us the answer
3545
            // this allows people to use the XPropertyContainer interface of a form to control
3546
            // the behaviour on a per-form basis.
3547
0
            Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
3548
0
            Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
3549
0
            if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
3550
0
            {
3551
0
                bool bShouldValidate = true;
3552
0
                OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3553
0
                return bShouldValidate;
3554
0
            }
3555
3556
            // next, check the data source which created the connection
3557
0
            Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
3558
0
            Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
3559
0
            if ( !xDataSource.is() )
3560
                // seldom (but possible): this is not a connection created by a data source
3561
0
                return true;
3562
3563
0
            Reference< XPropertySet > xDataSourceSettings(
3564
0
                xDataSource->getPropertyValue(u"Settings"_ustr),
3565
0
                UNO_QUERY_THROW );
3566
3567
0
            bool bShouldValidate = true;
3568
0
            OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
3569
0
            return bShouldValidate;
3570
0
        }
3571
0
        catch( const Exception& )
3572
0
        {
3573
0
            DBG_UNHANDLED_EXCEPTION("svx");
3574
0
        }
3575
3576
0
        return true;
3577
0
    }
3578
}
3579
3580
// XRowSetApproveListener
3581
3582
sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
3583
0
{
3584
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
3585
0
    impl_checkDisposed_throw();
3586
3587
0
    ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
3588
0
    bool bValid = true;
3589
0
    if (aIter.hasMoreElements())
3590
0
    {
3591
0
        RowChangeEvent aEvt( _rEvent );
3592
0
        aEvt.Source = *this;
3593
0
        bValid = aIter.next()->approveRowChange(aEvt);
3594
0
    }
3595
3596
0
    if ( !bValid )
3597
0
        return bValid;
3598
3599
0
    if  (   ( _rEvent.Action != RowChangeAction::INSERT )
3600
0
        &&  ( _rEvent.Action != RowChangeAction::UPDATE )
3601
0
        )
3602
0
        return bValid;
3603
3604
    // if some of the control models are bound to validators, check them
3605
0
    OUString sInvalidityExplanation;
3606
0
    Reference< XControlModel > xInvalidModel;
3607
0
    if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
3608
0
    {
3609
0
        Reference< XControl > xControl( locateControl( xInvalidModel ) );
3610
0
        aGuard.clear();
3611
0
        displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow(this) );
3612
0
        return false;
3613
0
    }
3614
3615
    // check values on NULL and required flag
3616
0
    if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
3617
0
        return true;
3618
3619
0
    OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
3620
0
    if (!m_pColumnInfoCache)
3621
0
        return true;
3622
3623
0
    try
3624
0
    {
3625
0
        if ( !m_pColumnInfoCache->controlsInitialized() )
3626
0
            m_pColumnInfoCache->initializeControls( getControls() );
3627
3628
0
        size_t colCount = m_pColumnInfoCache->getColumnCount();
3629
0
        for ( size_t col = 0; col < colCount; ++col )
3630
0
        {
3631
0
            const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
3632
3633
0
            if ( rColInfo.bAutoIncrement )
3634
0
                continue;
3635
3636
0
            if ( rColInfo.bReadOnly )
3637
0
                continue;
3638
3639
0
            if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
3640
0
            {
3641
0
                continue;
3642
0
            }
3643
3644
            // TODO: in case of binary fields, this "getString" below is extremely expensive
3645
0
            if ( !rColInfo.xColumn->getString().isEmpty() || !rColInfo.xColumn->wasNull() )
3646
0
                continue;
3647
3648
0
            OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
3649
0
            sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
3650
3651
            // the control to focus
3652
0
            Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
3653
0
            if ( !xControl.is() )
3654
0
                xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
3655
3656
0
            aGuard.clear();
3657
0
            displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow(this) );
3658
0
            return false;
3659
0
        }
3660
0
    }
3661
0
    catch( const Exception& )
3662
0
    {
3663
0
        DBG_UNHANDLED_EXCEPTION("svx");
3664
0
    }
3665
3666
0
    return true;
3667
0
}
3668
3669
3670
sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
3671
0
{
3672
0
    ::osl::MutexGuard aGuard( m_aMutex );
3673
0
    impl_checkDisposed_throw();
3674
3675
0
    ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
3676
0
    if (aIter.hasMoreElements())
3677
0
    {
3678
0
        EventObject aEvt(event);
3679
0
        aEvt.Source = *this;
3680
0
        return aIter.next()->approveCursorMove(aEvt);
3681
0
    }
3682
3683
0
    return true;
3684
0
}
3685
3686
3687
sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
3688
0
{
3689
0
    ::osl::MutexGuard aGuard( m_aMutex );
3690
0
    impl_checkDisposed_throw();
3691
3692
0
    ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
3693
0
    if (aIter.hasMoreElements())
3694
0
    {
3695
0
        EventObject aEvt(event);
3696
0
        aEvt.Source = *this;
3697
0
        return aIter.next()->approveRowSetChange(aEvt);
3698
0
    }
3699
3700
0
    return true;
3701
0
}
3702
3703
// XRowSetApproveBroadcaster
3704
3705
void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3706
0
{
3707
0
    ::osl::MutexGuard aGuard( m_aMutex );
3708
0
    impl_checkDisposed_throw();
3709
3710
0
    m_aRowSetApproveListeners.addInterface(_rxListener);
3711
0
}
3712
3713
3714
void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
3715
0
{
3716
0
    ::osl::MutexGuard aGuard( m_aMutex );
3717
0
    impl_checkDisposed_throw();
3718
3719
0
    m_aRowSetApproveListeners.removeInterface(_rxListener);
3720
0
}
3721
3722
// XErrorListener
3723
3724
void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
3725
0
{
3726
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
3727
0
    impl_checkDisposed_throw();
3728
3729
0
    ::comphelper::OInterfaceIteratorHelper3 aIter(m_aErrorListeners);
3730
0
    if (aIter.hasMoreElements())
3731
0
    {
3732
0
        SQLErrorEvent aEvt(aEvent);
3733
0
        aEvt.Source = *this;
3734
0
        aIter.next()->errorOccured(aEvt);
3735
0
    }
3736
0
    else
3737
0
    {
3738
0
        aGuard.clear();
3739
0
        displayException(aEvent, getDialogParentWindow(this));
3740
0
    }
3741
0
}
3742
3743
// XErrorBroadcaster
3744
3745
void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3746
0
{
3747
0
    ::osl::MutexGuard aGuard( m_aMutex );
3748
0
    impl_checkDisposed_throw();
3749
3750
0
    m_aErrorListeners.addInterface(aListener);
3751
0
}
3752
3753
3754
void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
3755
0
{
3756
0
    ::osl::MutexGuard aGuard( m_aMutex );
3757
0
    impl_checkDisposed_throw();
3758
3759
0
    m_aErrorListeners.removeInterface(aListener);
3760
0
}
3761
3762
// XDatabaseParameterBroadcaster2
3763
3764
void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3765
0
{
3766
0
    ::osl::MutexGuard aGuard( m_aMutex );
3767
0
    impl_checkDisposed_throw();
3768
3769
0
    m_aParameterListeners.addInterface(aListener);
3770
0
}
3771
3772
3773
void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3774
0
{
3775
0
    ::osl::MutexGuard aGuard( m_aMutex );
3776
0
    impl_checkDisposed_throw();
3777
3778
0
    m_aParameterListeners.removeInterface(aListener);
3779
0
}
3780
3781
// XDatabaseParameterBroadcaster
3782
3783
void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3784
0
{
3785
0
    FormController::addDatabaseParameterListener( aListener );
3786
0
}
3787
3788
3789
void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
3790
0
{
3791
0
    FormController::removeDatabaseParameterListener( aListener );
3792
0
}
3793
3794
// XDatabaseParameterListener
3795
3796
sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
3797
0
{
3798
0
    SolarMutexGuard aSolarGuard;
3799
0
    ::osl::MutexGuard aGuard( m_aMutex );
3800
0
    impl_checkDisposed_throw();
3801
3802
0
    ::comphelper::OInterfaceIteratorHelper3 aIter(m_aParameterListeners);
3803
0
    if (aIter.hasMoreElements())
3804
0
    {
3805
0
        DatabaseParameterEvent aEvt(aEvent);
3806
0
        aEvt.Source = *this;
3807
0
        return aIter.next()->approveParameter(aEvt);
3808
0
    }
3809
0
    else
3810
0
    {
3811
        // default handling: instantiate an interaction handler and let it handle the parameter request
3812
0
        try
3813
0
        {
3814
0
            if ( !ensureInteractionHandler() )
3815
0
                return false;
3816
3817
            // two continuations allowed: OK and Cancel
3818
0
            rtl::Reference<OParameterContinuation> pParamValues = new OParameterContinuation;
3819
0
            rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
3820
            // the request
3821
0
            ParametersRequest aRequest;
3822
0
            aRequest.Parameters = aEvent.Parameters;
3823
0
            aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
3824
0
            rtl::Reference<OInteractionRequest> pParamRequest = new OInteractionRequest(Any(aRequest));
3825
            // some knittings
3826
0
            pParamRequest->addContinuation(pParamValues);
3827
0
            pParamRequest->addContinuation(pAbort);
3828
3829
            // handle the request
3830
0
            m_xInteractionHandler->handle(pParamRequest);
3831
3832
0
            if (!pParamValues->wasSelected())
3833
                // canceled
3834
0
                return false;
3835
3836
            // transfer the values into the parameter supplier
3837
0
            Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
3838
0
            if (aFinalValues.getLength() != aRequest.Parameters->getCount())
3839
0
            {
3840
0
                OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
3841
0
                return false;
3842
0
            }
3843
0
            const PropertyValue* pFinalValues = aFinalValues.getConstArray();
3844
0
            for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
3845
0
            {
3846
0
                Reference< XPropertySet > xParam(
3847
0
                    aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
3848
0
                if (xParam.is())
3849
0
                {
3850
#ifdef DBG_UTIL
3851
                    OUString sName;
3852
                    xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
3853
                    DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
3854
#endif
3855
0
                    try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
3856
0
                    catch(Exception&)
3857
0
                    {
3858
0
                        OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
3859
0
                    }
3860
0
                }
3861
0
            }
3862
0
        }
3863
0
        catch(Exception&)
3864
0
        {
3865
0
            DBG_UNHANDLED_EXCEPTION("svx");
3866
0
        }
3867
0
    }
3868
0
    return true;
3869
0
}
3870
3871
// XConfirmDeleteBroadcaster
3872
3873
void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3874
0
{
3875
0
    ::osl::MutexGuard aGuard( m_aMutex );
3876
0
    impl_checkDisposed_throw();
3877
3878
0
    m_aDeleteListeners.addInterface(aListener);
3879
0
}
3880
3881
3882
void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
3883
0
{
3884
0
    ::osl::MutexGuard aGuard( m_aMutex );
3885
0
    impl_checkDisposed_throw();
3886
3887
0
    m_aDeleteListeners.removeInterface(aListener);
3888
0
}
3889
3890
// XConfirmDeleteListener
3891
3892
sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
3893
0
{
3894
0
    ::osl::MutexGuard aGuard( m_aMutex );
3895
0
    impl_checkDisposed_throw();
3896
3897
0
    ::comphelper::OInterfaceIteratorHelper3 aIter(m_aDeleteListeners);
3898
0
    if (aIter.hasMoreElements())
3899
0
    {
3900
0
        RowChangeEvent aEvt(aEvent);
3901
0
        aEvt.Source = *this;
3902
0
        return aIter.next()->confirmDelete(aEvt);
3903
0
    }
3904
    // default handling: instantiate an interaction handler and let it handle the request
3905
3906
0
    OUString sTitle;
3907
0
    sal_Int32 nLength = aEvent.Rows;
3908
0
    if ( nLength > 1 )
3909
0
    {
3910
0
        sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
3911
0
        sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
3912
0
    }
3913
0
    else
3914
0
        sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
3915
3916
0
    try
3917
0
    {
3918
0
        if ( !ensureInteractionHandler() )
3919
0
            return false;
3920
3921
        // two continuations allowed: Yes and No
3922
0
        rtl::Reference<OInteractionApprove> pApprove = new OInteractionApprove;
3923
0
        rtl::Reference<OInteractionDisapprove> pDisapprove = new OInteractionDisapprove;
3924
3925
        // the request
3926
0
        SQLWarning aDetails(SvxResId(RID_STR_DELETECONFIRM), {}, {}, 0, {});
3927
0
        SQLWarning aWarning(sTitle, {}, {}, 0, css::uno::Any(aDetails));
3928
3929
0
        rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( Any( aWarning ) );
3930
3931
        // some knittings
3932
0
        pRequest->addContinuation( pApprove );
3933
0
        pRequest->addContinuation( pDisapprove );
3934
3935
        // handle the request
3936
0
        m_xInteractionHandler->handle( pRequest );
3937
3938
0
        if ( pApprove->wasSelected() )
3939
0
            return true;
3940
0
    }
3941
0
    catch( const Exception& )
3942
0
    {
3943
0
        DBG_UNHANDLED_EXCEPTION("svx");
3944
0
    }
3945
3946
0
    return false;
3947
0
}
3948
3949
3950
void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
3951
0
{
3952
0
    ::osl::MutexGuard aGuard( m_aMutex );
3953
    // for now, just copy the ids of the features, because...
3954
0
    m_aInvalidFeatures.insert( Features.begin(), Features.end() );
3955
3956
    // ... we will do the real invalidation asynchronously
3957
0
    if ( !m_aFeatureInvalidationTimer.IsActive() )
3958
0
        m_aFeatureInvalidationTimer.Start();
3959
0
}
3960
3961
3962
void SAL_CALL FormController::invalidateAllFeatures(  )
3963
0
{
3964
0
    ::osl::ClearableMutexGuard aGuard( m_aMutex );
3965
3966
0
    Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
3967
3968
0
    aGuard.clear();
3969
0
    if ( aInterceptedFeatures.hasElements() )
3970
0
        invalidateFeatures( aInterceptedFeatures );
3971
0
}
3972
3973
3974
Reference< XDispatch >
3975
FormController::interceptedQueryDispatch( const URL& aURL,
3976
                                            const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
3977
0
{
3978
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
3979
0
    Reference< XDispatch >  xReturn;
3980
    // dispatches handled by ourself
3981
0
    if  (   ( aURL.Complete == FMURL_CONFIRM_DELETION )
3982
0
        ||  (   ( aURL.Complete == "private:/InteractionHandler" )
3983
0
            &&  ensureInteractionHandler()
3984
0
            )
3985
0
        )
3986
0
        xReturn = static_cast< XDispatch* >( this );
3987
3988
    // dispatches of FormSlot-URLs we have to translate
3989
0
    if ( !xReturn.is() && m_xFormOperations.is() )
3990
0
    {
3991
        // find the slot id which corresponds to the URL
3992
0
        sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
3993
0
        sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
3994
0
        if ( nFormFeature > 0 )
3995
0
        {
3996
            // get the dispatcher for this feature, create if necessary
3997
0
            DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
3998
0
            if ( aDispatcherPos == m_aFeatureDispatchers.end() )
3999
0
            {
4000
0
                aDispatcherPos = m_aFeatureDispatchers.emplace(
4001
0
                    nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
4002
0
                ).first;
4003
0
            }
4004
4005
0
            OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
4006
0
            return aDispatcherPos->second;
4007
0
        }
4008
0
    }
4009
4010
    // no more to offer
4011
0
    return xReturn;
4012
0
}
4013
4014
4015
void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
4016
0
{
4017
0
    if ( _rArgs.getLength() != 1 )
4018
0
    {
4019
0
        OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
4020
0
        return;
4021
0
    }
4022
4023
0
    if ( _rURL.Complete == "private:/InteractionHandler" )
4024
0
    {
4025
0
        Reference< XInteractionRequest > xRequest;
4026
0
        OSL_VERIFY( _rArgs[0].Value >>= xRequest );
4027
0
        if ( xRequest.is() )
4028
0
            handle( xRequest );
4029
0
        return;
4030
0
    }
4031
4032
0
    if  ( _rURL.Complete == FMURL_CONFIRM_DELETION )
4033
0
    {
4034
0
        OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
4035
            // confirmDelete has a return value - dispatch hasn't
4036
0
        return;
4037
0
    }
4038
4039
0
    OSL_FAIL( "FormController::dispatch: unknown URL!" );
4040
0
}
4041
4042
4043
void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
4044
0
{
4045
0
    if (_rURL.Complete == FMURL_CONFIRM_DELETION)
4046
0
    {
4047
0
        if (_rxListener.is())
4048
0
        {   // send an initial statusChanged event
4049
0
            FeatureStateEvent aEvent;
4050
0
            aEvent.FeatureURL = _rURL;
4051
0
            aEvent.IsEnabled = true;
4052
0
            _rxListener->statusChanged(aEvent);
4053
            // and don't add the listener at all (the status will never change)
4054
0
        }
4055
0
    }
4056
0
    else
4057
0
        OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
4058
0
}
4059
4060
4061
Reference< XInterface > SAL_CALL FormController::getParent()
4062
0
{
4063
0
    return m_xParent;
4064
0
}
4065
4066
4067
void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
4068
0
{
4069
0
    m_xParent = Parent;
4070
0
}
4071
4072
4073
void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
4074
0
{
4075
0
    OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
4076
    // we never really added the listener, so we don't need to remove it
4077
0
}
4078
4079
4080
Reference< XDispatchProviderInterceptor >  FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4081
0
{
4082
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4083
#ifdef DBG_UTIL
4084
    // check if we already have an interceptor for the given object
4085
    for ( const auto & it : m_aControlDispatchInterceptors )
4086
    {
4087
        if (it->getIntercepted() == _xInterception)
4088
            OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
4089
    }
4090
#endif
4091
4092
0
    rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
4093
0
    m_aControlDispatchInterceptors.push_back( pInterceptor );
4094
4095
0
    return pInterceptor;
4096
0
}
4097
4098
4099
bool FormController::ensureInteractionHandler()
4100
0
{
4101
0
    if ( m_xInteractionHandler.is() )
4102
0
        return true;
4103
0
    if ( m_bAttemptedHandlerCreation )
4104
0
        return false;
4105
0
    m_bAttemptedHandlerCreation = true;
4106
4107
0
    m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
4108
0
                                                                 getDialogParentWindow(this));
4109
0
    return m_xInteractionHandler.is();
4110
0
}
4111
4112
4113
void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
4114
0
{
4115
0
    if ( !ensureInteractionHandler() )
4116
0
        return;
4117
0
    m_xInteractionHandler->handle( _rRequest );
4118
0
}
4119
4120
4121
void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
4122
0
{
4123
0
    OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
4124
    // search the interceptor responsible for the given object
4125
0
    auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
4126
0
        [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
4127
0
            return rpInterceptor->getIntercepted() == _xInterception;
4128
0
        });
4129
0
    if (aIter != m_aControlDispatchInterceptors.end())
4130
0
    {
4131
        // log off the interception from its interception object
4132
0
        (*aIter)->dispose();
4133
        // remove the interceptor from our array
4134
0
        m_aControlDispatchInterceptors.erase(aIter);
4135
0
    }
4136
0
}
4137
4138
4139
void FormController::implInvalidateCurrentControlDependentFeatures()
4140
0
{
4141
0
    Sequence< sal_Int16 > aCurrentControlDependentFeatures
4142
0
    {
4143
0
        FormFeature::SortAscending,
4144
0
        FormFeature::SortDescending,
4145
0
        FormFeature::AutoFilter,
4146
0
        FormFeature::RefreshCurrentControl
4147
0
    };
4148
4149
0
    invalidateFeatures( aCurrentControlDependentFeatures );
4150
0
}
4151
4152
4153
void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
4154
0
{
4155
0
    implInvalidateCurrentControlDependentFeatures();
4156
0
}
4157
4158
}
4159
4160
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */