Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svx/source/form/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 141 kB image not shown  

Quelle  fmshimp.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <o3tl/safeint.hxx>
#include <sal/macros.h>
#include <sal/log.hxx>
#include <fmobj.hxx>
#include <fmpgeimp.hxx>
#include <svx/fmtools.hxx>
#include <fmprop.hxx>
#include <fmservs.hxx>
#include <fmshimp.hxx>
#include <fmtextcontrolshell.hxx>
#include <fmundo.hxx>
#include <fmurl.hxx>
#include <fmvwimp.hxx>
#include <gridcols.hxx>
#include <svx/svditer.hxx>
#include <svx/dialmgr.hxx>
#include <svx/strings.hrc>
#include <svx/svdobjkind.hxx>
#include <svx/fmmodel.hxx>
#include <svx/fmpage.hxx>
#include <svx/fmshell.hxx>
#include <svx/fmview.hxx>
#include <svx/obj3d.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svxdlg.hxx>
#include <svx/svxids.hrc>
#include <bitmaps.hlst>
#include <formnavi.hrc>

#include <com/sun/star/awt/XWindow2.hpp>
#include <com/sun/star/awt/XCheckBox.hpp>
#include <com/sun/star/awt/XListBox.hpp>
#include <com/sun/star/awt/XTextComponent.hpp>
#include <com/sun/star/beans/theIntrospection.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/container/XContainer.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/form/ListSourceType.hpp>
#include <com/sun/star/form/TabOrderDialog.hpp>
#include <com/sun/star/form/XGrid.hpp>
#include <com/sun/star/form/XGridPeer.hpp>
#include <com/sun/star/form/XLoadable.hpp>
#include <com/sun/star/form/XReset.hpp>
#include <com/sun/star/form/binding/XBindableValue.hpp>
#include <com/sun/star/form/binding/XListEntrySink.hpp>
#include <com/sun/star/frame/FrameSearchFlag.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/script/XEventAttacherManager.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
#include <com/sun/star/util/XModeSelector.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>

#include <comphelper/evtmethodhelper.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/property.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/solarmutex.hxx>
#include <comphelper/string.hxx>
#include <comphelper/types.hxx>
#include <connectivity/dbtools.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/frame.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/viewsh.hxx>
#include <toolkit/helper/vclunohelper.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/configuration.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/window.hxx>

#include <algorithm>
#include <map>
#include <memory>
#include <string_view>
#include <vector>

// is used for Invalidate -> maintain it as well
const sal_uInt16 DatabaseSlotMap[] =
{
    SID_FM_RECORD_FIRST,
    SID_FM_RECORD_NEXT,
    SID_FM_RECORD_PREV,
    SID_FM_RECORD_LAST,
    SID_FM_RECORD_NEW,
    SID_FM_RECORD_DELETE,
    SID_FM_RECORD_ABSOLUTE,
    SID_FM_RECORD_TOTAL,
    SID_FM_RECORD_SAVE,
    SID_FM_RECORD_UNDO,
    SID_FM_REMOVE_FILTER_SORT,
    SID_FM_SORTUP,
    SID_FM_SORTDOWN,
    SID_FM_ORDERCRIT,
    SID_FM_AUTOFILTER,
    SID_FM_FORM_FILTERED,
    SID_FM_REFRESH,
    SID_FM_REFRESH_FORM_CONTROL,
    SID_FM_SEARCH,
    SID_FM_FILTER_START,
    SID_FM_VIEW_AS_GRID,
    0
};

// is used for Invalidate -> maintain it as well
// sort ascending !!!!!!
const sal_uInt16 DlgSlotMap[] =    // slots of the controller
{
    SID_FM_CTL_PROPERTIES,
    SID_FM_PROPERTIES,
    SID_FM_TAB_DIALOG,
    SID_FM_ADD_FIELD,
    SID_FM_SHOW_FMEXPLORER,
    SID_FM_FIELDS_CONTROL,
    SID_FM_SHOW_PROPERTIES,
    SID_FM_PROPERTY_CONTROL,
    SID_FM_FMEXPLORER_CONTROL,
    SID_FM_SHOW_DATANAVIGATOR,
    SID_FM_DATANAVIGATOR_CONTROL,
    0
};

const sal_uInt16 SelObjectSlotMap[] =  // slots depending on the SelObject
{
    SID_FM_CONVERTTO_EDIT,
    SID_FM_CONVERTTO_BUTTON,
    SID_FM_CONVERTTO_FIXEDTEXT,
    SID_FM_CONVERTTO_LISTBOX,
    SID_FM_CONVERTTO_CHECKBOX,
    SID_FM_CONVERTTO_RADIOBUTTON,
    SID_FM_CONVERTTO_GROUPBOX,
    SID_FM_CONVERTTO_COMBOBOX,
    SID_FM_CONVERTTO_IMAGEBUTTON,
    SID_FM_CONVERTTO_FILECONTROL,
    SID_FM_CONVERTTO_DATE,
    SID_FM_CONVERTTO_TIME,
    SID_FM_CONVERTTO_NUMERIC,
    SID_FM_CONVERTTO_CURRENCY,
    SID_FM_CONVERTTO_PATTERN,
    SID_FM_CONVERTTO_IMAGECONTROL,
    SID_FM_CONVERTTO_FORMATTED,
    SID_FM_CONVERTTO_SCROLLBAR,
    SID_FM_CONVERTTO_SPINBUTTON,
    SID_FM_CONVERTTO_NAVIGATIONBAR,

    SID_FM_FMEXPLORER_CONTROL,
    SID_FM_DATANAVIGATOR_CONTROL,

    0
};

// the following arrays must be consistent, i.e., corresponding entries should
// be at the same relative position within their respective arrays
static std::u16string_view aConvertSlots[] =
{
    u"ConvertToEdit",
    u"ConvertToButton",
    u"ConvertToFixed",
    u"ConvertToList",
    u"ConvertToCheckBox",
    u"ConvertToRadio",
    u"ConvertToGroup",
    u"ConvertToCombo",
    u"ConvertToImageBtn",
    u"ConvertToFileControl",
    u"ConvertToDate",
    u"ConvertToTime",
    u"ConvertToNumeric",
    u"ConvertToCurrency",
    u"ConvertToPattern",
    u"ConvertToImageControl",
    u"ConvertToFormatted",
    u"ConvertToScrollBar",
    u"ConvertToSpinButton",
    u"ConvertToNavigationBar"
};

constexpr OUString aImgIds[] =
{
    RID_SVXBMP_EDITBOX,
    RID_SVXBMP_BUTTON,
    RID_SVXBMP_FIXEDTEXT,
    RID_SVXBMP_LISTBOX,
    RID_SVXBMP_CHECKBOX,
    RID_SVXBMP_RADIOBUTTON,
    RID_SVXBMP_GROUPBOX,
    RID_SVXBMP_COMBOBOX,
    RID_SVXBMP_IMAGEBUTTON,
    RID_SVXBMP_FILECONTROL,
    RID_SVXBMP_DATEFIELD,
    RID_SVXBMP_TIMEFIELD,
    RID_SVXBMP_NUMERICFIELD,
    RID_SVXBMP_CURRENCYFIELD,
    RID_SVXBMP_PATTERNFIELD,
    RID_SVXBMP_IMAGECONTROL,
    RID_SVXBMP_FORMATTEDFIELD,
    RID_SVXBMP_SCROLLBAR,
    RID_SVXBMP_SPINBUTTON,
    RID_SVXBMP_NAVIGATIONBAR
};

const SdrObjKind nObjectTypes[] =
{
    SdrObjKind::FormEdit,
    SdrObjKind::FormButton,
    SdrObjKind::FormFixedText,
    SdrObjKind::FormListbox,
    SdrObjKind::FormCheckbox,
    SdrObjKind::FormRadioButton,
    SdrObjKind::FormGroupBox,
    SdrObjKind::FormCombobox,
    SdrObjKind::FormImageButton,
    SdrObjKind::FormFileControl,
    SdrObjKind::FormDateField,
    SdrObjKind::FormTimeField,
    SdrObjKind::FormNumericField,
    SdrObjKind::FormCurrencyField,
    SdrObjKind::FormPatternField,
    SdrObjKind::FormImageControl,
    SdrObjKind::FormFormattedField,
    SdrObjKind::FormScrollbar,
    SdrObjKind::FormSpinButton,
    SdrObjKind::FormNavigationBar
};

using namespace ::com::sun::star;
using namespace ::com::sun::star::ui;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::sdbcx;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::form;
using namespace ::com::sun::star::form::binding;
using namespace ::com::sun::star::form::runtime;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::view;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::script;
using namespace ::svxform;
using namespace ::svx;
using namespace ::dbtools;


//= helper

namespace
{

    void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag&&nbsp;/* [out] */ _rInterfaces )
    {
        _rInterfaces.clear();

        const size_t nMarkCount = _rMarkList.GetMarkCount();
        for ( size_t i = 0; i < nMarkCount; ++i)
        {
            SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
            assert(pCurrent && "marked object will exist");

            std::optional<SdrObjListIter> oGroupIterator;
            if ( pCurrent->IsGroupObject() )
            {
                oGroupIterator.emplace( pCurrent->GetSubList() );
                pCurrent = oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
            }

            while ( pCurrent )
            {
                FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
                    // note this will de-reference virtual objects, if necessary/possible
                if ( pAsFormObject )
                {
                    Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
                        // the UNO_QUERY is important for normalization
                    if ( xControlModel.is() )
                        _rInterfaces.insert( xControlModel );
                }

                // next element
                pCurrent = oGroupIterator && oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
            }
        }
    }


    sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
    {
        try
        {
            if (rColumns.is())
            {
                // loop through all columns
                sal_Int32 i;
                Reference< XPropertySet> xCur;
                for (i=0; i<rColumns->getCount(); ++i)
                {
                    rColumns->getByIndex(i) >>= xCur;
                    if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
                    {
                        // for every visible col : if nViewPos is greater zero, decrement it, else we
                        // have found the model position
                        if (!nViewPos)
                            break;
                        else
                            --nViewPos;
                    }
                }
                if (i<rColumns->getCount())
                    return i;
            }
        }
        catch(const Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("svx");
        }
        return -1;
    }


    void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
        const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
    {
        // first check if we have a XEventAttacherManager for the model
        Reference< XChild> xModelChild(xModel, UNO_QUERY);
        if (!xModelChild.is())
            return// nothing to do

        Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
        if (!xEventManager.is())
            return// nothing to do

        if (!rTransferIfAvailable.hasElements())
            return// nothing to do

        // check for the index of the model within its parent
        Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
        if (!xParentIndex.is())
            return// nothing to do
        sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
        if (nIndex<0 || nIndex>=xParentIndex->getCount())
            return// nothing to do

        // then we need information about the listeners supported by the control and the model
        Sequence< Type> aModelListeners;
        Sequence< Type> aControlListeners;

        Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());

        if (xModel.is())
        {
            Any aModel(xModel);
            aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
        }

        if (xControl.is())
        {
            Any aControl(xControl);
            aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
        }

        sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
        if (!nMaxNewLen)
            return// the model and the listener don't support any listeners (or we were unable to retrieve these infos)

        Sequence< ScriptEventDescriptor>    aTransferable(nMaxNewLen);
        ScriptEventDescriptor* pTransferable = aTransferable.getArray();

        for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
        {
            // search the model/control idl classes for the event described by pCurrent
            for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
            {
                for (const Type& rCurrentListener : *pCurrentArray)
                {
                    OUString aListener = rCurrentListener.getTypeName();
                    if (!aListener.isEmpty())
                        aListener = aListener.copy(aListener.lastIndexOf('.')+1);

                    if (aListener == rCurrent.ListenerType)
                        // the current ScriptEventDescriptor doesn't match the current listeners class
                        continue;

                    // now check the methods
                    Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);

                    if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
                    {
                        // we can transfer the script event : the model (control) supports it
                        *pTransferable = rCurrent;
                        ++pTransferable;
                        break;
                    }
                }
            }
        }

        sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
        aTransferable.realloc(nRealNewLen);

        xEventManager->registerScriptEvents(nIndex, aTransferable);
    }


    const OUString & getServiceNameByControlType(SdrObjKind nType)
    {
        switch (nType)
        {
            case SdrObjKind::FormEdit            : return FM_COMPONENT_TEXTFIELD;
            case SdrObjKind::FormButton          : return FM_COMPONENT_COMMANDBUTTON;
            case SdrObjKind::FormFixedText       : return FM_COMPONENT_FIXEDTEXT;
            case SdrObjKind::FormListbox         : return FM_COMPONENT_LISTBOX;
            case SdrObjKind::FormCheckbox        : return FM_COMPONENT_CHECKBOX;
            case SdrObjKind::FormRadioButton     : return FM_COMPONENT_RADIOBUTTON;
            case SdrObjKind::FormGroupBox        : return FM_COMPONENT_GROUPBOX;
            case SdrObjKind::FormCombobox        : return FM_COMPONENT_COMBOBOX;
            case SdrObjKind::FormGrid            : return FM_COMPONENT_GRIDCONTROL;
            case SdrObjKind::FormImageButton     : return FM_COMPONENT_IMAGEBUTTON;
            case SdrObjKind::FormFileControl     : return FM_COMPONENT_FILECONTROL;
            case SdrObjKind::FormDateField       : return FM_COMPONENT_DATEFIELD;
            case SdrObjKind::FormTimeField       : return FM_COMPONENT_TIMEFIELD;
            case SdrObjKind::FormNumericField    : return FM_COMPONENT_NUMERICFIELD;
            case SdrObjKind::FormCurrencyField   : return FM_COMPONENT_CURRENCYFIELD;
            case SdrObjKind::FormPatternField    : return FM_COMPONENT_PATTERNFIELD;
            case SdrObjKind::FormHidden          : return FM_COMPONENT_HIDDENCONTROL;
            case SdrObjKind::FormImageControl    : return FM_COMPONENT_IMAGECONTROL;
            case SdrObjKind::FormFormattedField  : return FM_COMPONENT_FORMATTEDFIELD;
            case SdrObjKind::FormScrollbar       : return FM_SUN_COMPONENT_SCROLLBAR;
            case SdrObjKind::FormSpinButton      : return FM_SUN_COMPONENT_SPINBUTTON;
            case SdrObjKind::FormNavigationBar   : return FM_SUN_COMPONENT_NAVIGATIONBAR;
            default:;
        }
        return EMPTY_OUSTRING;
    }

}


// check if the control has one of the interfaces we can use for searching
// *_pCurrentText will be filled with the current text of the control (as used when searching this control)
bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
    OUString* _pCurrentText )
{
    if ( !_rxControl.is() )
        return false;

    Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
    if ( xAsText.is() )
    {
        if ( _pCurrentText )
            *_pCurrentText = xAsText->getText();
        return true;
    }

    Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
    if ( xListBox.is() )
    {
        if ( _pCurrentText )
            *_pCurrentText = xListBox->getSelectedItem();
        return true;
    }

    Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
    if ( xCheckBox.is() )
    {
        if ( _pCurrentText )
        {
            switch ( static_cast<::TriState>(xCheckBox->getState()) )
            {
                case TRISTATE_FALSE: *_pCurrentText = "0"break;
                case TRISTATE_TRUE: *_pCurrentText = "1"break;
                default: _pCurrentText->clear(); break;
            }
        }
        return true;
    }

    return false;
}


bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
{
    if (_rContainer == m_xStartingPoint)
        // would be quite stupid to step over the root...
        return true;

    return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
}


bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
{
    if (!_rElement.is())
        // NULL element
        return false;

    if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
        // a forms or a grid
        return false;

    Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
    if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
        // no "BoundField" property
        return false;

    Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
    if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
        // void or invalid property value
        return false;

    return aVal.hasValue();
}


static bool isControlList(const SdrMarkList& rMarkList)
{
    // the list contains only controls and at least one control
    const size_t nMarkCount = rMarkList.GetMarkCount();
    bool  bControlList = nMarkCount != 0;

    bool bHadAnyLeafs = false;

    for (size_t i = 0; i < nMarkCount && bControlList; ++i)
    {
        SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
        E3dObject* pAs3DObject = DynCastE3dObject( pObj);
        // E3dObject's do not contain any 2D-objects (by definition)
        // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
        // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
        // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
        // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
        // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
        // And this would be wrong :)
        // 03.02.00 - 72529 - FS
        if (!pAs3DObject)
        {
            if (pObj->IsGroupObject())
            {
                SdrObjListIter aIter(pObj->GetSubList());
                while (aIter.IsMore() && bControlList)
                {
                    bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
                    bHadAnyLeafs = true;
                }
            }
            else
            {
                bHadAnyLeafs = true;
                bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
            }
        }
    }

    return bControlList && bHadAnyLeafs;
}


static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
{
    Reference< XForm > xForm( _rxElement, UNO_QUERY );
    if ( xForm.is() )
        return xForm;

    Reference< XChild > xChild( _rxElement, UNO_QUERY );
    if ( xChild.is() )
        return GetForm( xChild->getParent() );

    return Reference< XForm >();
}

FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex&&nbsp;_rMutex )
    :FmXFormShell_BD_BASE( _rMutex )
{
}

FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
        :FmXFormShell_BASE(m_aMutex)
        ,FmXFormShell_CFGBASE(u"Office.Common/Misc"_ustr, ConfigItemMode::NONE)
        ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
        ,m_eNavigate( NavigationBarMode_NONE )
        ,m_nInvalidationEvent( nullptr )
        ,m_nActivationEvent( nullptr )
        ,m_pShell( &_rShell )
        ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
        ,m_aActiveControllerFeatures( this )
        ,m_aNavControllerFeatures( this )
        ,m_eDocumentType( eUnknownDocumentType )
        ,m_nLockSlotInvalidation( 0 )
        ,m_bHadPropertyBrowserInDesignMode( false )
        ,m_bTrackProperties( true )
        ,m_bUseWizards( true )
        ,m_bDatabaseBar( false )
        ,m_bInActivate( false )
        ,m_bSetFocus( false )
        ,m_bFilterMode( false )
        ,m_bChangingDesignMode( false )
        ,m_bPreparedClose( false )
        ,m_bFirstActivation( true )
{
    m_aMarkTimer.SetTimeout(100);
    m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));

    m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();

    // to prevent deletion of this we acquire our refcounter once
    osl_atomic_increment(&m_refCount);

    // correct the refcounter
    osl_atomic_decrement(&m_refCount);

    // cache the current configuration settings we're interested in
    implAdjustConfigCache_Lock();
    // and register for changes on this settings
    Sequence< OUString > aNames { u"FormControlPilotsEnabled"_ustr };
    EnableNotification(aNames);
}


FmXFormShell::~FmXFormShell()
{
}


Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
{
    Reference< css::frame::XModel > xModel;

    // determine the type of document we live in
    try
    {
        Reference< css::frame::XController > xController;
        if ( m_xAttachedFrame.is() )
            xController = m_xAttachedFrame->getController();
        if ( xController.is() )
            xModel = xController->getModel();
    }
    catchconst Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
    }
    return xModel;
}


bool FmXFormShell::isEnhancedForm_Lock() const
{
    return getDocumentType_Lock() == eEnhancedForm;
}


bool FmXFormShell::impl_checkDisposed_Lock() const
{
    DBG_TESTSOLARMUTEX();
    if ( !m_pShell )
    {
        OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
        return true;
    }
    return false;
}


::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
{
    if ( m_eDocumentType != eUnknownDocumentType )
        return m_eDocumentType;

    // determine the type of document we live in
    Reference<css::frame::XModel> xModel = getContextDocument_Lock();
    if ( xModel.is() )
        m_eDocumentType = DocumentClassification::classifyDocument( xModel );
    else
    {
        OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
        m_eDocumentType = eTextDocument;
            // fallback, just to have a defined state
    }

    return m_eDocumentType;
}


bool FmXFormShell::IsReadonlyDoc_Lock() const
{
    if (impl_checkDisposed_Lock())
        return true;

    FmFormModel* pModel = m_pShell->GetFormModel();
    if ( pModel && pModel->GetObjectShell() )
        return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
    return true;
}

//  EventListener

void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
{
    SolarMutexGuard g;

    if (m_xActiveController == e.Source)
    {
        // the controller will release, then release everything
        stopListening_Lock();
        m_xActiveForm = nullptr;
        m_xActiveController = nullptr;
        m_xNavigationController = nullptr;

        m_aActiveControllerFeatures.dispose();
        m_aNavControllerFeatures.dispose();

        if ( m_pShell )
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
    }

    if (e.Source != m_xExternalViewController)
        return;

    Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
    OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
    if (xFormController.is())
        xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));

    if (m_xExternalViewController.is())
        m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));

    m_xExternalViewController = nullptr;
    m_xExternalDisplayedForm = nullptr;
    m_xExtViewTriggerController = nullptr;

    InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
}


void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
{
    SolarMutexGuard g;

    if (impl_checkDisposed_Lock())
        return;

    if (evt.PropertyName == FM_PROP_ROWCOUNT)
    {
        // The update following this forces a re-painting of the corresponding
        // slots. But if I am not in the MainThread of the application (because,
        // for example, a cursor is counting data sets at the moment and always
        // gives me this PropertyChanges), this can clash with normal paints in
        // the MainThread of the application. (Such paints happen, for example,
        // if one simply places another application over the office and switches
        // back again).
        // Therefore the use of the SolarMutex, which safeguards that.
        comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
        if (rSolarSafety.tryToAcquire())
        {
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(SID_FM_RECORD_TOTAL);
            rSolarSafety.release();
        }
        else
        {
            // with the following the slot is invalidated asynchron
            LockSlotInvalidation_Lock(true);
            InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
            LockSlotInvalidation_Lock(false);
        }
    }

    // this may be called from a non-main-thread so invalidate the shell asynchronously
    LockSlotInvalidation_Lock(true);
    InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
    LockSlotInvalidation_Lock(false);
}


void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
{
    SolarMutexGuard g;

    if (impl_checkDisposed_Lock())
        return;

    OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );

    if (!m_pShell->GetViewShell())
        return;

    // unfortunately, SFX requires sal_uInt16
    ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );

    // furthermore, SFX wants a terminating 0
    aSlotIds.push_back( 0 );

    // and, last but not least, SFX wants the ids to be sorted
    ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );

    sal_uInt16 *pSlotIds = aSlotIds.data();
    m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( pSlotIds );
}


void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
{
    SolarMutexGuard g;

    if (impl_checkDisposed_Lock())
        return;

    Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
    m_pTextShell->formActivated( xController );
    setActiveController_Lock(xController);
}


void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
{
    SolarMutexGuard g;

    if (impl_checkDisposed_Lock())
        return;

    Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
    m_pTextShell->formDeactivated( xController );
}


void FmXFormShell::disposing()
{
    SolarMutexGuard g;

    FmXFormShell_BASE::disposing();

    if ( m_pShell && !m_pShell->IsDesignMode() )
        setActiveController_Lock(nullptr, true);
        // do NOT save the content of the old form (the second parameter tells this)
        // if we're here, then we expect that PrepareClose has been called, and thus the user
        // got a chance to commit or reject any changes. So in case we're here and there
        // are still uncommitted changes, the user explicitly wanted this.

    m_pTextShell->dispose();

    m_xAttachedFrame = nullptr;

    CloseExternalFormViewer_Lock();

    while ( !m_aLoadingPages.empty() )
    {
        Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
        m_aLoadingPages.pop();
    }

    {
        if (m_nInvalidationEvent)
        {
            Application::RemoveUserEvent(m_nInvalidationEvent);
            m_nInvalidationEvent = nullptr;
        }
        if ( m_nActivationEvent )
        {
            Application::RemoveUserEvent( m_nActivationEvent );
            m_nActivationEvent = nullptr;
        }
    }

    {
        DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
            // should have been deleted while being disposed

        m_aMarkTimer.Stop();
    }

    DisableNotification();

    RemoveElement_Lock(m_xForms);
    m_xForms.clear();

    impl_switchActiveControllerListening_Lock(false);
    m_xActiveController         = nullptr;
    m_xActiveForm               = nullptr;

    m_pShell                    = nullptr;
    m_xNavigationController     = nullptr;
    m_xCurrentForm              = nullptr;
    m_xLastGridFound            = nullptr;
    m_xAttachedFrame            = nullptr;
    m_xExternalViewController   = nullptr;
    m_xExtViewTriggerController = nullptr;
    m_xExternalDisplayedForm    = nullptr;

    InterfaceBag().swap(m_aCurrentSelection);

    m_aActiveControllerFeatures.dispose();
    m_aNavControllerFeatures.dispose();
}


void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
{
    if (impl_checkDisposed_Lock())
        return;

    if ( m_nLockSlotInvalidation )
    {
        OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
        InvalidateSlot_Lock(_nId, false);
    }
    else
    {
        OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
        m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( _nId, truetrue );
        m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update( _nId );
    }
}

void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
{
    if (impl_checkDisposed_Lock())
        return;

    if (m_nLockSlotInvalidation)
    {
        sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
        m_arrInvalidSlots.emplace_back(nId, nFlags );
    }
    else
        if (nId)
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(nId, true, bWithId);
        else
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
}

void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
{
    if (impl_checkDisposed_Lock())
        return;

    DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");

    if (bLock)
        ++m_nLockSlotInvalidation;
    else if (!--m_nLockSlotInvalidation)
    {
        // (asynchronously) invalidate everything accumulated during the locked phase
        if (!m_nInvalidationEvent)
            m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
    }
}

IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
{
    if (impl_checkDisposed_Lock())
        return;

    m_nInvalidationEvent = nullptr;

    for (const auto& rInvalidSlot : m_arrInvalidSlots)
    {
        if (rInvalidSlot.id)
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
        else
            m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
    }
    m_arrInvalidSlots.clear();
}

void FmXFormShell::ForceUpdateSelection_Lock()
{
    if (impl_checkDisposed_Lock())
        return;

    if (IsSelectionUpdatePending_Lock())
    {
        m_aMarkTimer.Stop();

        // optionally turn off the invalidation of slots which is implicitly done by SetSelection
        LockSlotInvalidation_Lock(true);

        SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());

        LockSlotInvalidation_Lock(false);
    }
}

void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
{
    for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
    {
        // the corresponding image at it
        rNewMenu.append(OUString(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), aImgIds[i]);
    }
}

OUString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
{
    static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));

    for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
    {
        if (nSlot == SelObjectSlotMap[i])
            return OUString(aConvertSlots[i]);
    }

    return {};
}

bool FmXFormShell::isControlConversionSlot(std::u16string_view rIdent)
{
    for (const auto& rConvertSlot : aConvertSlots)
        if (rIdent == rConvertSlot)
            return true;
    return false;
}

void FmXFormShell::executeControlConversionSlot_Lock(std::u16string_view rIdent)
{
    OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
    InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
    if ( aSelectedElement == m_aCurrentSelection.end() )
        return;

    executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
}

bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>&&nbsp;_rxObject, std::u16string_view rIdent)
{
    if (impl_checkDisposed_Lock())
        return false;

    OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
    if ( !_rxObject.is() )
        return false;

    SdrPage* pPage = m_pShell->GetCurPage();
    FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
    OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
    if ( !pFormPage )
        return false;

    OSL_ENSURE( isSolelySelected_Lock(_rxObject),
        "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );

    for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
    {
        if (rIdent == aConvertSlots[lookupSlot])
        {
            Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );

            FmFormObj* pFormObject = nullptr;
            SdrObjListIter aPageIter( pFormPage );
            while ( aPageIter.IsMore() )
            {
                SdrObject* pCurrent = aPageIter.Next();
                pFormObject = FmFormObj::GetFormObject( pCurrent );
                if ( !pFormObject )
                    continue;

                Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
                if ( xCurrentNormalized.get() == xNormalizedObject.get() )
                    break;

                pFormObject = nullptr;
            }

            if ( !pFormObject )
                return false;

            OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
            const Reference<XComponentContext>& xContext = comphelper::getProcessComponentContext();
            Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
            if (!xNewModel.is())
                return false;

            Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );

            // transfer properties
            Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
            Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);


            lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
            TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);

            Sequence< css::script::ScriptEventDescriptor> aOldScripts;
            Reference< XChild> xChild(xOldModel, UNO_QUERY);
            if (xChild.is())
            {
                Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);

                // remember old script events
                Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
                if (xParent.is() && xEvManager.is())
                {
                    sal_Int32 nIndex = getElementPos(xParent, xOldModel);
                    if (nIndex>=0 && nIndex<xParent->getCount())
                        aOldScripts = xEvManager->getScriptEvents(nIndex);
                }

                // replace the model within the parent container
                Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
                if (xIndexParent.is())
                {
                    // the form container works with FormComponents
                    Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
                    DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
                    Any aNewModel(xComponent);
                    try
                    {

                        sal_Int32 nIndex = getElementPos(xParent, xOldModel);
                        if (nIndex>=0 && nIndex<xParent->getCount())
                            xIndexParent->replaceByIndex(nIndex, aNewModel);
                        else
                        {
                            OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
                            Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
                            if (xNewComponent.is())
                                xNewComponent->dispose();
                            return false;
                        }
                    }
                    catch(Exception&)
                    {
                        OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
                        Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
                        if (xNewComponent.is())
                            xNewComponent->dispose();
                        return false;
                    }

                }
            }

            // special handling for the LabelControl-property : can only be set when the model is placed
            // within the forms hierarchy
            if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
            {
                try
                {
                    xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
                }
                catch(Exception&)
                {
                }

            }

            // set new model
            pFormObject->SetChanged();
            pFormObject->SetUnoControlModel(xNewModel);

            // transfer script events
            // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
            if (aOldScripts.hasElements())
            {
                // find the control for the model
                Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());

                const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );

                Reference< XControl> xControl;
                auto pControl = std::find_if(aControls.begin(), aControls.end(),
                    [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
                if (pControl != aControls.end())
                    xControl = *pControl;
                TransferEventScripts(xNewModel, xControl, aOldScripts);
            }

            // transfer value bindings, if possible
            {
                Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
                Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
                if ( xOldBindable.is() )
                {
                    try
                    {
                        if ( xNewBindable.is() )
                            xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
                        xOldBindable->setValueBinding( nullptr );
                    }
                    catch(const Exception&)
                    {
                        DBG_UNHANDLED_EXCEPTION("svx");
                    }
                }
            }
            // same for list entry sources
            {
                Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
                Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
                if ( xOldSink.is() )
                {
                    try
                    {
                        if ( xNewSink.is() )
                            xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
                        xOldSink->setListEntrySource( nullptr );
                    }
                    catch(const Exception&)
                    {
                        DBG_UNHANDLED_EXCEPTION("svx");
                    }
                }
            }

            // create an undo action
            FmFormModel* pModel = m_pShell->GetFormModel();
            DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
            if (pModel && pModel->IsUndoEnabled() )
            {
                pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
            }
            else
            {
                FmUndoModelReplaceAction::DisposeElement( xOldModel );
            }

            return true;
        }
    }
    return false;
}

bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::u16string_view rIdent)
{
    if ( m_aCurrentSelection.empty() )
        return false;

    InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
    Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
    if ( !xElementInfo.is() )
        // no service info -> cannot determine this
        return false;

    if (  ++aCheck != m_aCurrentSelection.end() )
        // more than one element
        return false;

    if ( Reference< XForm >::query( xElementInfo ).is() )
        // it's a form
        return false;

    SdrObjKind nObjectType = getControlTypeByObject( xElementInfo );

    if (  ( SdrObjKind::FormHidden == nObjectType )
       || ( SdrObjKind::FormControl == nObjectType )
       || ( SdrObjKind::FormGrid == nObjectType )
       )
        return false;   // those types cannot be converted

    DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
        "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");

    for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
        if (rIdent == aConvertSlots[i])
            return nObjectTypes[i] != nObjectType;

    return true;    // all other slots: assume "yes"
}

void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu&&nbsp;rMenu)
{
    for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
    {
        // the context is already of a type that corresponds to the entry -> disable
        OUString sIdent(aConvertSlots[i]);
        rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
    }
}

void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
{
    if (impl_checkDisposed_Lock())
        return;

    Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
    if (!xControlModels.is())
        return;

    for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
    {
        Reference< XPropertySet> xModelSet;
        xControlModels->getByIndex(i) >>= xModelSet;
        if (!xModelSet.is())
            continue;

        if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
            continue;
        sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
        if (FormComponentType::GRIDCONTROL != nClassId)
            continue;

        if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
            continue;

        switch (nSync)
        {
            case LoopGridsSync::DISABLE_SYNC:
            {
                xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
            }
            break;
            case LoopGridsSync::FORCE_SYNC:
            {
                Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
                xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
                xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
            }
            break;
            case LoopGridsSync::ENABLE_SYNC:
            {
                xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
            }
            break;
        }

        if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
        {
            xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
            Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
            if (xModelPropState.is())
                xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
            else
                xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());        // this should be the default
        }
    }
}


Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
{
    if (impl_checkDisposed_Lock())
        return nullptr;

    SdrPageView* pPageView = nullptr;
    if ( m_pShell && m_pShell->GetFormView() )
        pPageView = m_pShell->GetFormView()->GetSdrPageView();

    Reference< XControlContainer> xControlContainer;
    if ( pPageView )
        xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();

    return xControlContainer;
}


void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>&&nbsp;_rxForForm)
{
    if (impl_checkDisposed_Lock())
        return;

    OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
    if ( !_rxForForm.is() )
        return;

    try
    {
        Reference< XWindow > xParentWindow;
        if (m_pShell->GetViewShell())
            xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame().GetWindow() );

        Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
                comphelper::getProcessComponentContext(),
                _rxForForm, getControlContainerForView_Lock(), xParentWindow
            );

        (void)xDialog->execute();
    }
    catchconst Exception& )
    {
        TOOLS_WARN_EXCEPTION( "svx""FmXFormShell::ExecuteTabOrderDialog" );
    }
}


void FmXFormShell::ExecuteSearch_Lock()
{
    if (impl_checkDisposed_Lock())
        return;

    // a collection of all (logical) forms
    FmFormArray().swap(m_aSearchForms);
    ::std::vector< OUString > aContextNames;
    impl_collectFormSearchContexts_nothrow_Lock(
        m_pShell->GetCurPage()->GetForms(), u"",
        m_aSearchForms, aContextNames);

    if ( m_aSearchForms.size() != aContextNames.size() )
    {
        SAL_WARN ( "svx.form""FmXFormShell::ExecuteSearch: nonsense!" );
        return;
    }

    // filter out the forms which do not contain valid controls at all
    {
        FmFormArray aValidForms;
        ::std::vector< OUString > aValidContexts;
        FmFormArray::const_iterator form = m_aSearchForms.begin();
        ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
        for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
        {
            FmSearchContext aTestContext;
            aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
            sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
            if ( nValidControls > 0 )
            {
                aValidForms.push_back( *form );
                aValidContexts.push_back( *contextName );
            }
        }

        m_aSearchForms.swap( aValidForms );
        aContextNames.swap( aValidContexts );
    }

    if (m_aSearchForms.empty() )
    {
        // there are no controls that meet all the conditions for a search
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
                                                                 VclMessageType::Warning, VclButtonsType::Ok,
                                                                 SvxResId(RID_STR_NODATACONTROLS)));
        xBox->run();
        return;
    }

    // now I need another 'initial context'
    sal_Int16 nInitialContext = 0;
    Reference<XForm> xActiveForm(getActiveForm_Lock());
    for ( size_t i=0; i<m_aSearchForms.size(); ++i )
    {
        if (m_aSearchForms.at(i) == xActiveForm)
        {
            nInitialContext = static_cast<sal_Int16>(i);
            break;
        }
    }

    // If the dialog should initially offer the text of the active control,
    // this must have an XTextComponent interface. An addition, this makes
    // sense only if the current field is also bound to a table (or whatever) field.
    OUString strActiveField;
    OUString strInitialText;
    // ... this I get from my FormController
    DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
    Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
    if (xActiveControl.is())
    {
        // the control can tell me its model ...
        Reference< XControlModel> xActiveModel( xActiveControl->getModel());
        DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");

        // I ask the model for the ControlSource property ...
        Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
        if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
        {
            Reference< XPropertySet> xField;
            xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
            if (xField.is())    // (only when the thing is really bound)
            {
                // and the control itself for a TextComponent interface (so that I can pick up the text there)
                Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
                if (xText.is())
                {
                    strActiveField = getLabelName(xProperties);
                    strInitialText = xText->getText();
                }
            }
        }
        else
        {
            // the control itself has no ControlSource, but maybe it is a GridControl
            Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
            if (xGrid.is())
            {
                // for strActiveField I need the ControlSource of the column,
                // for that the columns container, for that the GridPeer
                Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
                Reference< XIndexAccess> xColumns;
                if (xGridPeer.is())
                    xColumns = xGridPeer->getColumns();

                sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
                sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
                Reference< XPropertySet> xCurrentCol;
                if(xColumns.is())
                    xColumns->getByIndex(nModelCol) >>= xCurrentCol;
                if (xCurrentCol.is())
                    strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));

                // the text of the current column
                Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
                Reference< XInterface> xCurControl;
                xColControls->getByIndex(nViewCol) >>= xCurControl;
                OUString sInitialText;
                if (IsSearchableControl(xCurControl, &sInitialText))
                    strInitialText = sInitialText;
            }
        }
    }

    // taking care of possible GridControls that I know
    LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);

    // Now I am ready for the dialogue.
    // When the potential deadlocks caused by the use of the solar mutex in
    // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
    // placed here, because the search in a separate thread is nevertheless
    // somewhat more fluid. Should be, however, somehow made dependent of the
    // underlying cursor. DAO for example is not thread-safe.
    SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
    ScopedVclPtr<AbstractFmSearchDialog> pDialog(
            pFact->CreateFmSearchDialog(
                m_pShell->GetViewShell()->GetViewFrame().GetFrameWeld(),
                strInitialText, aContextNames, nInitialContext,
                LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
    pDialog->SetActiveField( strActiveField );
    pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
    pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
    pDialog->Execute();
    pDialog.disposeAndClear();

    // restore GridControls again
    LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);

    m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
        // because I marked controls in OnFoundData (if I was there)
}


bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
{
    if (impl_checkDisposed_Lock())
        return false;

    if (m_pShell->IsDesignMode())
        // in the design mode (without active controls) the main document is to take care of it
        return false;

    Reference<XForm> xForm(getActiveForm_Lock());
    if (!xForm.is())
        // no current form (in particular no current control) -> the main document is to take care
        return false;

    Reference< XRowSet> xDB(xForm, UNO_QUERY);
    DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");

    Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
    if (xSupplier.is())
    {
        Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
        if (xSet.is())
        {
            try
            {
                Any aVal( xSet->getPropertyValue(u"TwoDigitDateStart"_ustr) );
                aVal >>= n;
                return true;
            }
            catch(Exception&)
            {
            }

        }
    }
    return false;
}


void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
{
    if (impl_checkDisposed_Lock())
        return;

    Reference<XForm> xActiveForm(getActiveForm_Lock());
    Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
    if ( xActiveRowSet.is() )
    {
        Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
        if (xSupplier.is())
        {
            Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
            if (xSet.is())
            {
                try
                {
                    xSet->setPropertyValue(u"TwoDigitDateStart"_ustr, Any(sal_uInt16(n)));
                }
                catch(Exception&)
                {
                    TOOLS_WARN_EXCEPTION("svx.form""");
                }

            }
            return;
        }
    }

    // no active form found -> iterate through all current forms
    Reference< XIndexAccess> xCurrentForms( m_xForms);
    if (!xCurrentForms.is())
    {   // in the alive mode, my forms are not set, but the ones on the page are
        if (m_pShell->GetCurPage())
            xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
    }
    if (!xCurrentForms.is())
        return;

    ::comphelper::IndexAccessIterator aIter(xCurrentForms);
    Reference< XInterface> xCurrentElement( aIter.Next());
    while (xCurrentElement.is())
    {
        // is the current element a DatabaseForm?
        Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
        if ( xElementAsRowSet.is() )
        {
            Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
            if (!xSupplier.is())
                continue;

            Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
            if (xSet.is())
            {
                try
                {
                    xSet->setPropertyValue(u"TwoDigitDateStart"_ustr, Any(sal_uInt16(n)));
                }
                catch(Exception&)
                {
                    TOOLS_WARN_EXCEPTION("svx.form""");
                }

            }
        }
        xCurrentElement = aIter.Next();
    }
}


void FmXFormShell::CloseExternalFormViewer_Lock()
{
    if (impl_checkDisposed_Lock())
        return;

    if (!m_xExternalViewController.is())
        return;

    Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
    Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
    if (!xCommLink.is())
        return;

    xExternalViewFrame->setComponent(nullptr,nullptr);
    ::comphelper::disposeComponent(xExternalViewFrame);
    m_xExternalViewController   = nullptr;
    m_xExtViewTriggerController = nullptr;
    m_xExternalDisplayedForm    = nullptr;
}


Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>&&nbsp;_xForm) const
{
    if (impl_checkDisposed_Lock())
        return nullptr;

    Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
    if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
    {
        DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
        return m_xExternalDisplayedForm;
    }
    return _xForm;
}


Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
{
    if (impl_checkDisposed_Lock())
        return nullptr;

    Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
    if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
    {
        DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
        return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
    }
    return _xForm;
}


namespace
{
    bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
    {
        return  ( _nWhich == SID_FM_RECORD_FIRST )
            ||  ( _nWhich == SID_FM_RECORD_PREV )
            ||  ( _nWhich == SID_FM_RECORD_NEXT )
            ||  ( _nWhich == SID_FM_RECORD_LAST )
            ||  ( _nWhich == SID_FM_RECORD_NEW );
    }
}


bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState const
{
    const svx::ControllerFeatures& rController =
            lcl_isNavigationRelevant( _nSlot )
        ?   getNavControllerFeatures_Lock()
        :   getActiveControllerFeatures_Lock();

    if ( !_pCompleteState )
        return rController->isEnabled( _nSlot );

    rController->getState( _nSlot, *_pCompleteState );
    return _pCompleteState->Enabled;
}


void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
{
    const svx::ControllerFeatures& rController =
            lcl_isNavigationRelevant( _nSlot )
        ?   getNavControllerFeatures_Lock()
        :   getActiveControllerFeatures_Lock();

    rController->execute( _nSlot );

    if ( _nSlot != SID_FM_RECORD_UNDO )
        return;

    // if we're doing an UNDO, *and* if the affected form is the form which we also display
    // as external view, then we need to reset the controls of the external form, too
    if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
        return;

    Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
    if ( !xContainer.is() )
        return;

    Reference< XReset > xReset;
    for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
    {
        if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
        {
            // no resets on sub forms
            Reference< XForm > xAsForm( xReset, UNO_QUERY );
            if ( !xAsForm.is() )
                xReset->reset();
        }
    }
}

void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
{
    if ( !m_xActiveController.is() )
        return;

    if ( _bListen )
        m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
    else
        m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
}

void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
{
    if (impl_checkDisposed_Lock())
        return;

    if (m_bChangingDesignMode)
        return;
    DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");

    // if the routine has been called a second time,
    // the focus should no longer be transferred
    if (m_bInActivate)
    {
        m_bSetFocus = xController != m_xActiveController;
        return;
    }

    if (xController == m_xActiveController)
        return;

    // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
    Reference< XResultSet> xNavigationForm;
    if (m_xNavigationController.is())
        xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);

    m_bInActivate = true;

    // check if the 2 controllers serve different forms
    Reference< XResultSet> xOldForm;
    if (m_xActiveController.is())
        xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
    Reference< XResultSet> xNewForm;
    if (xController.is())
        xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
    xOldForm = getInternalForm_Lock(xOldForm);
    xNewForm = getInternalForm_Lock(xNewForm);

    bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
    bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
        // we save the content of the old form if we move to a new form, and saving old content is allowed

    if ( m_xActiveController.is() && bNeedSave )
    {
        // save content on change of the controller; a commit has already been executed
        if ( m_aActiveControllerFeatures->commitCurrentControl() )
        {
            m_bSetFocus = true;
            if ( m_aActiveControllerFeatures->isModifiedRow() )
            {
                bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
                bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
                if ( !bResult && m_bSetFocus )
                {
                    // if we couldn't save the current record, set the focus back to the
                    // current control
                    Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
                    if ( xWindow.is() )
                        xWindow->setFocus();
                    m_bInActivate = false;
                    return;
                }
                else if ( bResult && bIsNew )
                {
                    Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
                    if ( xCursor.is() )
                    {
                        DO_SAFE( xCursor->last(); );
                    }
                }
            }
        }
    }

    stopListening_Lock();

    impl_switchActiveControllerListening_Lock(false);

    m_aActiveControllerFeatures.dispose();
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=94 G=94

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.