svx: add class "ONeutralParseContext"
[LibreOffice.git] / svx / source / form / fmshimp.cxx
blobcfaab66fa28ba79bc93daaeba3fd42faa8b415fc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <o3tl/safeint.hxx>
23 #include <sal/macros.h>
24 #include <sal/log.hxx>
25 #include <fmobj.hxx>
26 #include <fmpgeimp.hxx>
27 #include <svx/fmtools.hxx>
28 #include <fmprop.hxx>
29 #include <fmservs.hxx>
30 #include <fmshimp.hxx>
31 #include <fmtextcontrolshell.hxx>
32 #include <fmundo.hxx>
33 #include <fmurl.hxx>
34 #include <fmvwimp.hxx>
35 #include <gridcols.hxx>
36 #include <svx/svditer.hxx>
37 #include <svx/dialmgr.hxx>
38 #include <svx/strings.hrc>
39 #include <svx/svdobjkind.hxx>
40 #include <svx/fmmodel.hxx>
41 #include <svx/fmpage.hxx>
42 #include <svx/fmshell.hxx>
43 #include <svx/fmview.hxx>
44 #include <svx/obj3d.hxx>
45 #include <svx/sdrpagewindow.hxx>
46 #include <svx/svdpagv.hxx>
47 #include <svx/svxdlg.hxx>
48 #include <svx/svxids.hrc>
49 #include <bitmaps.hlst>
50 #include <formnavi.hrc>
52 #include <com/sun/star/awt/XWindow2.hpp>
53 #include <com/sun/star/awt/XCheckBox.hpp>
54 #include <com/sun/star/awt/XListBox.hpp>
55 #include <com/sun/star/awt/XTextComponent.hpp>
56 #include <com/sun/star/beans/theIntrospection.hpp>
57 #include <com/sun/star/beans/PropertyAttribute.hpp>
58 #include <com/sun/star/beans/XPropertyState.hpp>
59 #include <com/sun/star/container/XContainer.hpp>
60 #include <com/sun/star/container/XIndexAccess.hpp>
61 #include <com/sun/star/container/XNamed.hpp>
62 #include <com/sun/star/form/ListSourceType.hpp>
63 #include <com/sun/star/form/TabOrderDialog.hpp>
64 #include <com/sun/star/form/XGrid.hpp>
65 #include <com/sun/star/form/XGridPeer.hpp>
66 #include <com/sun/star/form/XLoadable.hpp>
67 #include <com/sun/star/form/XReset.hpp>
68 #include <com/sun/star/form/binding/XBindableValue.hpp>
69 #include <com/sun/star/form/binding/XListEntrySink.hpp>
70 #include <com/sun/star/frame/FrameSearchFlag.hpp>
71 #include <com/sun/star/lang/XServiceInfo.hpp>
72 #include <com/sun/star/script/XEventAttacherManager.hpp>
73 #include <com/sun/star/sdbc/SQLException.hpp>
74 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
75 #include <com/sun/star/util/XModeSelector.hpp>
76 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
77 #include <com/sun/star/view/XSelectionSupplier.hpp>
79 #include <comphelper/evtmethodhelper.hxx>
80 #include <comphelper/processfactory.hxx>
81 #include <comphelper/property.hxx>
82 #include <comphelper/sequence.hxx>
83 #include <comphelper/solarmutex.hxx>
84 #include <comphelper/string.hxx>
85 #include <comphelper/types.hxx>
86 #include <connectivity/dbtools.hxx>
87 #include <sfx2/dispatch.hxx>
88 #include <sfx2/frame.hxx>
89 #include <sfx2/objsh.hxx>
90 #include <sfx2/viewfrm.hxx>
91 #include <sfx2/viewsh.hxx>
92 #include <toolkit/helper/vclunohelper.hxx>
93 #include <tools/debug.hxx>
94 #include <tools/diagnose_ex.h>
95 #include <vcl/settings.hxx>
96 #include <vcl/svapp.hxx>
97 #include <vcl/weld.hxx>
98 #include <vcl/window.hxx>
100 #include <algorithm>
101 #include <map>
102 #include <memory>
103 #include <string_view>
104 #include <vector>
106 // is used for Invalidate -> maintain it as well
107 const sal_uInt16 DatabaseSlotMap[] =
109 SID_FM_RECORD_FIRST,
110 SID_FM_RECORD_NEXT,
111 SID_FM_RECORD_PREV,
112 SID_FM_RECORD_LAST,
113 SID_FM_RECORD_NEW,
114 SID_FM_RECORD_DELETE,
115 SID_FM_RECORD_ABSOLUTE,
116 SID_FM_RECORD_TOTAL,
117 SID_FM_RECORD_SAVE,
118 SID_FM_RECORD_UNDO,
119 SID_FM_REMOVE_FILTER_SORT,
120 SID_FM_SORTUP,
121 SID_FM_SORTDOWN,
122 SID_FM_ORDERCRIT,
123 SID_FM_AUTOFILTER,
124 SID_FM_FORM_FILTERED,
125 SID_FM_REFRESH,
126 SID_FM_REFRESH_FORM_CONTROL,
127 SID_FM_SEARCH,
128 SID_FM_FILTER_START,
129 SID_FM_VIEW_AS_GRID,
133 // is used for Invalidate -> maintain it as well
134 // sort ascending !!!!!!
135 const sal_Int16 DlgSlotMap[] = // slots of the controller
137 SID_FM_CTL_PROPERTIES,
138 SID_FM_PROPERTIES,
139 SID_FM_TAB_DIALOG,
140 SID_FM_ADD_FIELD,
141 SID_FM_SHOW_FMEXPLORER,
142 SID_FM_FIELDS_CONTROL,
143 SID_FM_SHOW_PROPERTIES,
144 SID_FM_PROPERTY_CONTROL,
145 SID_FM_FMEXPLORER_CONTROL,
146 SID_FM_SHOW_DATANAVIGATOR,
147 SID_FM_DATANAVIGATOR_CONTROL,
151 const sal_Int16 SelObjectSlotMap[] = // slots depending on the SelObject
153 SID_FM_CONVERTTO_EDIT,
154 SID_FM_CONVERTTO_BUTTON,
155 SID_FM_CONVERTTO_FIXEDTEXT,
156 SID_FM_CONVERTTO_LISTBOX,
157 SID_FM_CONVERTTO_CHECKBOX,
158 SID_FM_CONVERTTO_RADIOBUTTON,
159 SID_FM_CONVERTTO_GROUPBOX,
160 SID_FM_CONVERTTO_COMBOBOX,
161 SID_FM_CONVERTTO_IMAGEBUTTON,
162 SID_FM_CONVERTTO_FILECONTROL,
163 SID_FM_CONVERTTO_DATE,
164 SID_FM_CONVERTTO_TIME,
165 SID_FM_CONVERTTO_NUMERIC,
166 SID_FM_CONVERTTO_CURRENCY,
167 SID_FM_CONVERTTO_PATTERN,
168 SID_FM_CONVERTTO_IMAGECONTROL,
169 SID_FM_CONVERTTO_FORMATTED,
170 SID_FM_CONVERTTO_SCROLLBAR,
171 SID_FM_CONVERTTO_SPINBUTTON,
172 SID_FM_CONVERTTO_NAVIGATIONBAR,
174 SID_FM_FMEXPLORER_CONTROL,
175 SID_FM_DATANAVIGATOR_CONTROL,
180 // the following arrays must be consistent, i.e., corresponding entries should
181 // be at the same relative position within their respective arrays
182 static const char* aConvertSlots[] =
184 "ConvertToEdit",
185 "ConvertToButton",
186 "ConvertToFixed",
187 "ConvertToList",
188 "ConvertToCheckBox",
189 "ConvertToRadio",
190 "ConvertToGroup",
191 "ConvertToCombo",
192 "ConvertToImageBtn",
193 "ConvertToFileControl",
194 "ConvertToDate",
195 "ConvertToTime",
196 "ConvertToNumeric",
197 "ConvertToCurrency",
198 "ConvertToPattern",
199 "ConvertToImageControl",
200 "ConvertToFormatted",
201 "ConvertToScrollBar",
202 "ConvertToSpinButton",
203 "ConvertToNavigationBar"
206 const std::u16string_view aImgIds[] =
208 u"" RID_SVXBMP_EDITBOX,
209 u"" RID_SVXBMP_BUTTON,
210 u"" RID_SVXBMP_FIXEDTEXT,
211 u"" RID_SVXBMP_LISTBOX,
212 u"" RID_SVXBMP_CHECKBOX,
213 u"" RID_SVXBMP_RADIOBUTTON,
214 u"" RID_SVXBMP_GROUPBOX,
215 u"" RID_SVXBMP_COMBOBOX,
216 u"" RID_SVXBMP_IMAGEBUTTON,
217 u"" RID_SVXBMP_FILECONTROL,
218 u"" RID_SVXBMP_DATEFIELD,
219 u"" RID_SVXBMP_TIMEFIELD,
220 u"" RID_SVXBMP_NUMERICFIELD,
221 u"" RID_SVXBMP_CURRENCYFIELD,
222 u"" RID_SVXBMP_PATTERNFIELD,
223 u"" RID_SVXBMP_IMAGECONTROL,
224 u"" RID_SVXBMP_FORMATTEDFIELD,
225 u"" RID_SVXBMP_SCROLLBAR,
226 u"" RID_SVXBMP_SPINBUTTON,
227 u"" RID_SVXBMP_NAVIGATIONBAR
230 const sal_Int16 nObjectTypes[] =
232 OBJ_FM_EDIT,
233 OBJ_FM_BUTTON,
234 OBJ_FM_FIXEDTEXT,
235 OBJ_FM_LISTBOX,
236 OBJ_FM_CHECKBOX,
237 OBJ_FM_RADIOBUTTON,
238 OBJ_FM_GROUPBOX,
239 OBJ_FM_COMBOBOX,
240 OBJ_FM_IMAGEBUTTON,
241 OBJ_FM_FILECONTROL,
242 OBJ_FM_DATEFIELD,
243 OBJ_FM_TIMEFIELD,
244 OBJ_FM_NUMERICFIELD,
245 OBJ_FM_CURRENCYFIELD,
246 OBJ_FM_PATTERNFIELD,
247 OBJ_FM_IMAGECONTROL,
248 OBJ_FM_FORMATTEDFIELD,
249 OBJ_FM_SCROLLBAR,
250 OBJ_FM_SPINBUTTON,
251 OBJ_FM_NAVIGATIONBAR
254 using namespace ::com::sun::star;
255 using namespace ::com::sun::star::ui;
256 using namespace ::com::sun::star::uno;
257 using namespace ::com::sun::star::sdb;
258 using namespace ::com::sun::star::sdbc;
259 using namespace ::com::sun::star::sdbcx;
260 using namespace ::com::sun::star::beans;
261 using namespace ::com::sun::star::container;
262 using namespace ::com::sun::star::form;
263 using namespace ::com::sun::star::form::binding;
264 using namespace ::com::sun::star::form::runtime;
265 using namespace ::com::sun::star::awt;
266 using namespace ::com::sun::star::view;
267 using namespace ::com::sun::star::util;
268 using namespace ::com::sun::star::script;
269 using namespace ::svxform;
270 using namespace ::svx;
271 using namespace ::dbtools;
274 //= helper
276 namespace
279 void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag& /* [out] */ _rInterfaces )
281 _rInterfaces.clear();
283 const size_t nMarkCount = _rMarkList.GetMarkCount();
284 for ( size_t i = 0; i < nMarkCount; ++i)
286 SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
288 std::unique_ptr<SdrObjListIter> pGroupIterator;
289 if ( pCurrent->IsGroupObject() )
291 pGroupIterator.reset(new SdrObjListIter( pCurrent->GetSubList() ));
292 pCurrent = pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
295 while ( pCurrent )
297 FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
298 // note this will de-reference virtual objects, if necessary/possible
299 if ( pAsFormObject )
301 Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
302 // the UNO_QUERY is important for normalization
303 if ( xControlModel.is() )
304 _rInterfaces.insert( xControlModel );
307 // next element
308 pCurrent = pGroupIterator && pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
314 sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
318 if (rColumns.is())
320 // loop through all columns
321 sal_Int32 i;
322 Reference< XPropertySet> xCur;
323 for (i=0; i<rColumns->getCount(); ++i)
325 rColumns->getByIndex(i) >>= xCur;
326 if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
328 // for every visible col : if nViewPos is greater zero, decrement it, else we
329 // have found the model position
330 if (!nViewPos)
331 break;
332 else
333 --nViewPos;
336 if (i<rColumns->getCount())
337 return i;
340 catch(const Exception&)
342 DBG_UNHANDLED_EXCEPTION("svx");
344 return -1;
348 void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
349 const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
351 // first check if we have a XEventAttacherManager for the model
352 Reference< XChild> xModelChild(xModel, UNO_QUERY);
353 if (!xModelChild.is())
354 return; // nothing to do
356 Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
357 if (!xEventManager.is())
358 return; // nothing to do
360 if (!rTransferIfAvailable.hasElements())
361 return; // nothing to do
363 // check for the index of the model within its parent
364 Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
365 if (!xParentIndex.is())
366 return; // nothing to do
367 sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
368 if (nIndex<0 || nIndex>=xParentIndex->getCount())
369 return; // nothing to do
371 // then we need information about the listeners supported by the control and the model
372 Sequence< Type> aModelListeners;
373 Sequence< Type> aControlListeners;
375 Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
377 if (xModel.is())
379 Any aModel(makeAny(xModel));
380 aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
383 if (xControl.is())
385 Any aControl(makeAny(xControl));
386 aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
389 sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
390 if (!nMaxNewLen)
391 return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
393 Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
394 ScriptEventDescriptor* pTransferable = aTransferable.getArray();
396 for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
398 // search the model/control idl classes for the event described by pCurrent
399 for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
401 for (const Type& rCurrentListener : *pCurrentArray)
403 OUString aListener = rCurrentListener.getTypeName();
404 if (!aListener.isEmpty())
405 aListener = aListener.copy(aListener.lastIndexOf('.')+1);
407 if (aListener == rCurrent.ListenerType)
408 // the current ScriptEventDescriptor doesn't match the current listeners class
409 continue;
411 // now check the methods
412 Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
414 if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
416 // we can transfer the script event : the model (control) supports it
417 *pTransferable = rCurrent;
418 ++pTransferable;
419 break;
425 sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
426 aTransferable.realloc(nRealNewLen);
428 xEventManager->registerScriptEvents(nIndex, aTransferable);
432 OUString getServiceNameByControlType(sal_Int16 nType)
434 switch (nType)
436 case OBJ_FM_EDIT : return FM_COMPONENT_TEXTFIELD;
437 case OBJ_FM_BUTTON : return FM_COMPONENT_COMMANDBUTTON;
438 case OBJ_FM_FIXEDTEXT : return FM_COMPONENT_FIXEDTEXT;
439 case OBJ_FM_LISTBOX : return FM_COMPONENT_LISTBOX;
440 case OBJ_FM_CHECKBOX : return FM_COMPONENT_CHECKBOX;
441 case OBJ_FM_RADIOBUTTON : return FM_COMPONENT_RADIOBUTTON;
442 case OBJ_FM_GROUPBOX : return FM_COMPONENT_GROUPBOX;
443 case OBJ_FM_COMBOBOX : return FM_COMPONENT_COMBOBOX;
444 case OBJ_FM_GRID : return FM_COMPONENT_GRIDCONTROL;
445 case OBJ_FM_IMAGEBUTTON : return FM_COMPONENT_IMAGEBUTTON;
446 case OBJ_FM_FILECONTROL : return FM_COMPONENT_FILECONTROL;
447 case OBJ_FM_DATEFIELD : return FM_COMPONENT_DATEFIELD;
448 case OBJ_FM_TIMEFIELD : return FM_COMPONENT_TIMEFIELD;
449 case OBJ_FM_NUMERICFIELD : return FM_COMPONENT_NUMERICFIELD;
450 case OBJ_FM_CURRENCYFIELD : return FM_COMPONENT_CURRENCYFIELD;
451 case OBJ_FM_PATTERNFIELD : return FM_COMPONENT_PATTERNFIELD;
452 case OBJ_FM_HIDDEN : return FM_COMPONENT_HIDDENCONTROL;
453 case OBJ_FM_IMAGECONTROL : return FM_COMPONENT_IMAGECONTROL;
454 case OBJ_FM_FORMATTEDFIELD : return FM_COMPONENT_FORMATTEDFIELD;
455 case OBJ_FM_SCROLLBAR : return FM_SUN_COMPONENT_SCROLLBAR;
456 case OBJ_FM_SPINBUTTON : return FM_SUN_COMPONENT_SPINBUTTON;
457 case OBJ_FM_NAVIGATIONBAR : return FM_SUN_COMPONENT_NAVIGATIONBAR;
459 return OUString();
465 // check if the control has one of the interfaces we can use for searching
466 // *_pCurrentText will be filled with the current text of the control (as used when searching this control)
467 bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
468 OUString* _pCurrentText )
470 if ( !_rxControl.is() )
471 return false;
473 Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
474 if ( xAsText.is() )
476 if ( _pCurrentText )
477 *_pCurrentText = xAsText->getText();
478 return true;
481 Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
482 if ( xListBox.is() )
484 if ( _pCurrentText )
485 *_pCurrentText = xListBox->getSelectedItem();
486 return true;
489 Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
490 if ( xCheckBox.is() )
492 if ( _pCurrentText )
494 switch ( static_cast<::TriState>(xCheckBox->getState()) )
496 case TRISTATE_FALSE: *_pCurrentText = "0"; break;
497 case TRISTATE_TRUE: *_pCurrentText = "1"; break;
498 default: _pCurrentText->clear(); break;
501 return true;
504 return false;
508 bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
510 if (_rContainer == m_xStartingPoint)
511 // would be quite stupid to step over the root...
512 return true;
514 return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
518 bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
520 if (!_rElement.is())
521 // NULL element
522 return false;
524 if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
525 // a forms or a grid
526 return false;
528 Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
529 if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
530 // no "BoundField" property
531 return false;
533 Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
534 if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
535 // void or invalid property value
536 return false;
538 return aVal.hasValue();
542 static bool isControlList(const SdrMarkList& rMarkList)
544 // the list contains only controls and at least one control
545 const size_t nMarkCount = rMarkList.GetMarkCount();
546 bool bControlList = nMarkCount != 0;
548 bool bHadAnyLeafs = false;
550 for (size_t i = 0; i < nMarkCount && bControlList; ++i)
552 SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
553 E3dObject* pAs3DObject = dynamic_cast< E3dObject* >( pObj);
554 // E3dObject's do not contain any 2D-objects (by definition)
555 // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
556 // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
557 // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
558 // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
559 // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
560 // And this would be wrong :)
561 // 03.02.00 - 72529 - FS
562 if (!pAs3DObject)
564 if (pObj->IsGroupObject())
566 SdrObjListIter aIter(pObj->GetSubList());
567 while (aIter.IsMore() && bControlList)
569 bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
570 bHadAnyLeafs = true;
573 else
575 bHadAnyLeafs = true;
576 bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
581 return bControlList && bHadAnyLeafs;
585 static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
587 Reference< XForm > xForm( _rxElement, UNO_QUERY );
588 if ( xForm.is() )
589 return xForm;
591 Reference< XChild > xChild( _rxElement, UNO_QUERY );
592 if ( xChild.is() )
593 return GetForm( xChild->getParent() );
595 return Reference< XForm >();
598 FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
599 :FmXFormShell_BD_BASE( _rMutex )
603 FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
604 :FmXFormShell_BASE(m_aMutex)
605 ,FmXFormShell_CFGBASE("Office.Common/Misc", ConfigItemMode::NONE)
606 ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
607 ,m_eNavigate( NavigationBarMode_NONE )
608 ,m_nInvalidationEvent( nullptr )
609 ,m_nActivationEvent( nullptr )
610 ,m_pShell( &_rShell )
611 ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
612 ,m_aActiveControllerFeatures( this )
613 ,m_aNavControllerFeatures( this )
614 ,m_eDocumentType( eUnknownDocumentType )
615 ,m_nLockSlotInvalidation( 0 )
616 ,m_bHadPropertyBrowserInDesignMode( false )
617 ,m_bTrackProperties( true )
618 ,m_bUseWizards( true )
619 ,m_bDatabaseBar( false )
620 ,m_bInActivate( false )
621 ,m_bSetFocus( false )
622 ,m_bFilterMode( false )
623 ,m_bChangingDesignMode( false )
624 ,m_bPreparedClose( false )
625 ,m_bFirstActivation( true )
627 m_aMarkTimer.SetTimeout(100);
628 m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
630 m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
632 // to prevent deletion of this we acquire our refcounter once
633 osl_atomic_increment(&m_refCount);
635 // correct the refcounter
636 osl_atomic_decrement(&m_refCount);
638 // cache the current configuration settings we're interested in
639 implAdjustConfigCache_Lock();
640 // and register for changes on this settings
641 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
642 EnableNotification(aNames);
646 FmXFormShell::~FmXFormShell()
651 Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
653 Reference< css::frame::XModel > xModel;
655 // determine the type of document we live in
658 Reference< css::frame::XController > xController;
659 if ( m_xAttachedFrame.is() )
660 xController = m_xAttachedFrame->getController();
661 if ( xController.is() )
662 xModel = xController->getModel();
664 catch( const Exception& )
666 DBG_UNHANDLED_EXCEPTION("svx");
668 return xModel;
672 bool FmXFormShell::isEnhancedForm_Lock() const
674 return getDocumentType_Lock() == eEnhancedForm;
678 bool FmXFormShell::impl_checkDisposed_Lock() const
680 DBG_TESTSOLARMUTEX();
681 if ( !m_pShell )
683 OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
684 return true;
686 return false;
690 ::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
692 if ( m_eDocumentType != eUnknownDocumentType )
693 return m_eDocumentType;
695 // determine the type of document we live in
696 Reference<css::frame::XModel> xModel = getContextDocument_Lock();
697 if ( xModel.is() )
698 m_eDocumentType = DocumentClassification::classifyDocument( xModel );
699 else
701 OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
702 m_eDocumentType = eTextDocument;
703 // fallback, just to have a defined state
706 return m_eDocumentType;
710 bool FmXFormShell::IsReadonlyDoc_Lock() const
712 if (impl_checkDisposed_Lock())
713 return true;
715 FmFormModel* pModel = m_pShell->GetFormModel();
716 if ( pModel && pModel->GetObjectShell() )
717 return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
718 return true;
721 // EventListener
723 void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
725 SolarMutexGuard g;
727 if (m_xActiveController == e.Source)
729 // the controller will release, then release everything
730 stopListening_Lock();
731 m_xActiveForm = nullptr;
732 m_xActiveController = nullptr;
733 m_xNavigationController = nullptr;
735 m_aActiveControllerFeatures.dispose();
736 m_aNavControllerFeatures.dispose();
738 if ( m_pShell )
739 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
742 if (e.Source != m_xExternalViewController)
743 return;
745 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
746 OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
747 if (xFormController.is())
748 xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
750 if (m_xExternalViewController.is())
751 m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
753 m_xExternalViewController = nullptr;
754 m_xExternalDisplayedForm = nullptr;
755 m_xExtViewTriggerController = nullptr;
757 InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
761 void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
763 SolarMutexGuard g;
765 if (impl_checkDisposed_Lock())
766 return;
768 if (evt.PropertyName == FM_PROP_ROWCOUNT)
770 // The update following this forces a re-painting of the corresponding
771 // slots. But if I am not in the MainThread of the application (because,
772 // for example, a cursor is counting data sets at the moment and always
773 // gives me this PropertyChanges), this can clash with normal paints in
774 // the MainThread of the application. (Such paints happen, for example,
775 // if one simply places another application over the office and switches
776 // back again).
777 // Therefore the use of the SolarMutex, which safeguards that.
778 comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
779 if (rSolarSafety.tryToAcquire())
781 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
782 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(SID_FM_RECORD_TOTAL);
783 rSolarSafety.release();
785 else
787 // with the following the slot is invalidated asynchron
788 LockSlotInvalidation_Lock(true);
789 InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
790 LockSlotInvalidation_Lock(false);
794 // this may be called from a non-main-thread so invalidate the shell asynchronously
795 LockSlotInvalidation_Lock(true);
796 InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
797 LockSlotInvalidation_Lock(false);
801 void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
803 SolarMutexGuard g;
805 if (impl_checkDisposed_Lock())
806 return;
808 OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
810 if ( !(m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()) )
811 return;
813 // unfortunately, SFX requires sal_uInt16
814 ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );
816 // furthermore, SFX wants a terminating 0
817 aSlotIds.push_back( 0 );
819 // and, last but not least, SFX wants the ids to be sorted
820 ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
822 sal_uInt16 *pSlotIds = aSlotIds.data();
823 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( pSlotIds );
827 void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
829 SolarMutexGuard g;
831 if (impl_checkDisposed_Lock())
832 return;
834 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
835 m_pTextShell->formActivated( xController );
836 setActiveController_Lock(xController);
840 void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
842 SolarMutexGuard g;
844 if (impl_checkDisposed_Lock())
845 return;
847 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
848 m_pTextShell->formDeactivated( xController );
852 void FmXFormShell::disposing()
854 SolarMutexGuard g;
856 FmXFormShell_BASE::disposing();
858 if ( m_pShell && !m_pShell->IsDesignMode() )
859 setActiveController_Lock(nullptr, true);
860 // do NOT save the content of the old form (the second parameter tells this)
861 // if we're here, then we expect that PrepareClose has been called, and thus the user
862 // got a chance to commit or reject any changes. So in case we're here and there
863 // are still uncommitted changes, the user explicitly wanted this.
865 m_pTextShell->dispose();
867 m_xAttachedFrame = nullptr;
869 CloseExternalFormViewer_Lock();
871 while ( !m_aLoadingPages.empty() )
873 Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
874 m_aLoadingPages.pop();
878 if (m_nInvalidationEvent)
880 Application::RemoveUserEvent(m_nInvalidationEvent);
881 m_nInvalidationEvent = nullptr;
883 if ( m_nActivationEvent )
885 Application::RemoveUserEvent( m_nActivationEvent );
886 m_nActivationEvent = nullptr;
891 DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
892 // should have been deleted while being disposed
894 m_aMarkTimer.Stop();
897 DisableNotification();
899 RemoveElement_Lock(m_xForms);
900 m_xForms.clear();
902 impl_switchActiveControllerListening_Lock(false);
903 m_xActiveController = nullptr;
904 m_xActiveForm = nullptr;
906 m_pShell = nullptr;
907 m_xNavigationController = nullptr;
908 m_xCurrentForm = nullptr;
909 m_xLastGridFound = nullptr;
910 m_xAttachedFrame = nullptr;
911 m_xExternalViewController = nullptr;
912 m_xExtViewTriggerController = nullptr;
913 m_xExternalDisplayedForm = nullptr;
915 InterfaceBag().swap(m_aCurrentSelection);
917 m_aActiveControllerFeatures.dispose();
918 m_aNavControllerFeatures.dispose();
922 void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
924 if (impl_checkDisposed_Lock())
925 return;
927 if ( m_nLockSlotInvalidation )
929 OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
930 InvalidateSlot_Lock(_nId, false);
932 else
934 OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
935 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( _nId, true, true );
936 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update( _nId );
941 void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
943 if (impl_checkDisposed_Lock())
944 return;
946 if (m_nLockSlotInvalidation)
948 sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
949 m_arrInvalidSlots.emplace_back(nId, nFlags );
951 else
952 if (nId)
953 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(nId, true, bWithId);
954 else
955 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
959 void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
961 if (impl_checkDisposed_Lock())
962 return;
964 DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
966 if (bLock)
967 ++m_nLockSlotInvalidation;
968 else if (!--m_nLockSlotInvalidation)
970 // (asynchronously) invalidate everything accumulated during the locked phase
971 if (!m_nInvalidationEvent)
972 m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
977 IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
979 if (impl_checkDisposed_Lock())
980 return;
982 m_nInvalidationEvent = nullptr;
984 for (const auto& rInvalidSlot : m_arrInvalidSlots)
986 if (rInvalidSlot.id)
987 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
988 else
989 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
991 m_arrInvalidSlots.clear();
995 void FmXFormShell::ForceUpdateSelection_Lock()
997 if (impl_checkDisposed_Lock())
998 return;
1000 if (IsSelectionUpdatePending_Lock())
1002 m_aMarkTimer.Stop();
1004 // optionally turn off the invalidation of slots which is implicitly done by SetSelection
1005 LockSlotInvalidation_Lock(true);
1007 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
1009 LockSlotInvalidation_Lock(false);
1013 void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
1015 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1017 // the corresponding image at it
1018 rNewMenu.append(OUString::createFromAscii(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), OUString(aImgIds[i]));
1022 OString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
1024 static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
1026 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1028 if (nSlot == SelObjectSlotMap[i])
1029 return aConvertSlots[i];
1032 return OString();
1035 bool FmXFormShell::isControlConversionSlot(std::string_view rIdent)
1037 for (const auto& rConvertSlot : aConvertSlots)
1038 if (rIdent == rConvertSlot)
1039 return true;
1040 return false;
1043 void FmXFormShell::executeControlConversionSlot_Lock(std::string_view rIdent)
1045 OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
1046 InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
1047 if ( aSelectedElement == m_aCurrentSelection.end() )
1048 return;
1050 executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
1053 bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, std::string_view rIdent)
1055 if (impl_checkDisposed_Lock())
1056 return false;
1058 OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
1059 if ( !_rxObject.is() )
1060 return false;
1062 SdrPage* pPage = m_pShell->GetCurPage();
1063 FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1064 OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
1065 if ( !pFormPage )
1066 return false;
1068 OSL_ENSURE( isSolelySelected_Lock(_rxObject),
1069 "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
1071 for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
1073 if (rIdent == aConvertSlots[lookupSlot])
1075 Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
1077 FmFormObj* pFormObject = nullptr;
1078 SdrObjListIter aPageIter( pFormPage );
1079 while ( aPageIter.IsMore() )
1081 SdrObject* pCurrent = aPageIter.Next();
1082 pFormObject = FmFormObj::GetFormObject( pCurrent );
1083 if ( !pFormObject )
1084 continue;
1086 Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
1087 if ( xCurrentNormalized.get() == xNormalizedObject.get() )
1088 break;
1090 pFormObject = nullptr;
1093 if ( !pFormObject )
1094 return false;
1096 OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
1097 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1098 Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
1099 if (!xNewModel.is())
1100 return false;
1102 Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
1104 // transfer properties
1105 Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
1106 Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
1109 lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
1110 TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
1112 Sequence< css::script::ScriptEventDescriptor> aOldScripts;
1113 Reference< XChild> xChild(xOldModel, UNO_QUERY);
1114 if (xChild.is())
1116 Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
1118 // remember old script events
1119 Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
1120 if (xParent.is() && xEvManager.is())
1122 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1123 if (nIndex>=0 && nIndex<xParent->getCount())
1124 aOldScripts = xEvManager->getScriptEvents(nIndex);
1127 // replace the model within the parent container
1128 Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
1129 if (xIndexParent.is())
1131 // the form container works with FormComponents
1132 Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
1133 DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
1134 Any aNewModel(makeAny(xComponent));
1138 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1139 if (nIndex>=0 && nIndex<xParent->getCount())
1140 xIndexParent->replaceByIndex(nIndex, aNewModel);
1141 else
1143 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1144 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1145 if (xNewComponent.is())
1146 xNewComponent->dispose();
1147 return false;
1150 catch(Exception&)
1152 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1153 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1154 if (xNewComponent.is())
1155 xNewComponent->dispose();
1156 return false;
1162 // special handling for the LabelControl-property : can only be set when the model is placed
1163 // within the forms hierarchy
1164 if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
1168 xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
1170 catch(Exception&)
1176 // set new model
1177 pFormObject->SetChanged();
1178 pFormObject->SetUnoControlModel(xNewModel);
1180 // transfer script events
1181 // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
1182 if (aOldScripts.hasElements())
1184 // find the control for the model
1185 Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
1187 const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
1189 Reference< XControl> xControl;
1190 auto pControl = std::find_if(aControls.begin(), aControls.end(),
1191 [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
1192 if (pControl != aControls.end())
1193 xControl = *pControl;
1194 TransferEventScripts(xNewModel, xControl, aOldScripts);
1197 // transfer value bindings, if possible
1199 Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
1200 Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
1201 if ( xOldBindable.is() )
1205 if ( xNewBindable.is() )
1206 xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
1207 xOldBindable->setValueBinding( nullptr );
1209 catch(const Exception&)
1211 DBG_UNHANDLED_EXCEPTION("svx");
1215 // same for list entry sources
1217 Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
1218 Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
1219 if ( xOldSink.is() )
1223 if ( xNewSink.is() )
1224 xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
1225 xOldSink->setListEntrySource( nullptr );
1227 catch(const Exception&)
1229 DBG_UNHANDLED_EXCEPTION("svx");
1234 // create an undo action
1235 FmFormModel* pModel = m_pShell->GetFormModel();
1236 DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
1237 if (pModel && pModel->IsUndoEnabled() )
1239 pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
1241 else
1243 FmUndoModelReplaceAction::DisposeElement( xOldModel );
1246 return true;
1249 return false;
1252 bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::string_view rIdent)
1254 if ( m_aCurrentSelection.empty() )
1255 return false;
1257 InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
1258 Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
1259 if ( !xElementInfo.is() )
1260 // no service info -> cannot determine this
1261 return false;
1263 if ( ++aCheck != m_aCurrentSelection.end() )
1264 // more than one element
1265 return false;
1267 if ( Reference< XForm >::query( xElementInfo ).is() )
1268 // it's a form
1269 return false;
1271 sal_Int16 nObjectType = getControlTypeByObject( xElementInfo );
1273 if ( ( OBJ_FM_HIDDEN == nObjectType )
1274 || ( OBJ_FM_CONTROL == nObjectType )
1275 || ( OBJ_FM_GRID == nObjectType )
1277 return false; // those types cannot be converted
1279 DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
1280 "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
1282 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1283 if (rIdent == aConvertSlots[i])
1284 return nObjectTypes[i] != nObjectType;
1286 return true; // all other slots: assume "yes"
1289 void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu)
1291 for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
1293 // the context is already of a type that corresponds to the entry -> disable
1294 OString sIdent(aConvertSlots[i]);
1295 rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
1299 void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
1301 if (impl_checkDisposed_Lock())
1302 return;
1304 Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
1305 if (!xControlModels.is())
1306 return;
1308 for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
1310 Reference< XPropertySet> xModelSet;
1311 xControlModels->getByIndex(i) >>= xModelSet;
1312 if (!xModelSet.is())
1313 continue;
1315 if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
1316 continue;
1317 sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
1318 if (FormComponentType::GRIDCONTROL != nClassId)
1319 continue;
1321 if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
1322 continue;
1324 switch (nSync)
1326 case LoopGridsSync::DISABLE_SYNC:
1328 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
1330 break;
1331 case LoopGridsSync::FORCE_SYNC:
1333 Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
1334 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1335 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
1337 break;
1338 case LoopGridsSync::ENABLE_SYNC:
1340 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1342 break;
1345 if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
1347 xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
1348 Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
1349 if (xModelPropState.is())
1350 xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
1351 else
1352 xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
1358 Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
1360 if (impl_checkDisposed_Lock())
1361 return nullptr;
1363 SdrPageView* pPageView = nullptr;
1364 if ( m_pShell && m_pShell->GetFormView() )
1365 pPageView = m_pShell->GetFormView()->GetSdrPageView();
1367 Reference< XControlContainer> xControlContainer;
1368 if ( pPageView )
1369 xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
1371 return xControlContainer;
1375 void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
1377 if (impl_checkDisposed_Lock())
1378 return;
1380 OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
1381 if ( !_rxForForm.is() )
1382 return;
1386 Reference< XWindow > xParentWindow;
1387 if ( m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame() )
1388 xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame()->GetWindow() );
1390 Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
1391 comphelper::getProcessComponentContext(),
1392 _rxForForm, getControlContainerForView_Lock(), xParentWindow
1395 xDialog->execute();
1397 catch( const Exception& )
1399 TOOLS_WARN_EXCEPTION( "svx", "FmXFormShell::ExecuteTabOrderDialog" );
1404 void FmXFormShell::ExecuteSearch_Lock()
1406 if (impl_checkDisposed_Lock())
1407 return;
1409 // a collection of all (logical) forms
1410 FmFormArray().swap(m_aSearchForms);
1411 ::std::vector< OUString > aContextNames;
1412 impl_collectFormSearchContexts_nothrow_Lock(
1413 m_pShell->GetCurPage()->GetForms(), u"",
1414 m_aSearchForms, aContextNames);
1416 if ( m_aSearchForms.size() != aContextNames.size() )
1418 SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
1419 return;
1422 // filter out the forms which do not contain valid controls at all
1424 FmFormArray aValidForms;
1425 ::std::vector< OUString > aValidContexts;
1426 FmFormArray::const_iterator form = m_aSearchForms.begin();
1427 ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
1428 for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
1430 FmSearchContext aTestContext;
1431 aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
1432 sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
1433 if ( nValidControls > 0 )
1435 aValidForms.push_back( *form );
1436 aValidContexts.push_back( *contextName );
1440 m_aSearchForms.swap( aValidForms );
1441 aContextNames.swap( aValidContexts );
1444 if (m_aSearchForms.empty() )
1446 // there are no controls that meet all the conditions for a search
1447 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
1448 VclMessageType::Warning, VclButtonsType::Ok,
1449 SvxResId(RID_STR_NODATACONTROLS)));
1450 xBox->run();
1451 return;
1454 // now I need another 'initial context'
1455 sal_Int16 nInitialContext = 0;
1456 Reference<XForm> xActiveForm(getActiveForm_Lock());
1457 for ( size_t i=0; i<m_aSearchForms.size(); ++i )
1459 if (m_aSearchForms.at(i) == xActiveForm)
1461 nInitialContext = static_cast<sal_Int16>(i);
1462 break;
1466 // If the dialog should initially offer the text of the active control,
1467 // this must have an XTextComponent interface. An addition, this makes
1468 // sense only if the current field is also bound to a table (or whatever) field.
1469 OUString strActiveField;
1470 OUString strInitialText;
1471 // ... this I get from my FormController
1472 DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
1473 Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
1474 if (xActiveControl.is())
1476 // the control can tell me its model ...
1477 Reference< XControlModel> xActiveModel( xActiveControl->getModel());
1478 DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
1480 // I ask the model for the ControlSource property ...
1481 Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
1482 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
1484 Reference< XPropertySet> xField;
1485 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1486 if (xField.is()) // (only when the thing is really bound)
1488 // and the control itself for a TextComponent interface (so that I can pick up the text there)
1489 Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
1490 if (xText.is())
1492 strActiveField = getLabelName(xProperties);
1493 strInitialText = xText->getText();
1497 else
1499 // the control itself has no ControlSource, but maybe it is a GridControl
1500 Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
1501 if (xGrid.is())
1503 // for strActiveField I need the ControlSource of the column,
1504 // for that the columns container, for that the GridPeer
1505 Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
1506 Reference< XIndexAccess> xColumns;
1507 if (xGridPeer.is())
1508 xColumns = xGridPeer->getColumns();
1510 sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
1511 sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
1512 Reference< XPropertySet> xCurrentCol;
1513 if(xColumns.is())
1514 xColumns->getByIndex(nModelCol) >>= xCurrentCol;
1515 if (xCurrentCol.is())
1516 strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
1518 // the text of the current column
1519 Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
1520 Reference< XInterface> xCurControl;
1521 xColControls->getByIndex(nViewCol) >>= xCurControl;
1522 OUString sInitialText;
1523 if (IsSearchableControl(xCurControl, &sInitialText))
1524 strInitialText = sInitialText;
1529 // taking care of possible GridControls that I know
1530 LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
1532 // Now I am ready for the dialogue.
1533 // When the potential deadlocks caused by the use of the solar mutex in
1534 // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
1535 // placed here, because the search in a separate thread is nevertheless
1536 // somewhat more fluid. Should be, however, somehow made dependent of the
1537 // underlying cursor. DAO for example is not thread-safe.
1538 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1539 ScopedVclPtr<AbstractFmSearchDialog> pDialog(
1540 pFact->CreateFmSearchDialog(
1541 m_pShell->GetViewShell()->GetViewFrame()->GetFrameWeld(),
1542 strInitialText, aContextNames, nInitialContext,
1543 LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
1544 pDialog->SetActiveField( strActiveField );
1545 pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
1546 pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
1547 pDialog->Execute();
1548 pDialog.disposeAndClear();
1550 // restore GridControls again
1551 LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
1553 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
1554 // because I marked controls in OnFoundData (if I was there)
1558 bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
1560 if (impl_checkDisposed_Lock())
1561 return false;
1563 if (m_pShell->IsDesignMode())
1564 // in the design mode (without active controls) the main document is to take care of it
1565 return false;
1567 Reference<XForm> xForm(getActiveForm_Lock());
1568 if (!xForm.is())
1569 // no current form (in particular no current control) -> the main document is to take care
1570 return false;
1572 Reference< XRowSet> xDB(xForm, UNO_QUERY);
1573 DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
1575 Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
1576 if (xSupplier.is())
1578 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1579 if (xSet.is())
1583 Any aVal( xSet->getPropertyValue("TwoDigitDateStart") );
1584 aVal >>= n;
1585 return true;
1587 catch(Exception&)
1593 return false;
1597 void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
1599 if (impl_checkDisposed_Lock())
1600 return;
1602 Reference<XForm> xActiveForm(getActiveForm_Lock());
1603 Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
1604 if ( xActiveRowSet.is() )
1606 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
1607 if (xSupplier.is())
1609 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1610 if (xSet.is())
1614 xSet->setPropertyValue("TwoDigitDateStart", makeAny<sal_uInt16>(n));
1616 catch(Exception&)
1618 TOOLS_WARN_EXCEPTION("svx.form", "");
1622 return;
1626 // no active form found -> iterate through all current forms
1627 Reference< XIndexAccess> xCurrentForms( m_xForms);
1628 if (!xCurrentForms.is())
1629 { // in the alive mode, my forms are not set, but the ones on the page are
1630 if (m_pShell->GetCurPage())
1631 xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
1633 if (!xCurrentForms.is())
1634 return;
1636 ::comphelper::IndexAccessIterator aIter(xCurrentForms);
1637 Reference< XInterface> xCurrentElement( aIter.Next());
1638 while (xCurrentElement.is())
1640 // is the current element a DatabaseForm?
1641 Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
1642 if ( xElementAsRowSet.is() )
1644 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
1645 if (!xSupplier.is())
1646 continue;
1648 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1649 if (xSet.is())
1653 xSet->setPropertyValue("TwoDigitDateStart", makeAny<sal_uInt16>(n));
1655 catch(Exception&)
1657 TOOLS_WARN_EXCEPTION("svx.form", "");
1662 xCurrentElement = aIter.Next();
1667 void FmXFormShell::CloseExternalFormViewer_Lock()
1669 if (impl_checkDisposed_Lock())
1670 return;
1672 if (!m_xExternalViewController.is())
1673 return;
1675 Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
1676 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
1677 if (!xCommLink.is())
1678 return;
1680 xExternalViewFrame->setComponent(nullptr,nullptr);
1681 ::comphelper::disposeComponent(xExternalViewFrame);
1682 m_xExternalViewController = nullptr;
1683 m_xExtViewTriggerController = nullptr;
1684 m_xExternalDisplayedForm = nullptr;
1688 Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _xForm) const
1690 if (impl_checkDisposed_Lock())
1691 return nullptr;
1693 Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1694 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1696 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1697 return m_xExternalDisplayedForm;
1699 return _xForm;
1703 Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
1705 if (impl_checkDisposed_Lock())
1706 return nullptr;
1708 Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1709 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1711 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1712 return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
1714 return _xForm;
1718 namespace
1720 bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
1722 return ( _nWhich == SID_FM_RECORD_FIRST )
1723 || ( _nWhich == SID_FM_RECORD_PREV )
1724 || ( _nWhich == SID_FM_RECORD_NEXT )
1725 || ( _nWhich == SID_FM_RECORD_LAST )
1726 || ( _nWhich == SID_FM_RECORD_NEW );
1731 bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState ) const
1733 const svx::ControllerFeatures& rController =
1734 lcl_isNavigationRelevant( _nSlot )
1735 ? getNavControllerFeatures_Lock()
1736 : getActiveControllerFeatures_Lock();
1738 if ( !_pCompleteState )
1739 return rController->isEnabled( _nSlot );
1741 rController->getState( _nSlot, *_pCompleteState );
1742 return _pCompleteState->Enabled;
1746 void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
1748 const svx::ControllerFeatures& rController =
1749 lcl_isNavigationRelevant( _nSlot )
1750 ? getNavControllerFeatures_Lock()
1751 : getActiveControllerFeatures_Lock();
1753 rController->execute( _nSlot );
1755 if ( _nSlot != SID_FM_RECORD_UNDO )
1756 return;
1758 // if we're doing an UNDO, *and* if the affected form is the form which we also display
1759 // as external view, then we need to reset the controls of the external form, too
1760 if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
1761 return;
1763 Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
1764 if ( !xContainer.is() )
1765 return;
1767 Reference< XReset > xReset;
1768 for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
1770 if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
1772 // no resets on sub forms
1773 Reference< XForm > xAsForm( xReset, UNO_QUERY );
1774 if ( !xAsForm.is() )
1775 xReset->reset();
1781 void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
1783 if ( !m_xActiveController.is() )
1784 return;
1786 if ( _bListen )
1787 m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
1788 else
1789 m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
1793 void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
1795 if (impl_checkDisposed_Lock())
1796 return;
1798 if (m_bChangingDesignMode)
1799 return;
1800 DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
1802 // if the routine has been called a second time,
1803 // the focus should no longer be transferred
1804 if (m_bInActivate)
1806 m_bSetFocus = xController != m_xActiveController;
1807 return;
1810 if (xController == m_xActiveController)
1811 return;
1813 // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
1814 Reference< XResultSet> xNavigationForm;
1815 if (m_xNavigationController.is())
1816 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1818 m_bInActivate = true;
1820 // check if the 2 controllers serve different forms
1821 Reference< XResultSet> xOldForm;
1822 if (m_xActiveController.is())
1823 xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
1824 Reference< XResultSet> xNewForm;
1825 if (xController.is())
1826 xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
1827 xOldForm = getInternalForm_Lock(xOldForm);
1828 xNewForm = getInternalForm_Lock(xNewForm);
1830 bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
1831 bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
1832 // we save the content of the old form if we move to a new form, and saving old content is allowed
1834 if ( m_xActiveController.is() && bNeedSave )
1836 // save content on change of the controller; a commit has already been executed
1837 if ( m_aActiveControllerFeatures->commitCurrentControl() )
1839 m_bSetFocus = true;
1840 if ( m_aActiveControllerFeatures->isModifiedRow() )
1842 bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
1843 bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
1844 if ( !bResult && m_bSetFocus )
1846 // if we couldn't save the current record, set the focus back to the
1847 // current control
1848 Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
1849 if ( xWindow.is() )
1850 xWindow->setFocus();
1851 m_bInActivate = false;
1852 return;
1854 else if ( bResult && bIsNew )
1856 Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
1857 if ( xCursor.is() )
1859 DO_SAFE( xCursor->last(); );
1866 stopListening_Lock();
1868 impl_switchActiveControllerListening_Lock(false);
1870 m_aActiveControllerFeatures.dispose();
1871 m_xActiveController = xController;
1872 if ( m_xActiveController.is() )
1873 m_aActiveControllerFeatures.assign( m_xActiveController );
1875 impl_switchActiveControllerListening_Lock(true);
1877 if ( m_xActiveController.is() )
1878 m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
1879 else
1880 m_xActiveForm = nullptr;
1882 startListening_Lock();
1884 // activate all dispatchers belonging to form of the new navigation controller
1885 xNavigationForm = nullptr;
1886 if (m_xNavigationController.is())
1887 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1889 m_bInActivate = false;
1891 m_pShell->UIFeatureChanged();
1892 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
1894 InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
1898 void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
1900 _rSelection = m_aCurrentSelection;
1904 bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
1906 m_aLastKnownMarkedControls.clear();
1908 if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
1909 collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
1911 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1915 bool FmXFormShell::selectLastMarkedControls_Lock()
1917 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1921 bool FmXFormShell::setCurrentSelection_Lock( InterfaceBag&& _rSelection )
1923 if (impl_checkDisposed_Lock())
1924 return false;
1926 DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
1928 if ( _rSelection.empty() && m_aCurrentSelection.empty() )
1929 // nothing to do
1930 return false;
1932 if ( _rSelection.size() == m_aCurrentSelection.size() )
1934 InterfaceBag::const_iterator aNew = _rSelection.begin();
1935 InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
1936 for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
1938 OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
1939 OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
1941 if ( aNew->get() != aOld->get() )
1942 break;
1945 if ( aNew == _rSelection.end() )
1946 // both bags equal
1947 return false;
1950 // the following is some strange code to ensure that when you have two grid controls in a document,
1951 // only one of them can have a selected column.
1952 // TODO: this should happen elsewhere, but not here - shouldn't it?
1953 if ( !m_aCurrentSelection.empty() )
1955 Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
1956 Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
1958 // is there nothing to be selected, or the parents differ, and the parent of the current object
1959 // is a selection supplier, then deselect
1960 if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
1962 Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
1963 if ( xSel.is() )
1964 xSel->select( Any() );
1968 m_aCurrentSelection = _rSelection;
1970 // determine the form which all the selected objects belong to, if any
1971 Reference< XForm > xNewCurrentForm;
1972 for (const auto& rpSelection : m_aCurrentSelection)
1974 Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
1975 OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
1977 if ( !xNewCurrentForm.is() )
1978 { // the first form we encountered
1979 xNewCurrentForm = xThisRoundsForm;
1981 else if ( xNewCurrentForm != xThisRoundsForm )
1982 { // different forms -> no "current form" at all
1983 xNewCurrentForm.clear();
1984 break;
1988 if ( !m_aCurrentSelection.empty() )
1989 impl_updateCurrentForm_Lock(xNewCurrentForm);
1991 // ensure some slots are updated
1992 for (sal_Int16 i : SelObjectSlotMap)
1993 InvalidateSlot_Lock(i, false);
1995 return true;
1999 bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
2001 return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
2005 void FmXFormShell::forgetCurrentForm_Lock()
2007 if ( !m_xCurrentForm.is() )
2008 return;
2010 // reset ...
2011 impl_updateCurrentForm_Lock(nullptr);
2013 // ... and try finding a new current form
2014 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
2015 impl_defaultCurrentForm_nothrow_Lock();
2019 void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
2021 if (impl_checkDisposed_Lock())
2022 return;
2024 m_xCurrentForm = _rxNewCurForm;
2026 // propagate to the FormPage(Impl)
2027 FmFormPage* pPage = m_pShell->GetCurPage();
2028 if ( pPage )
2029 pPage->GetImpl().setCurForm( m_xCurrentForm );
2031 // ensure the UI which depends on the current form is up-to-date
2032 for (sal_Int16 i : DlgSlotMap)
2033 InvalidateSlot_Lock(i, false);
2037 void FmXFormShell::startListening_Lock()
2039 if (impl_checkDisposed_Lock())
2040 return;
2042 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2043 if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
2045 Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
2046 if (xActiveFormSet.is())
2048 // if there is a data source, then build the listener
2049 // TODO: this is strange - shouldn't this depend on a isLoaded instead of
2050 // a "has command value"? Finally, the command value only means that it was
2051 // intended to be loaded, not that it actually *is* loaded
2052 OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
2053 if (!aSource.isEmpty())
2055 m_bDatabaseBar = true;
2057 xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2059 switch (m_eNavigate)
2061 case NavigationBarMode_PARENT:
2063 // search for the controller via which navigation is possible
2064 Reference< XChild> xChild = m_xActiveController;
2065 Reference< runtime::XFormController > xParent;
2066 while (xChild.is())
2068 xChild.set(xChild->getParent(), UNO_QUERY);
2069 xParent.set(xChild, UNO_QUERY);
2070 Reference< XPropertySet> xParentSet;
2071 if (xParent.is())
2072 xParentSet.set(xParent->getModel(), UNO_QUERY);
2073 if (xParentSet.is())
2075 xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2076 if (m_eNavigate == NavigationBarMode_CURRENT)
2077 break;
2080 m_xNavigationController = xParent;
2082 break;
2084 case NavigationBarMode_CURRENT:
2085 m_xNavigationController = m_xActiveController;
2086 break;
2088 default:
2089 m_xNavigationController = nullptr;
2090 m_bDatabaseBar = false;
2093 m_aNavControllerFeatures.dispose();
2094 if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
2095 m_aNavControllerFeatures.assign( m_xNavigationController );
2097 // because of RecordCount, listen at the controller which controls the navigation
2098 Reference< XPropertySet> xNavigationSet;
2099 if (m_xNavigationController.is())
2101 xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
2102 if (xNavigationSet.is())
2103 xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
2105 return;
2110 m_eNavigate = NavigationBarMode_NONE;
2111 m_bDatabaseBar = false;
2112 m_xNavigationController = nullptr;
2116 void FmXFormShell::stopListening_Lock()
2118 if (impl_checkDisposed_Lock())
2119 return;
2121 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2122 if ( xDatabaseForm.is() )
2124 if (m_xNavigationController.is())
2126 Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
2127 if (xSet.is())
2128 xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2133 m_bDatabaseBar = false;
2134 m_eNavigate = NavigationBarMode_NONE;
2135 m_xNavigationController = nullptr;
2139 void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
2141 if (impl_checkDisposed_Lock())
2142 return;
2144 // if the window is already visible, only update the state
2145 bool bHasChild = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_PROPERTIES );
2146 if ( bHasChild && bShow )
2147 UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
2149 // else toggle state
2150 else
2151 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2153 InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
2154 InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
2158 IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
2160 if (impl_checkDisposed_Lock())
2161 return;
2163 DBG_ASSERT((rfriWhere.nContext >= 0) && (rfriWhere.nContext < static_cast<sal_Int16>(m_aSearchForms.size())),
2164 "FmXFormShell::OnFoundData : invalid context!");
2165 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2166 DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
2168 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2169 if (!xCursor.is())
2170 return; // what should I do there?
2172 // to the record
2175 xCursor->moveToBookmark(rfriWhere.aPosition);
2177 catch(const SQLException&)
2179 OSL_FAIL("Can position on bookmark!");
2182 LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
2184 // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
2185 SAL_WARN_IF(o3tl::make_unsigned(rfriWhere.nFieldPos) >=
2186 m_arrSearchedControls.size(),
2187 "svx.form", "FmXFormShell::OnFoundData : invalid index!");
2188 SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
2190 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2191 m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
2193 FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
2194 Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
2195 DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
2196 if ( !xControlModel.is() )
2197 return;
2199 // disable the permanent cursor for the last grid we found a record
2200 if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
2202 Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
2203 xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, makeAny( false ) );
2204 Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
2205 if (xOldSetState.is())
2206 xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
2207 else
2208 xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
2211 // if the field is in a GridControl, I have to additionally go into the corresponding column there
2212 sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
2213 if (nGridColumn != -1)
2214 { // unfortunately, I have to first get the control again
2215 Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
2216 Reference< XGrid> xGrid(xControl, UNO_QUERY);
2217 DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
2218 // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
2220 // enable a permanent cursor for the grid so we can see the found text
2221 Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
2222 DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
2223 xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, makeAny( true ) );
2224 xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, makeAny( COL_LIGHTRED ) );
2225 m_xLastGridFound = xControlModel;
2227 if ( xGrid.is() )
2228 xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
2231 // As the cursor has been repositioned, I have (in positioned) invalidated
2232 // my form bar slots. But that does not take effect here unfortunately, as
2233 // generally the (modal) search dialog is of course at the top ... So, force ...
2234 sal_uInt16 nPos = 0;
2235 while (DatabaseSlotMap[nPos])
2236 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(DatabaseSlotMap[nPos++]);
2237 // unfortunately the update goes against the invalidate with only individual slots
2241 IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
2243 if (impl_checkDisposed_Lock())
2244 return;
2246 DBG_ASSERT((rfriWhere.nContext >= 0) && (rfriWhere.nContext < static_cast<sal_Int16>(m_aSearchForms.size())),
2247 "FmXFormShell::OnCanceledNotFound : invalid context!");
2248 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2249 DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
2251 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2252 if (!xCursor.is())
2253 return; // what should I do there?
2255 // to the record
2258 xCursor->moveToBookmark(rfriWhere.aPosition);
2260 catch(const SQLException&)
2262 OSL_FAIL("Can position on bookmark!");
2266 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2270 IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
2272 if (impl_checkDisposed_Lock())
2273 return 0;
2275 DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
2276 Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
2277 DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
2279 Reference< XResultSet> xIter(xForm, UNO_QUERY);
2280 DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
2283 // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
2284 OUString strFieldList, sFieldDisplayNames;
2285 m_arrSearchedControls.clear();
2286 m_arrRelativeGridColumn.clear();
2288 // small problem: To mark found fields, I need SdrObjects. To determine which controls
2289 // to include in the search, I need Controls (that is, XControl interfaces). So I have
2290 // to iterate over one of them and get the other in some way. Unfortunately, there is
2291 // no direct connection between the two worlds (except from a GetUnoControl to a
2292 // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
2293 // However I can get to the Model from the Control and also from the SdrObject, and in
2294 // this way the assignment SdrObject<->Control is possible with a double loop.
2295 // The alternative to this (ugly but certainly not entirely fixable) solution would be
2296 // to renounce the caching of the SdrObjects, which would lead to significant extra
2297 // work in OnFoundData (since there I'd have to get the SdrObject first thing every
2298 // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
2299 // do that here.
2301 Reference< XNameAccess> xValidFormFields;
2302 Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
2303 DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
2304 if (xSupplyCols.is())
2305 xValidFormFields = xSupplyCols->getColumns();
2306 DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
2308 // current Page/Controller
2309 FmFormPage* pCurrentPage = m_pShell->GetCurPage();
2310 assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
2311 // Search all SdrControls of this page...
2312 OUString sControlSource, aName;
2314 SdrObjListIter aPageIter( pCurrentPage );
2315 while ( aPageIter.IsMore() )
2317 SdrObject* pCurrent = aPageIter.Next();
2318 FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
2319 // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
2321 if ( !pFormObject )
2322 continue;
2324 // the current object's model, in different tastes
2325 Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
2326 Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
2327 DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
2328 if ( !xCurrentFormComponent.is() )
2329 continue;
2331 // does the component belong to the form which we're interested in?
2332 if ( xCurrentFormComponent->getParent() != xForm )
2333 continue;
2335 // ... ask for the ControlSource property
2336 SearchableControlIterator iter( xCurrentFormComponent );
2337 Reference< XControl> xControl;
2338 // the control that has model xControlModel
2339 // (the following while can be passed through several times, without the Control
2340 // being modified, so I don't have to search every time from scratch)
2342 Reference< XInterface > xSearchable( iter.Next() );
2343 while ( xSearchable.is() )
2345 sControlSource = iter.getCurrentValue();
2346 if ( sControlSource.isEmpty() )
2348 // the current element has no ControlSource, so it is a GridControl (that
2349 // is the only thing that still permits the SearchableControlIteratore)
2350 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2351 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2353 Reference< XGridPeer> xGridPeer;
2354 if ( xControl.is() )
2355 xGridPeer.set( xControl->getPeer(), UNO_QUERY );
2358 if (!xGridPeer.is())
2359 break;
2361 Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
2362 if (!xPeerContainer.is())
2363 break;
2365 Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
2366 DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
2367 // the case 'no columns' should be indicated with an empty container, I think ...
2368 DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
2370 Reference< XInterface> xCurrentColumn;
2371 for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
2373 xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
2374 if (!xCurrentColumn.is())
2375 continue;
2377 // can we use this column control for searching ?
2378 if (!IsSearchableControl(xCurrentColumn))
2379 continue;
2381 sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
2382 Reference< XPropertySet> xCurrentColModel;
2383 xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
2384 aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
2385 // the cursor has a field matching the control source ?
2386 if (xValidFormFields->hasByName(aName))
2388 strFieldList += aName + ";";
2390 sFieldDisplayNames +=
2391 ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
2392 ";";
2394 rfmscContextInfo.arrFields.push_back(xCurrentColumn);
2396 // and the SdrOject to the Field
2397 m_arrSearchedControls.push_back(pCurrent);
2398 // the number of the column
2399 m_arrRelativeGridColumn.push_back(nViewPos);
2402 } while (false);
2404 else
2406 if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
2408 // now I need the Control to SdrObject
2409 if (!xControl.is())
2411 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2412 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2415 if (IsSearchableControl(xControl))
2417 // all tests passed -> take along in the list
2418 strFieldList += sControlSource + ";";
2420 // the label which should appear for the control :
2421 sFieldDisplayNames +=
2422 getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
2423 ";";
2425 // mark the SdrObject (accelerates the treatment in OnFoundData)
2426 m_arrSearchedControls.push_back(pCurrent);
2428 // the number of the column (here a dummy, since it is only interesting for GridControls)
2429 m_arrRelativeGridColumn.push_back(-1);
2431 // and for the formatted search...
2432 rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
2437 xSearchable = iter.Next();
2441 strFieldList = comphelper::string::stripEnd(strFieldList, ';');
2442 sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
2444 if (rfmscContextInfo.arrFields.empty())
2446 rfmscContextInfo.arrFields.clear();
2447 rfmscContextInfo.xCursor = nullptr;
2448 rfmscContextInfo.strUsedFields.clear();
2449 return 0;
2452 rfmscContextInfo.xCursor = xIter;
2453 rfmscContextInfo.strUsedFields = strFieldList;
2454 rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
2456 // 66463 - 31.05.99 - FS
2457 // when the cursor is a non-STANDARD RecordMode, set it back
2458 Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
2459 Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
2460 if (xUpdateCursor.is() && xCursorSet.is())
2462 if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
2463 xUpdateCursor->moveToCurrentRow();
2464 else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
2465 xUpdateCursor->cancelRowUpdates();
2468 return rfmscContextInfo.arrFields.size();
2471 // XContainerListener
2473 void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
2475 SolarMutexGuard g;
2477 if (impl_checkDisposed_Lock())
2478 return;
2480 // new object to listen to
2481 Reference< XInterface> xTemp;
2482 evt.Element >>= xTemp;
2483 AddElement_Lock(xTemp);
2485 m_pShell->DetermineForms(true);
2489 void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
2491 SolarMutexGuard g;
2493 if (impl_checkDisposed_Lock() )
2494 return;
2496 Reference< XInterface> xTemp;
2497 evt.ReplacedElement >>= xTemp;
2498 RemoveElement_Lock(xTemp);
2499 evt.Element >>= xTemp;
2500 AddElement_Lock(xTemp);
2504 void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
2506 SolarMutexGuard g;
2508 if (impl_checkDisposed_Lock())
2509 return;
2511 Reference< XInterface> xTemp;
2512 evt.Element >>= xTemp;
2513 RemoveElement_Lock(xTemp);
2515 m_pShell->DetermineForms(true);
2519 void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
2521 if (impl_checkDisposed_Lock())
2522 return;
2524 Reference< XIndexAccess > xForms;
2526 FmFormPage* pPage = m_pShell->GetCurPage();
2527 if ( pPage && m_pShell->m_bDesignMode )
2528 xForms = pPage->GetForms( false );
2530 if ( m_xForms != xForms )
2532 RemoveElement_Lock( m_xForms );
2533 m_xForms = xForms;
2534 AddElement_Lock(m_xForms);
2537 SolarMutexGuard g;
2538 m_pShell->DetermineForms( _bInvalidate );
2542 void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
2544 if (impl_checkDisposed_Lock())
2545 return;
2546 impl_AddElement_nothrow(_xElement);
2549 void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
2551 // listen at the container
2552 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2553 if (xContainer.is())
2555 const sal_uInt32 nCount = xContainer->getCount();
2556 Reference< XInterface> xElement;
2557 for (sal_uInt32 i = 0; i < nCount; ++i)
2559 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2560 impl_AddElement_nothrow(xElement);
2563 const Reference< XContainer> xCont(Element, UNO_QUERY);
2564 if (xCont.is())
2565 xCont->addContainerListener(this);
2568 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2569 if (xSelSupplier.is())
2570 xSelSupplier->addSelectionChangeListener(this);
2574 void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
2576 if (impl_checkDisposed_Lock())
2577 return;
2578 impl_RemoveElement_nothrow_Lock(Element);
2581 void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
2583 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2584 if (xSelSupplier.is())
2585 xSelSupplier->removeSelectionChangeListener(this);
2587 // remove connection to children
2588 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2589 if (xContainer.is())
2591 const Reference< XContainer> xCont(Element, UNO_QUERY);
2592 if (xCont.is())
2593 xCont->removeContainerListener(this);
2595 const sal_uInt32 nCount = xContainer->getCount();
2596 Reference< XInterface> xElement;
2597 for (sal_uInt32 i = 0; i < nCount; i++)
2599 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2600 impl_RemoveElement_nothrow_Lock(xElement);
2604 auto wasSelectedPos = m_aCurrentSelection.find( Element );
2605 if ( wasSelectedPos != m_aCurrentSelection.end() )
2606 m_aCurrentSelection.erase( wasSelectedPos );
2610 void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
2612 SolarMutexGuard g;
2614 if (impl_checkDisposed_Lock())
2615 return;
2617 Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
2618 Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
2619 // a selection was removed, this can only be done by the shell
2620 if ( !xSelObj.is() )
2621 return;
2623 EnableTrackProperties_Lock(false);
2625 bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
2627 InterfaceBag aNewSelection;
2628 aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
2630 if (setCurrentSelection_Lock(std::move(aNewSelection)) && IsPropBrwOpen_Lock())
2631 ShowSelectionProperties_Lock(true);
2633 EnableTrackProperties_Lock(true);
2635 if ( bMarkChanged )
2636 m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
2640 IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
2642 if (impl_checkDisposed_Lock())
2643 return;
2645 if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
2646 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
2650 void FmXFormShell::SetSelectionDelayed_Lock()
2652 if (impl_checkDisposed_Lock())
2653 return;
2655 if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
2656 m_aMarkTimer.Start();
2660 void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
2662 if (impl_checkDisposed_Lock())
2663 return;
2665 DetermineSelection_Lock(rMarkList);
2666 m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
2670 void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
2672 if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
2673 ShowSelectionProperties_Lock(true);
2677 bool FmXFormShell::IsPropBrwOpen_Lock() const
2679 if (impl_checkDisposed_Lock())
2680 return false;
2682 return m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()
2683 && m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
2687 class FmXFormShell::SuspendPropertyTracking
2689 private:
2690 FmXFormShell& m_rShell;
2691 bool m_bEnabled;
2693 public:
2694 explicit SuspendPropertyTracking( FmXFormShell& _rShell )
2695 :m_rShell( _rShell )
2696 ,m_bEnabled( false )
2698 if (m_rShell.IsTrackPropertiesEnabled_Lock())
2700 m_rShell.EnableTrackProperties_Lock(false);
2701 m_bEnabled = true;
2705 ~SuspendPropertyTracking( )
2707 if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
2708 m_rShell.EnableTrackProperties_Lock(true);
2713 void FmXFormShell::SetDesignMode_Lock(bool bDesign)
2715 if (impl_checkDisposed_Lock())
2716 return;
2718 DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
2719 m_bChangingDesignMode = true;
2721 // 67506 - 15.07.99 - FS
2722 // if we're switching off the design mode we have to force the property browser to be closed
2723 // so it can commit it's changes _before_ we load the forms
2724 if (!bDesign)
2726 m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
2727 if (m_bHadPropertyBrowserInDesignMode)
2728 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2731 FmFormView* pFormView = m_pShell->GetFormView();
2732 if (bDesign)
2734 // we are currently filtering, so stop filtering
2735 if (m_bFilterMode)
2736 stopFiltering_Lock(false);
2738 // unsubscribe from the objects of my MarkList
2739 pFormView->GetImpl()->stopMarkListWatching();
2741 else
2743 m_aMarkTimer.Stop();
2745 SuspendPropertyTracking aSuspend( *this );
2746 pFormView->GetImpl()->saveMarkList();
2749 if (bDesign && m_xExternalViewController.is())
2750 CloseExternalFormViewer_Lock();
2752 pFormView->ChangeDesignMode(bDesign);
2754 // notify listeners
2755 FmDesignModeChangedHint aChangedHint( bDesign );
2756 m_pShell->Broadcast(aChangedHint);
2758 m_pShell->m_bDesignMode = bDesign;
2759 UpdateForms_Lock(false);
2761 m_pTextShell->designModeChanged();
2763 if (bDesign)
2765 SdrMarkList aList;
2767 // during changing the mark list, don't track the selected objects in the property browser
2768 SuspendPropertyTracking aSuspend( *this );
2769 // restore the marks
2770 pFormView->GetImpl()->restoreMarkList( aList );
2773 // synchronize with the restored mark list
2774 if ( aList.GetMarkCount() )
2775 SetSelection_Lock(aList);
2777 else
2779 // subscribe to the model of the view (so that I'm informed when someone deletes
2780 // during the alive mode controls that I had saved in the saveMarklist (60343)
2781 pFormView->GetImpl()->startMarkListWatching();
2784 m_pShell->UIFeatureChanged();
2786 // 67506 - 15.07.99 - FS
2787 if (bDesign && m_bHadPropertyBrowserInDesignMode)
2789 // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
2790 // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
2791 // That's why we use an asynchron execution on the dispatcher.
2792 // (And that's why this has to be done AFTER the UIFeatureChanged.)
2793 m_pShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
2795 m_bChangingDesignMode = false;
2799 Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
2801 if (impl_checkDisposed_Lock())
2802 return nullptr;
2804 Reference< XControl > xControl;
2807 Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
2809 const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
2810 // ... that I can then search
2811 for (Reference< XControl > const & control : seqControls)
2813 xControl.set( control, UNO_SET_THROW );
2814 Reference< XControlModel > xCurrentModel( xControl->getModel() );
2815 if ( xCurrentModel == i_rxModel )
2816 break;
2817 xControl.clear();
2820 if ( !xControl.is() )
2822 // fallback (some controls might not have been created, yet, since they were never visible so far)
2823 Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
2824 const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
2825 ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
2827 const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
2828 ENSURE_OR_THROW( pSdrView, "no current view" );
2830 xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow->GetOutDev() ), UNO_SET_THROW );
2833 catch( const Exception& )
2835 DBG_UNHANDLED_EXCEPTION("svx");
2838 OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
2839 return xControl;
2842 // note: _out_rForms is a member so needs lock
2843 void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
2844 std::u16string_view _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
2848 Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
2849 if ( !xContainer.is() )
2850 return;
2852 sal_Int32 nCount( xContainer->getCount() );
2853 if ( nCount == 0 )
2854 return;
2856 OUString sCurrentFormName;
2857 OUStringBuffer aNextLevelPrefix;
2858 for ( sal_Int32 i=0; i<nCount; ++i )
2860 // is the current child a form?
2861 Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
2862 if ( !xCurrentAsForm.is() )
2863 continue;
2865 Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
2866 sCurrentFormName = xNamed->getName();
2868 // the name of the current form
2869 OUString sCompleteCurrentName( sCurrentFormName );
2870 if ( !_rCurrentLevelPrefix.empty() )
2872 sCompleteCurrentName += OUString::Concat(" (") + _rCurrentLevelPrefix + ")";
2875 // the prefix for the next level
2876 aNextLevelPrefix = _rCurrentLevelPrefix;
2877 if ( !_rCurrentLevelPrefix.empty() )
2878 aNextLevelPrefix.append( '/' );
2879 aNextLevelPrefix.append( sCurrentFormName );
2881 // remember both the form and its "display name"
2882 _out_rForms.push_back( xCurrentAsForm );
2883 _out_rNames.push_back( sCompleteCurrentName );
2885 // and descend
2886 impl_collectFormSearchContexts_nothrow_Lock(
2887 xCurrentAsForm, aNextLevelPrefix.makeStringAndClear(),
2888 _out_rForms, _out_rNames);
2891 catch( const Exception& )
2893 DBG_UNHANDLED_EXCEPTION("svx");
2898 void FmXFormShell::startFiltering_Lock()
2900 if (impl_checkDisposed_Lock())
2901 return;
2903 // setting all forms in filter mode
2904 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2906 // if the active controller is our external one we have to use the trigger controller
2907 Reference< XControlContainer> xContainer;
2908 if (getActiveController_Lock() == m_xExternalViewController)
2910 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
2911 xContainer = m_xExtViewTriggerController->getContainer();
2913 else
2914 xContainer = getActiveController_Lock()->getContainer();
2916 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow( xContainer );
2917 if ( pAdapter.is() )
2919 const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
2920 for (const auto& rpController : rControllerList)
2922 Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
2923 if (xModeSelector.is())
2924 xModeSelector->setMode( "FilterMode" );
2928 m_bFilterMode = true;
2930 m_pShell->UIFeatureChanged();
2931 SfxViewFrame* pViewFrame = m_pShell->GetViewShell()->GetViewFrame();
2932 pViewFrame->GetBindings().InvalidateShell( *m_pShell );
2934 if ( pViewFrame->KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
2935 && !pViewFrame->HasChildWindow( SID_FM_FILTER_NAVIGATOR )
2938 pViewFrame->ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
2943 static void saveFilter(const Reference< runtime::XFormController >& _rxController)
2945 Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
2946 Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
2948 // call the subcontroller
2949 Reference< runtime::XFormController > xController;
2950 for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
2952 _rxController->getByIndex(i) >>= xController;
2953 saveFilter(xController);
2959 xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
2960 xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, makeAny( true ) );
2962 catch (const Exception& )
2964 DBG_UNHANDLED_EXCEPTION("svx");
2970 void FmXFormShell::stopFiltering_Lock(bool bSave)
2972 if (impl_checkDisposed_Lock())
2973 return;
2975 m_bFilterMode = false;
2977 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2979 // if the active controller is our external one we have to use the trigger controller
2980 Reference< XControlContainer> xContainer;
2981 if (getActiveController_Lock() == m_xExternalViewController)
2983 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
2984 xContainer = m_xExtViewTriggerController->getContainer();
2986 else
2987 xContainer = getActiveController_Lock()->getContainer();
2989 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow(xContainer);
2990 if ( pAdapter.is() )
2992 const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
2993 ::std::vector < OUString > aOriginalFilters;
2994 ::std::vector < bool > aOriginalApplyFlags;
2996 if (bSave)
2998 for (const auto& rpController : rControllerList)
3000 // remember the current filter settings in case we're going to reload the forms below (which may fail)
3003 Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
3004 aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
3005 aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
3007 catch(Exception&)
3009 OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
3010 // put dummies into the arrays so the they have the right size
3012 if (aOriginalFilters.size() == aOriginalApplyFlags.size())
3013 // the first getPropertyValue failed -> use two dummies
3014 aOriginalFilters.emplace_back( );
3015 aOriginalApplyFlags.push_back( false );
3017 saveFilter(rpController);
3020 for (const auto& rController : rControllerList)
3023 Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
3024 if (xModeSelector.is())
3025 xModeSelector->setMode( "DataMode" );
3027 if (bSave) // execute the filter
3029 const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
3030 for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
3031 j != rControllers.end(); ++j)
3033 Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
3034 if (!xReload.is())
3035 continue;
3036 Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
3040 xReload->reload();
3042 catch(Exception&)
3044 TOOLS_WARN_EXCEPTION("svx.form", "");
3047 if (!isRowSetAlive(xFormSet))
3048 { // something went wrong -> restore the original state
3049 OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
3050 bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
3053 xFormSet->setPropertyValue(FM_PROP_FILTER, makeAny(sOriginalFilter));
3054 xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, makeAny(bOriginalApplyFlag));
3055 xReload->reload();
3057 catch(const Exception&)
3059 DBG_UNHANDLED_EXCEPTION("svx");
3066 m_pShell->UIFeatureChanged();
3067 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
3071 void FmXFormShell::CreateExternalView_Lock()
3073 if (impl_checkDisposed_Lock())
3074 return;
3076 DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
3078 // the frame the external view is displayed in
3079 bool bAlreadyExistent = m_xExternalViewController.is();
3080 Reference< css::frame::XFrame> xExternalViewFrame;
3082 Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
3083 // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
3085 // _first_ check if we have any valid fields we can use for the grid view
3086 // FS - 21.10.99 - 69219
3088 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3089 bool bHaveUsableControls = false;
3090 for (;;)
3092 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3093 if (!xCurrentModelSet.is())
3094 break;
3095 // the FmXBoundFormFieldIterator only supplies controls with a valid control source
3096 // so we just have to check the field type
3097 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3098 switch (nClassId)
3100 case FormComponentType::IMAGECONTROL:
3101 case FormComponentType::CONTROL:
3102 continue;
3104 bHaveUsableControls = true;
3105 break;
3108 if (!bHaveUsableControls)
3110 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
3111 VclMessageType::Warning, VclButtonsType::Ok,
3112 SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
3113 xBox->run();
3114 return;
3118 // load the component for external form views
3119 if (!bAlreadyExistent)
3121 OUString sFrameName("_beamer");
3122 URL aWantToDispatch;
3123 aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
3125 Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
3126 Reference< css::frame::XDispatch> xDisp;
3127 if (xProv.is())
3128 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
3129 css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
3130 if (xDisp.is())
3132 xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
3135 // with this the component should be loaded, now search the frame where it resides in
3136 xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
3137 if (xExternalViewFrame.is())
3139 m_xExternalViewController = xExternalViewFrame->getController();
3140 if (m_xExternalViewController.is())
3141 m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
3144 else
3146 xExternalViewFrame = m_xExternalViewController->getFrame();
3147 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3149 // if we display the active form we interpret the slot as "remove it"
3150 Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
3151 if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
3153 if (m_xExternalViewController == getActiveController_Lock())
3155 Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
3156 ControllerFeatures aHelper( xAsFormController );
3157 (void)aHelper->commitCurrentControl();
3160 Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
3161 CloseExternalFormViewer_Lock();
3162 setActiveController_Lock(xNewController);
3163 return;
3166 URL aClearURL;
3167 aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
3169 Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
3170 if (xClear.is())
3171 xClear->dispatch(aClearURL, Sequence< PropertyValue>());
3174 // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
3175 // instance for which this "external view" was triggered
3177 // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
3178 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3180 if (m_xExternalViewController.is())
3182 DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
3183 // collect the dispatchers we will need
3184 URL aAddColumnURL;
3185 aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
3186 Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
3187 URL aAttachURL;
3188 aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
3189 Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
3191 if (xAddColumnDispatch.is() && xAttachDispatch.is())
3193 DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
3194 // first : dispatch the descriptions for the columns to add
3195 sal_Int16 nAddedColumns = 0;
3197 // for radio buttons we need some special structures
3198 typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
3199 typedef std::map< OUString, OUString > FmMapUString2UString;
3200 typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
3202 MapUString2UstringSeq aRadioValueLists;
3203 MapUString2UstringSeq aRadioListSources;
3204 FmMapUString2UString aRadioControlSources;
3205 FmMapUString2Int16 aRadioPositions;
3207 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3208 OUString sColumnType,aGroupName,sControlSource;
3209 Sequence< Property> aProps;
3210 for (;;)
3212 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3213 if (!xCurrentModelSet.is())
3214 break;
3215 OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
3216 // create a description of the column to be created
3217 // first : determine it's type
3219 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3220 switch (nClassId)
3222 case FormComponentType::RADIOBUTTON:
3224 // get the label of the button (this is the access key for our structures)
3225 aGroupName = getLabelName(xCurrentModelSet);
3227 // add the reference value of the radio button to the list source sequence
3228 Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
3229 sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
3230 aThisGroupLabels.realloc(nNewSizeL);
3231 aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
3233 // add the label to the value list sequence
3234 Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
3235 sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
3236 aThisGroupControlSources.realloc(nNewSizeC);
3237 aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
3239 // remember the controls source of the radio group
3240 sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
3241 if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
3242 aRadioControlSources[aGroupName] = sControlSource;
3243 #ifdef DBG_UTIL
3244 else
3245 DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
3246 "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
3247 // (radio buttons with the same name should have the same control source)
3248 #endif
3249 // remember the position within the columns
3250 if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
3251 aRadioPositions[aGroupName] = nAddedColumns;
3253 // any further handling is done below
3255 continue;
3257 case FormComponentType::IMAGECONTROL:
3258 case FormComponentType::CONTROL:
3259 // no grid columns for these types (though they have a control source)
3260 continue;
3261 case FormComponentType::CHECKBOX:
3262 sColumnType = FM_COL_CHECKBOX; break;
3263 case FormComponentType::LISTBOX:
3264 sColumnType = FM_COL_LISTBOX; break;
3265 case FormComponentType::COMBOBOX:
3266 sColumnType = FM_COL_COMBOBOX; break;
3267 case FormComponentType::DATEFIELD:
3268 sColumnType = FM_COL_DATEFIELD; break;
3269 case FormComponentType::TIMEFIELD:
3270 sColumnType = FM_COL_TIMEFIELD; break;
3271 case FormComponentType::NUMERICFIELD:
3272 sColumnType = FM_COL_NUMERICFIELD; break;
3273 case FormComponentType::CURRENCYFIELD:
3274 sColumnType = FM_COL_CURRENCYFIELD; break;
3275 case FormComponentType::PATTERNFIELD:
3276 sColumnType = FM_COL_PATTERNFIELD; break;
3278 case FormComponentType::TEXTFIELD:
3280 sColumnType = FM_COL_TEXTFIELD;
3281 // we know at least two different controls which are TextFields : the basic edit field and the formatted
3282 // field. we distinguish them by their service name
3283 Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
3284 if (xInfo.is())
3286 sal_Int16 nObjectType = getControlTypeByObject(xInfo);
3287 if (OBJ_FM_FORMATTEDFIELD == nObjectType)
3288 sColumnType = FM_COL_FORMATTEDFIELD;
3291 break;
3292 default:
3293 sColumnType = FM_COL_TEXTFIELD; break;
3296 const sal_Int16 nDispatchArgs = 3;
3297 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3298 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3300 // properties describing "meta data" about the column
3301 // the type
3302 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3303 pDispatchArgs->Value <<= sColumnType;
3304 ++pDispatchArgs;
3306 // the pos : append the col
3307 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3308 pDispatchArgs->Value <<= nAddedColumns;
3309 ++pDispatchArgs;
3311 // the properties to forward to the new column
3312 Sequence< PropertyValue> aColumnProps(1);
3313 PropertyValue* pColumnProps = aColumnProps.getArray();
3315 // the label
3316 pColumnProps->Name = FM_PROP_LABEL;
3317 pColumnProps->Value <<= getLabelName(xCurrentModelSet);
3318 ++pColumnProps;
3320 // for all other props : transfer them
3321 Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
3322 DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
3323 aProps = xControlModelInfo->getProperties();
3325 // realloc the control description sequence
3326 sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
3327 aColumnProps.realloc(nExistentDescs + aProps.getLength());
3328 pColumnProps = aColumnProps.getArray() + nExistentDescs;
3330 for (const Property& rProp : std::as_const(aProps))
3332 if (rProp.Name == FM_PROP_LABEL)
3333 // already set
3334 continue;
3335 if (rProp.Name == FM_PROP_DEFAULTCONTROL)
3336 // allow the column's own "default control"
3337 continue;
3338 if (rProp.Attributes & PropertyAttribute::READONLY)
3339 // assume that properties which are readonly for the control are ro for the column to be created, too
3340 continue;
3342 pColumnProps->Name = rProp.Name;
3343 pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
3344 ++pColumnProps;
3346 aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
3348 // columns props are a dispatch argument
3349 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3350 pDispatchArgs->Value <<= aColumnProps;
3351 ++pDispatchArgs;
3352 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3353 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3355 // dispatch the "add column"
3356 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3357 ++nAddedColumns;
3360 // now for the radio button handling
3361 sal_Int16 nOffset(0);
3362 // properties describing the "direct" column properties
3363 const sal_Int16 nListBoxDescription = 6;
3364 Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
3365 for (const auto& rCtrlSource : aRadioControlSources)
3367 PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
3368 // label
3369 pListBoxDescription->Name = FM_PROP_LABEL;
3370 pListBoxDescription->Value <<= rCtrlSource.first;
3371 ++pListBoxDescription;
3373 // control source
3374 pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
3375 pListBoxDescription->Value <<= rCtrlSource.second;
3376 ++pListBoxDescription;
3378 // bound column
3379 pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
3380 pListBoxDescription->Value <<= sal_Int16(1);
3381 ++pListBoxDescription;
3383 // content type
3384 pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
3385 pListBoxDescription->Value <<= ListSourceType_VALUELIST;
3386 ++pListBoxDescription;
3388 // list source
3389 MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
3390 DBG_ASSERT(aCurrentListSource != aRadioListSources.end(),
3391 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3392 pListBoxDescription->Name = FM_PROP_LISTSOURCE;
3393 pListBoxDescription->Value <<= (*aCurrentListSource).second;
3394 ++pListBoxDescription;
3396 // value list
3397 MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
3398 DBG_ASSERT(aCurrentValueList != aRadioValueLists.end(),
3399 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3400 pListBoxDescription->Name = FM_PROP_STRINGITEMLIST;
3401 pListBoxDescription->Value <<= (*aCurrentValueList).second;
3402 ++pListBoxDescription;
3404 DBG_ASSERT(nListBoxDescription == (pListBoxDescription - aListBoxDescription.getConstArray()),
3405 "FmXFormShell::CreateExternalView : forgot to adjust nListBoxDescription ?");
3407 // properties describing the column "meta data"
3408 const sal_Int16 nDispatchArgs = 3;
3409 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3410 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3412 // column type : listbox
3413 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3414 pDispatchArgs->Value <<= OUString(FM_COL_LISTBOX);
3415 // pDispatchArgs->Value <<= (OUString)FM_COL_LISTBOX;
3416 ++pDispatchArgs;
3418 // column position
3419 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3420 FmMapUString2Int16::const_iterator aOffset = aRadioPositions.find(rCtrlSource.first);
3421 DBG_ASSERT(aOffset != aRadioPositions.end(),
3422 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3423 sal_Int16 nPosition = (*aOffset).second;
3424 nPosition = nPosition + nOffset;
3425 // we already inserted nOffset additional columns...
3426 pDispatchArgs->Value <<= nPosition;
3427 ++pDispatchArgs;
3429 // the
3430 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3431 pDispatchArgs->Value <<= aListBoxDescription;
3432 ++pDispatchArgs;
3433 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3434 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3436 // dispatch the "add column"
3437 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3438 ++nAddedColumns;
3439 ++nOffset;
3443 DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
3444 // we should have checked if we have any usable controls (see above).
3446 // "load" the "form" of the external view
3447 PropertyValue aArg;
3448 aArg.Name = FMARG_ATTACHTO_MASTERFORM;
3449 Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
3450 aArg.Value <<= xForm;
3452 m_xExternalDisplayedForm = xForm;
3453 // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
3454 // which needs the m_xExternalDisplayedForm
3456 xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
3458 m_xExtViewTriggerController = xCurrentNavController;
3460 // we want to know modifications done in the external view
3461 // if the external controller is a XFormController we can use all our default handlings for it
3462 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
3463 OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
3464 if (xFormController.is())
3465 xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
3468 #ifdef DBG_UTIL
3469 else
3471 OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
3473 #endif
3474 InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
3478 void FmXFormShell::implAdjustConfigCache_Lock()
3480 // get (cache) the wizard usage flag
3481 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3482 Sequence< Any > aFlags = GetProperties(aNames);
3483 if (1 == aFlags.getLength())
3484 m_bUseWizards = ::cppu::any2bool(aFlags[0]);
3488 void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
3490 DBG_TESTSOLARMUTEX();
3491 if (impl_checkDisposed_Lock())
3492 return;
3494 for (const OUString& rName : _rPropertyNames)
3495 if (rName == "FormControlPilotsEnabled")
3497 implAdjustConfigCache_Lock();
3498 InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
3502 void FmXFormShell::ImplCommit()
3507 void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
3509 m_bUseWizards = _bUseThem;
3511 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3512 Sequence< Any > aValues{ Any(m_bUseWizards) };
3513 PutProperties(aNames, aValues);
3517 void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
3520 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3522 _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
3525 // if we have an async load operation pending for the 0-th page for this view,
3526 // we need to cancel this
3527 if (FmFormPage* pPage = _rCurrentView.GetCurPage())
3529 // move all events from our queue to a new one, omit the events for the deactivated
3530 // page
3531 ::std::queue< FmLoadAction > aNewEvents;
3532 while ( !m_aLoadingPages.empty() )
3534 FmLoadAction aAction = m_aLoadingPages.front();
3535 m_aLoadingPages.pop();
3536 if ( pPage != aAction.pPage )
3538 aNewEvents.push( aAction );
3540 else
3542 Application::RemoveUserEvent( aAction.nEventId );
3545 m_aLoadingPages = aNewEvents;
3547 // remove callbacks at the page
3548 pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
3550 UpdateForms_Lock(true);
3554 IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
3556 if (impl_checkDisposed_Lock())
3557 return;
3559 m_nActivationEvent = nullptr;
3560 SfxObjectShell* pDocument = m_pShell->GetObjectShell();
3562 if ( pDocument && !pDocument->HasName() )
3564 if (isEnhancedForm_Lock())
3566 // show the data navigator
3567 if ( !m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
3568 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
3574 IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
3576 UpdateForms_Lock(true);
3580 void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
3582 FmFormPage* pPage = _rCurrentView.GetCurPage();
3584 // activate our view if we are activated ourself
3585 // FS - 30.06.99 - 67308
3586 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3588 // load forms for the page the current view belongs to
3589 if ( pPage )
3591 if ( !pPage->GetImpl().hasEverBeenActivated() )
3592 loadForms_Lock(pPage, LoadFormsFlags::Load
3593 | (_bSyncAction ? LoadFormsFlags::Sync
3594 : LoadFormsFlags::Async));
3595 pPage->GetImpl().setHasBeenActivated( );
3598 // first-time initializations for the views
3599 if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
3601 _rCurrentView.GetImpl()->onFirstViewActivation( dynamic_cast<FmFormModel*>( _rCurrentView.GetModel() ) );
3602 _rCurrentView.GetImpl()->setHasBeenActivated( );
3605 // activate the current view
3606 _rCurrentView.GetImpl()->Activate( _bSyncAction );
3609 // set callbacks at the page
3610 if ( pPage )
3612 pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
3615 UpdateForms_Lock(true);
3617 if ( m_bFirstActivation )
3619 m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
3620 m_bFirstActivation = false;
3623 // find a default "current form", if there is none, yet
3624 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
3625 impl_defaultCurrentForm_nothrow_Lock();
3629 void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
3631 if (impl_checkDisposed_Lock())
3632 return;
3634 if ( m_xCurrentForm.is() )
3635 // no action required
3636 return;
3638 FmFormView* pFormView = m_pShell->GetFormView();
3639 FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
3640 if ( !pPage )
3641 return;
3645 Reference< XIndexAccess > xForms = pPage->GetForms( false );
3646 if ( !xForms.is() || !xForms->hasElements() )
3647 return;
3649 Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
3650 impl_updateCurrentForm_Lock(xNewCurrentForm);
3652 catch( const Exception& )
3654 DBG_UNHANDLED_EXCEPTION("svx");
3659 void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
3661 if (!_rxModels.is())
3663 OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
3664 return;
3667 static constexpr OUStringLiteral sClassIdPropertyName = u"" FM_PROP_CLASSID;
3668 static constexpr OUStringLiteral sBoundFieldPropertyName = u"" FM_PROP_BOUNDFIELD;
3669 sal_Int32 nCount = _rxModels->getCount();
3670 Reference< XPropertySet > xCurrent;
3671 Reference< XPropertySetInfo > xCurrentInfo;
3672 Reference< XPropertySet > xBoundField;
3674 for (sal_Int32 i=0; i<nCount; ++i)
3676 _rxModels->getByIndex(i) >>= xCurrent;
3677 if (xCurrent.is())
3678 xCurrentInfo = xCurrent->getPropertySetInfo();
3679 else
3680 xCurrentInfo.clear();
3681 if (!xCurrentInfo.is())
3682 continue;
3684 if (xCurrentInfo->hasPropertyByName(sClassIdPropertyName))
3685 { // it's a control model
3687 // check if this control is bound to a living database field
3688 if (xCurrentInfo->hasPropertyByName(sBoundFieldPropertyName))
3689 xCurrent->getPropertyValue(sBoundFieldPropertyName) >>= xBoundField;
3690 else
3691 xBoundField.clear();
3693 // reset only if it's *not* bound
3694 bool bReset = !xBoundField.is();
3696 // and additionally, check if it has an external value binding
3697 Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
3698 if ( xBindable.is() && xBindable->getValueBinding().is() )
3699 bReset = false;
3701 if ( bReset )
3703 Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
3704 if ( xControlReset.is() )
3705 xControlReset->reset();
3708 else
3710 Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
3711 if (xContainer.is())
3712 smartControlReset(xContainer);
3718 IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
3720 FmLoadAction aAction = m_aLoadingPages.front();
3721 m_aLoadingPages.pop();
3723 loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
3727 namespace
3729 bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
3731 // determines whether a form should be loaded or not
3732 // if there is no datasource or connection there is no reason to load a form
3733 Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
3734 if ( !xSet.is() )
3735 return false;
3738 Reference< XConnection > xConn;
3739 if ( isEmbeddedInDatabase( _rxLoadable, xConn ) )
3740 return true;
3742 // is there already an active connection
3743 xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
3744 if ( xConn.is() )
3745 return true;
3747 OUString sPropertyValue;
3748 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
3749 if ( !sPropertyValue.isEmpty() )
3750 return true;
3752 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
3753 if ( !sPropertyValue.isEmpty() )
3754 return true;
3756 catch(const Exception&)
3758 DBG_UNHANDLED_EXCEPTION("svx");
3760 return false;
3765 void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
3767 DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
3768 "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
3770 if ( _nBehaviour & LoadFormsFlags::Async )
3772 m_aLoadingPages.push( FmLoadAction(
3773 _pPage,
3774 _nBehaviour,
3775 Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
3776 ) );
3777 return;
3780 DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
3781 if ( !_pPage )
3782 return;
3784 // lock the undo env so the forms can change non-transient properties while loading
3785 // (without this my doc's modified flag would be set)
3786 FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
3787 rFmFormModel.GetUndoEnv().Lock();
3789 // load all forms
3790 Reference< XIndexAccess > xForms = _pPage->GetForms( false );
3792 if ( xForms.is() )
3794 Reference< XLoadable > xForm;
3795 for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
3797 xForms->getByIndex( j ) >>= xForm;
3798 bool bFormWasLoaded = false;
3799 // a database form must be loaded for
3802 if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
3804 if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
3805 xForm->load();
3807 else
3809 if ( xForm->isLoaded() )
3811 bFormWasLoaded = true;
3812 xForm->unload();
3816 catch( const Exception& )
3818 DBG_UNHANDLED_EXCEPTION("svx");
3821 // reset the form if it was loaded
3822 if ( bFormWasLoaded )
3824 Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
3825 DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
3826 if ( xContainer.is() )
3827 smartControlReset( xContainer );
3832 // unlock the environment
3833 rFmFormModel.GetUndoEnv().UnLock();
3837 void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
3839 DBG_TESTSOLARMUTEX();
3840 m_pTextShell->ExecuteTextAttribute( _rReq );
3844 void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
3846 DBG_TESTSOLARMUTEX();
3847 m_pTextShell->GetTextAttributeState( _rSet );
3851 bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
3853 DBG_TESTSOLARMUTEX();
3854 return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
3858 void FmXFormShell::ForgetActiveControl_Lock()
3860 DBG_TESTSOLARMUTEX();
3861 m_pTextShell->ForgetActiveControl();
3865 void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
3867 DBG_TESTSOLARMUTEX();
3868 m_pTextShell->SetControlActivationHandler( _rHdl );
3871 void FmXFormShell::handleShowPropertiesRequest_Lock()
3873 if (onlyControlsAreMarked_Lock())
3874 ShowSelectionProperties_Lock( true );
3878 void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
3880 // catch simple double clicks
3881 if (_rViewEvent.mnMouseClicks == 2 && _rViewEvent.mnMouseCode == MOUSE_LEFT)
3883 if ( _rViewEvent.meHit == SdrHitKind::MarkedObject )
3885 if (onlyControlsAreMarked_Lock())
3886 ShowSelectionProperties_Lock( true );
3892 bool FmXFormShell::HasControlFocus_Lock() const
3894 bool bHasControlFocus = false;
3898 Reference<runtime::XFormController> xController(getActiveController_Lock());
3899 Reference< XControl > xCurrentControl;
3900 if ( xController.is() )
3901 xCurrentControl.set( xController->getCurrentControl() );
3902 if ( xCurrentControl.is() )
3904 Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
3905 bHasControlFocus = xPeerWindow->hasFocus();
3908 catch( const Exception& )
3910 DBG_UNHANDLED_EXCEPTION("svx");
3913 return bHasControlFocus;
3917 SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
3918 :IndexAccessIterator(xStartingPoint)
3923 bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
3925 // if the thing has a ControlSource and a BoundField property
3926 Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
3927 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
3929 // and the BoundField is valid
3930 Reference< XPropertySet> xField;
3931 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3932 if (xField.is())
3934 // we take it
3935 m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
3936 return true;
3940 // if it is a grid control
3941 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
3943 Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
3944 if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
3946 m_sCurrentValue.clear();
3947 return true;
3951 return false;
3955 bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
3957 return true;
3960 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */