cid#1636690 Dereference after null check
[LibreOffice.git] / svx / source / form / fmshimp.cxx
blob99e43e908ac35fc2750b929e13d5a977f84e8d34
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 <comphelper/diagnose_ex.hxx>
95 #include <comphelper/configuration.hxx>
96 #include <vcl/settings.hxx>
97 #include <vcl/svapp.hxx>
98 #include <vcl/weld.hxx>
99 #include <vcl/window.hxx>
101 #include <algorithm>
102 #include <map>
103 #include <memory>
104 #include <string_view>
105 #include <vector>
107 // is used for Invalidate -> maintain it as well
108 const sal_uInt16 DatabaseSlotMap[] =
110 SID_FM_RECORD_FIRST,
111 SID_FM_RECORD_NEXT,
112 SID_FM_RECORD_PREV,
113 SID_FM_RECORD_LAST,
114 SID_FM_RECORD_NEW,
115 SID_FM_RECORD_DELETE,
116 SID_FM_RECORD_ABSOLUTE,
117 SID_FM_RECORD_TOTAL,
118 SID_FM_RECORD_SAVE,
119 SID_FM_RECORD_UNDO,
120 SID_FM_REMOVE_FILTER_SORT,
121 SID_FM_SORTUP,
122 SID_FM_SORTDOWN,
123 SID_FM_ORDERCRIT,
124 SID_FM_AUTOFILTER,
125 SID_FM_FORM_FILTERED,
126 SID_FM_REFRESH,
127 SID_FM_REFRESH_FORM_CONTROL,
128 SID_FM_SEARCH,
129 SID_FM_FILTER_START,
130 SID_FM_VIEW_AS_GRID,
134 // is used for Invalidate -> maintain it as well
135 // sort ascending !!!!!!
136 const sal_uInt16 DlgSlotMap[] = // slots of the controller
138 SID_FM_CTL_PROPERTIES,
139 SID_FM_PROPERTIES,
140 SID_FM_TAB_DIALOG,
141 SID_FM_ADD_FIELD,
142 SID_FM_SHOW_FMEXPLORER,
143 SID_FM_FIELDS_CONTROL,
144 SID_FM_SHOW_PROPERTIES,
145 SID_FM_PROPERTY_CONTROL,
146 SID_FM_FMEXPLORER_CONTROL,
147 SID_FM_SHOW_DATANAVIGATOR,
148 SID_FM_DATANAVIGATOR_CONTROL,
152 const sal_uInt16 SelObjectSlotMap[] = // slots depending on the SelObject
154 SID_FM_CONVERTTO_EDIT,
155 SID_FM_CONVERTTO_BUTTON,
156 SID_FM_CONVERTTO_FIXEDTEXT,
157 SID_FM_CONVERTTO_LISTBOX,
158 SID_FM_CONVERTTO_CHECKBOX,
159 SID_FM_CONVERTTO_RADIOBUTTON,
160 SID_FM_CONVERTTO_GROUPBOX,
161 SID_FM_CONVERTTO_COMBOBOX,
162 SID_FM_CONVERTTO_IMAGEBUTTON,
163 SID_FM_CONVERTTO_FILECONTROL,
164 SID_FM_CONVERTTO_DATE,
165 SID_FM_CONVERTTO_TIME,
166 SID_FM_CONVERTTO_NUMERIC,
167 SID_FM_CONVERTTO_CURRENCY,
168 SID_FM_CONVERTTO_PATTERN,
169 SID_FM_CONVERTTO_IMAGECONTROL,
170 SID_FM_CONVERTTO_FORMATTED,
171 SID_FM_CONVERTTO_SCROLLBAR,
172 SID_FM_CONVERTTO_SPINBUTTON,
173 SID_FM_CONVERTTO_NAVIGATIONBAR,
175 SID_FM_FMEXPLORER_CONTROL,
176 SID_FM_DATANAVIGATOR_CONTROL,
181 // the following arrays must be consistent, i.e., corresponding entries should
182 // be at the same relative position within their respective arrays
183 static std::u16string_view aConvertSlots[] =
185 u"ConvertToEdit",
186 u"ConvertToButton",
187 u"ConvertToFixed",
188 u"ConvertToList",
189 u"ConvertToCheckBox",
190 u"ConvertToRadio",
191 u"ConvertToGroup",
192 u"ConvertToCombo",
193 u"ConvertToImageBtn",
194 u"ConvertToFileControl",
195 u"ConvertToDate",
196 u"ConvertToTime",
197 u"ConvertToNumeric",
198 u"ConvertToCurrency",
199 u"ConvertToPattern",
200 u"ConvertToImageControl",
201 u"ConvertToFormatted",
202 u"ConvertToScrollBar",
203 u"ConvertToSpinButton",
204 u"ConvertToNavigationBar"
207 constexpr OUString aImgIds[] =
209 RID_SVXBMP_EDITBOX,
210 RID_SVXBMP_BUTTON,
211 RID_SVXBMP_FIXEDTEXT,
212 RID_SVXBMP_LISTBOX,
213 RID_SVXBMP_CHECKBOX,
214 RID_SVXBMP_RADIOBUTTON,
215 RID_SVXBMP_GROUPBOX,
216 RID_SVXBMP_COMBOBOX,
217 RID_SVXBMP_IMAGEBUTTON,
218 RID_SVXBMP_FILECONTROL,
219 RID_SVXBMP_DATEFIELD,
220 RID_SVXBMP_TIMEFIELD,
221 RID_SVXBMP_NUMERICFIELD,
222 RID_SVXBMP_CURRENCYFIELD,
223 RID_SVXBMP_PATTERNFIELD,
224 RID_SVXBMP_IMAGECONTROL,
225 RID_SVXBMP_FORMATTEDFIELD,
226 RID_SVXBMP_SCROLLBAR,
227 RID_SVXBMP_SPINBUTTON,
228 RID_SVXBMP_NAVIGATIONBAR
231 const SdrObjKind nObjectTypes[] =
233 SdrObjKind::FormEdit,
234 SdrObjKind::FormButton,
235 SdrObjKind::FormFixedText,
236 SdrObjKind::FormListbox,
237 SdrObjKind::FormCheckbox,
238 SdrObjKind::FormRadioButton,
239 SdrObjKind::FormGroupBox,
240 SdrObjKind::FormCombobox,
241 SdrObjKind::FormImageButton,
242 SdrObjKind::FormFileControl,
243 SdrObjKind::FormDateField,
244 SdrObjKind::FormTimeField,
245 SdrObjKind::FormNumericField,
246 SdrObjKind::FormCurrencyField,
247 SdrObjKind::FormPatternField,
248 SdrObjKind::FormImageControl,
249 SdrObjKind::FormFormattedField,
250 SdrObjKind::FormScrollbar,
251 SdrObjKind::FormSpinButton,
252 SdrObjKind::FormNavigationBar
255 using namespace ::com::sun::star;
256 using namespace ::com::sun::star::ui;
257 using namespace ::com::sun::star::uno;
258 using namespace ::com::sun::star::sdb;
259 using namespace ::com::sun::star::sdbc;
260 using namespace ::com::sun::star::sdbcx;
261 using namespace ::com::sun::star::beans;
262 using namespace ::com::sun::star::container;
263 using namespace ::com::sun::star::form;
264 using namespace ::com::sun::star::form::binding;
265 using namespace ::com::sun::star::form::runtime;
266 using namespace ::com::sun::star::awt;
267 using namespace ::com::sun::star::view;
268 using namespace ::com::sun::star::util;
269 using namespace ::com::sun::star::script;
270 using namespace ::svxform;
271 using namespace ::svx;
272 using namespace ::dbtools;
275 //= helper
277 namespace
280 void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag& /* [out] */ _rInterfaces )
282 _rInterfaces.clear();
284 const size_t nMarkCount = _rMarkList.GetMarkCount();
285 for ( size_t i = 0; i < nMarkCount; ++i)
287 SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
288 assert(pCurrent && "marked object will exist");
290 std::optional<SdrObjListIter> oGroupIterator;
291 if ( pCurrent->IsGroupObject() )
293 oGroupIterator.emplace( pCurrent->GetSubList() );
294 pCurrent = oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
297 while ( pCurrent )
299 FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
300 // note this will de-reference virtual objects, if necessary/possible
301 if ( pAsFormObject )
303 Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
304 // the UNO_QUERY is important for normalization
305 if ( xControlModel.is() )
306 _rInterfaces.insert( xControlModel );
309 // next element
310 pCurrent = oGroupIterator && oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
316 sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
320 if (rColumns.is())
322 // loop through all columns
323 sal_Int32 i;
324 Reference< XPropertySet> xCur;
325 for (i=0; i<rColumns->getCount(); ++i)
327 rColumns->getByIndex(i) >>= xCur;
328 if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
330 // for every visible col : if nViewPos is greater zero, decrement it, else we
331 // have found the model position
332 if (!nViewPos)
333 break;
334 else
335 --nViewPos;
338 if (i<rColumns->getCount())
339 return i;
342 catch(const Exception&)
344 DBG_UNHANDLED_EXCEPTION("svx");
346 return -1;
350 void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
351 const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
353 // first check if we have a XEventAttacherManager for the model
354 Reference< XChild> xModelChild(xModel, UNO_QUERY);
355 if (!xModelChild.is())
356 return; // nothing to do
358 Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
359 if (!xEventManager.is())
360 return; // nothing to do
362 if (!rTransferIfAvailable.hasElements())
363 return; // nothing to do
365 // check for the index of the model within its parent
366 Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
367 if (!xParentIndex.is())
368 return; // nothing to do
369 sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
370 if (nIndex<0 || nIndex>=xParentIndex->getCount())
371 return; // nothing to do
373 // then we need information about the listeners supported by the control and the model
374 Sequence< Type> aModelListeners;
375 Sequence< Type> aControlListeners;
377 Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
379 if (xModel.is())
381 Any aModel(xModel);
382 aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
385 if (xControl.is())
387 Any aControl(xControl);
388 aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
391 sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
392 if (!nMaxNewLen)
393 return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
395 Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
396 ScriptEventDescriptor* pTransferable = aTransferable.getArray();
398 for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
400 // search the model/control idl classes for the event described by pCurrent
401 for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
403 for (const Type& rCurrentListener : *pCurrentArray)
405 OUString aListener = rCurrentListener.getTypeName();
406 if (!aListener.isEmpty())
407 aListener = aListener.copy(aListener.lastIndexOf('.')+1);
409 if (aListener == rCurrent.ListenerType)
410 // the current ScriptEventDescriptor doesn't match the current listeners class
411 continue;
413 // now check the methods
414 Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
416 if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
418 // we can transfer the script event : the model (control) supports it
419 *pTransferable = rCurrent;
420 ++pTransferable;
421 break;
427 sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
428 aTransferable.realloc(nRealNewLen);
430 xEventManager->registerScriptEvents(nIndex, aTransferable);
434 const OUString & getServiceNameByControlType(SdrObjKind nType)
436 switch (nType)
438 case SdrObjKind::FormEdit : return FM_COMPONENT_TEXTFIELD;
439 case SdrObjKind::FormButton : return FM_COMPONENT_COMMANDBUTTON;
440 case SdrObjKind::FormFixedText : return FM_COMPONENT_FIXEDTEXT;
441 case SdrObjKind::FormListbox : return FM_COMPONENT_LISTBOX;
442 case SdrObjKind::FormCheckbox : return FM_COMPONENT_CHECKBOX;
443 case SdrObjKind::FormRadioButton : return FM_COMPONENT_RADIOBUTTON;
444 case SdrObjKind::FormGroupBox : return FM_COMPONENT_GROUPBOX;
445 case SdrObjKind::FormCombobox : return FM_COMPONENT_COMBOBOX;
446 case SdrObjKind::FormGrid : return FM_COMPONENT_GRIDCONTROL;
447 case SdrObjKind::FormImageButton : return FM_COMPONENT_IMAGEBUTTON;
448 case SdrObjKind::FormFileControl : return FM_COMPONENT_FILECONTROL;
449 case SdrObjKind::FormDateField : return FM_COMPONENT_DATEFIELD;
450 case SdrObjKind::FormTimeField : return FM_COMPONENT_TIMEFIELD;
451 case SdrObjKind::FormNumericField : return FM_COMPONENT_NUMERICFIELD;
452 case SdrObjKind::FormCurrencyField : return FM_COMPONENT_CURRENCYFIELD;
453 case SdrObjKind::FormPatternField : return FM_COMPONENT_PATTERNFIELD;
454 case SdrObjKind::FormHidden : return FM_COMPONENT_HIDDENCONTROL;
455 case SdrObjKind::FormImageControl : return FM_COMPONENT_IMAGECONTROL;
456 case SdrObjKind::FormFormattedField : return FM_COMPONENT_FORMATTEDFIELD;
457 case SdrObjKind::FormScrollbar : return FM_SUN_COMPONENT_SCROLLBAR;
458 case SdrObjKind::FormSpinButton : return FM_SUN_COMPONENT_SPINBUTTON;
459 case SdrObjKind::FormNavigationBar : return FM_SUN_COMPONENT_NAVIGATIONBAR;
460 default:;
462 return EMPTY_OUSTRING;
468 // check if the control has one of the interfaces we can use for searching
469 // *_pCurrentText will be filled with the current text of the control (as used when searching this control)
470 bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
471 OUString* _pCurrentText )
473 if ( !_rxControl.is() )
474 return false;
476 Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
477 if ( xAsText.is() )
479 if ( _pCurrentText )
480 *_pCurrentText = xAsText->getText();
481 return true;
484 Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
485 if ( xListBox.is() )
487 if ( _pCurrentText )
488 *_pCurrentText = xListBox->getSelectedItem();
489 return true;
492 Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
493 if ( xCheckBox.is() )
495 if ( _pCurrentText )
497 switch ( static_cast<::TriState>(xCheckBox->getState()) )
499 case TRISTATE_FALSE: *_pCurrentText = "0"; break;
500 case TRISTATE_TRUE: *_pCurrentText = "1"; break;
501 default: _pCurrentText->clear(); break;
504 return true;
507 return false;
511 bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
513 if (_rContainer == m_xStartingPoint)
514 // would be quite stupid to step over the root...
515 return true;
517 return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
521 bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
523 if (!_rElement.is())
524 // NULL element
525 return false;
527 if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
528 // a forms or a grid
529 return false;
531 Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
532 if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
533 // no "BoundField" property
534 return false;
536 Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
537 if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
538 // void or invalid property value
539 return false;
541 return aVal.hasValue();
545 static bool isControlList(const SdrMarkList& rMarkList)
547 // the list contains only controls and at least one control
548 const size_t nMarkCount = rMarkList.GetMarkCount();
549 bool bControlList = nMarkCount != 0;
551 bool bHadAnyLeafs = false;
553 for (size_t i = 0; i < nMarkCount && bControlList; ++i)
555 SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
556 E3dObject* pAs3DObject = DynCastE3dObject( pObj);
557 // E3dObject's do not contain any 2D-objects (by definition)
558 // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
559 // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
560 // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
561 // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
562 // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
563 // And this would be wrong :)
564 // 03.02.00 - 72529 - FS
565 if (!pAs3DObject)
567 if (pObj->IsGroupObject())
569 SdrObjListIter aIter(pObj->GetSubList());
570 while (aIter.IsMore() && bControlList)
572 bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
573 bHadAnyLeafs = true;
576 else
578 bHadAnyLeafs = true;
579 bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
584 return bControlList && bHadAnyLeafs;
588 static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
590 Reference< XForm > xForm( _rxElement, UNO_QUERY );
591 if ( xForm.is() )
592 return xForm;
594 Reference< XChild > xChild( _rxElement, UNO_QUERY );
595 if ( xChild.is() )
596 return GetForm( xChild->getParent() );
598 return Reference< XForm >();
601 FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
602 :FmXFormShell_BD_BASE( _rMutex )
606 FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
607 :FmXFormShell_BASE(m_aMutex)
608 ,FmXFormShell_CFGBASE(u"Office.Common/Misc"_ustr, ConfigItemMode::NONE)
609 ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
610 ,m_eNavigate( NavigationBarMode_NONE )
611 ,m_nInvalidationEvent( nullptr )
612 ,m_nActivationEvent( nullptr )
613 ,m_pShell( &_rShell )
614 ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
615 ,m_aActiveControllerFeatures( this )
616 ,m_aNavControllerFeatures( this )
617 ,m_eDocumentType( eUnknownDocumentType )
618 ,m_nLockSlotInvalidation( 0 )
619 ,m_bHadPropertyBrowserInDesignMode( false )
620 ,m_bTrackProperties( true )
621 ,m_bUseWizards( true )
622 ,m_bDatabaseBar( false )
623 ,m_bInActivate( false )
624 ,m_bSetFocus( false )
625 ,m_bFilterMode( false )
626 ,m_bChangingDesignMode( false )
627 ,m_bPreparedClose( false )
628 ,m_bFirstActivation( true )
630 m_aMarkTimer.SetTimeout(100);
631 m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
633 m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
635 // to prevent deletion of this we acquire our refcounter once
636 osl_atomic_increment(&m_refCount);
638 // correct the refcounter
639 osl_atomic_decrement(&m_refCount);
641 // cache the current configuration settings we're interested in
642 implAdjustConfigCache_Lock();
643 // and register for changes on this settings
644 Sequence< OUString > aNames { u"FormControlPilotsEnabled"_ustr };
645 EnableNotification(aNames);
649 FmXFormShell::~FmXFormShell()
654 Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
656 Reference< css::frame::XModel > xModel;
658 // determine the type of document we live in
661 Reference< css::frame::XController > xController;
662 if ( m_xAttachedFrame.is() )
663 xController = m_xAttachedFrame->getController();
664 if ( xController.is() )
665 xModel = xController->getModel();
667 catch( const Exception& )
669 DBG_UNHANDLED_EXCEPTION("svx");
671 return xModel;
675 bool FmXFormShell::isEnhancedForm_Lock() const
677 return getDocumentType_Lock() == eEnhancedForm;
681 bool FmXFormShell::impl_checkDisposed_Lock() const
683 DBG_TESTSOLARMUTEX();
684 if ( !m_pShell )
686 OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
687 return true;
689 return false;
693 ::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
695 if ( m_eDocumentType != eUnknownDocumentType )
696 return m_eDocumentType;
698 // determine the type of document we live in
699 Reference<css::frame::XModel> xModel = getContextDocument_Lock();
700 if ( xModel.is() )
701 m_eDocumentType = DocumentClassification::classifyDocument( xModel );
702 else
704 OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
705 m_eDocumentType = eTextDocument;
706 // fallback, just to have a defined state
709 return m_eDocumentType;
713 bool FmXFormShell::IsReadonlyDoc_Lock() const
715 if (impl_checkDisposed_Lock())
716 return true;
718 FmFormModel* pModel = m_pShell->GetFormModel();
719 if ( pModel && pModel->GetObjectShell() )
720 return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
721 return true;
724 // EventListener
726 void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
728 SolarMutexGuard g;
730 if (m_xActiveController == e.Source)
732 // the controller will release, then release everything
733 stopListening_Lock();
734 m_xActiveForm = nullptr;
735 m_xActiveController = nullptr;
736 m_xNavigationController = nullptr;
738 m_aActiveControllerFeatures.dispose();
739 m_aNavControllerFeatures.dispose();
741 if ( m_pShell )
742 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
745 if (e.Source != m_xExternalViewController)
746 return;
748 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
749 OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
750 if (xFormController.is())
751 xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
753 if (m_xExternalViewController.is())
754 m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
756 m_xExternalViewController = nullptr;
757 m_xExternalDisplayedForm = nullptr;
758 m_xExtViewTriggerController = nullptr;
760 InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
764 void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
766 SolarMutexGuard g;
768 if (impl_checkDisposed_Lock())
769 return;
771 if (evt.PropertyName == FM_PROP_ROWCOUNT)
773 // The update following this forces a re-painting of the corresponding
774 // slots. But if I am not in the MainThread of the application (because,
775 // for example, a cursor is counting data sets at the moment and always
776 // gives me this PropertyChanges), this can clash with normal paints in
777 // the MainThread of the application. (Such paints happen, for example,
778 // if one simply places another application over the office and switches
779 // back again).
780 // Therefore the use of the SolarMutex, which safeguards that.
781 comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
782 if (rSolarSafety.tryToAcquire())
784 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
785 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(SID_FM_RECORD_TOTAL);
786 rSolarSafety.release();
788 else
790 // with the following the slot is invalidated asynchron
791 LockSlotInvalidation_Lock(true);
792 InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
793 LockSlotInvalidation_Lock(false);
797 // this may be called from a non-main-thread so invalidate the shell asynchronously
798 LockSlotInvalidation_Lock(true);
799 InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
800 LockSlotInvalidation_Lock(false);
804 void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
806 SolarMutexGuard g;
808 if (impl_checkDisposed_Lock())
809 return;
811 OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
813 if (!m_pShell->GetViewShell())
814 return;
816 // unfortunately, SFX requires sal_uInt16
817 ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );
819 // furthermore, SFX wants a terminating 0
820 aSlotIds.push_back( 0 );
822 // and, last but not least, SFX wants the ids to be sorted
823 ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
825 sal_uInt16 *pSlotIds = aSlotIds.data();
826 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( pSlotIds );
830 void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
832 SolarMutexGuard g;
834 if (impl_checkDisposed_Lock())
835 return;
837 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
838 m_pTextShell->formActivated( xController );
839 setActiveController_Lock(xController);
843 void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
845 SolarMutexGuard g;
847 if (impl_checkDisposed_Lock())
848 return;
850 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
851 m_pTextShell->formDeactivated( xController );
855 void FmXFormShell::disposing()
857 SolarMutexGuard g;
859 FmXFormShell_BASE::disposing();
861 if ( m_pShell && !m_pShell->IsDesignMode() )
862 setActiveController_Lock(nullptr, true);
863 // do NOT save the content of the old form (the second parameter tells this)
864 // if we're here, then we expect that PrepareClose has been called, and thus the user
865 // got a chance to commit or reject any changes. So in case we're here and there
866 // are still uncommitted changes, the user explicitly wanted this.
868 m_pTextShell->dispose();
870 m_xAttachedFrame = nullptr;
872 CloseExternalFormViewer_Lock();
874 while ( !m_aLoadingPages.empty() )
876 Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
877 m_aLoadingPages.pop();
881 if (m_nInvalidationEvent)
883 Application::RemoveUserEvent(m_nInvalidationEvent);
884 m_nInvalidationEvent = nullptr;
886 if ( m_nActivationEvent )
888 Application::RemoveUserEvent( m_nActivationEvent );
889 m_nActivationEvent = nullptr;
894 DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
895 // should have been deleted while being disposed
897 m_aMarkTimer.Stop();
900 DisableNotification();
902 RemoveElement_Lock(m_xForms);
903 m_xForms.clear();
905 impl_switchActiveControllerListening_Lock(false);
906 m_xActiveController = nullptr;
907 m_xActiveForm = nullptr;
909 m_pShell = nullptr;
910 m_xNavigationController = nullptr;
911 m_xCurrentForm = nullptr;
912 m_xLastGridFound = nullptr;
913 m_xAttachedFrame = nullptr;
914 m_xExternalViewController = nullptr;
915 m_xExtViewTriggerController = nullptr;
916 m_xExternalDisplayedForm = nullptr;
918 InterfaceBag().swap(m_aCurrentSelection);
920 m_aActiveControllerFeatures.dispose();
921 m_aNavControllerFeatures.dispose();
925 void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
927 if (impl_checkDisposed_Lock())
928 return;
930 if ( m_nLockSlotInvalidation )
932 OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
933 InvalidateSlot_Lock(_nId, false);
935 else
937 OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
938 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( _nId, true, true );
939 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update( _nId );
943 void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
945 if (impl_checkDisposed_Lock())
946 return;
948 if (m_nLockSlotInvalidation)
950 sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
951 m_arrInvalidSlots.emplace_back(nId, nFlags );
953 else
954 if (nId)
955 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(nId, true, bWithId);
956 else
957 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
960 void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
962 if (impl_checkDisposed_Lock())
963 return;
965 DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
967 if (bLock)
968 ++m_nLockSlotInvalidation;
969 else if (!--m_nLockSlotInvalidation)
971 // (asynchronously) invalidate everything accumulated during the locked phase
972 if (!m_nInvalidationEvent)
973 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();
994 void FmXFormShell::ForceUpdateSelection_Lock()
996 if (impl_checkDisposed_Lock())
997 return;
999 if (IsSelectionUpdatePending_Lock())
1001 m_aMarkTimer.Stop();
1003 // optionally turn off the invalidation of slots which is implicitly done by SetSelection
1004 LockSlotInvalidation_Lock(true);
1006 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
1008 LockSlotInvalidation_Lock(false);
1012 void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
1014 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1016 // the corresponding image at it
1017 rNewMenu.append(OUString(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), aImgIds[i]);
1021 OUString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
1023 static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
1025 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1027 if (nSlot == SelObjectSlotMap[i])
1028 return OUString(aConvertSlots[i]);
1031 return {};
1034 bool FmXFormShell::isControlConversionSlot(std::u16string_view rIdent)
1036 for (const auto& rConvertSlot : aConvertSlots)
1037 if (rIdent == rConvertSlot)
1038 return true;
1039 return false;
1042 void FmXFormShell::executeControlConversionSlot_Lock(std::u16string_view rIdent)
1044 OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
1045 InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
1046 if ( aSelectedElement == m_aCurrentSelection.end() )
1047 return;
1049 executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
1052 bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, std::u16string_view rIdent)
1054 if (impl_checkDisposed_Lock())
1055 return false;
1057 OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
1058 if ( !_rxObject.is() )
1059 return false;
1061 SdrPage* pPage = m_pShell->GetCurPage();
1062 FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1063 OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
1064 if ( !pFormPage )
1065 return false;
1067 OSL_ENSURE( isSolelySelected_Lock(_rxObject),
1068 "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
1070 for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
1072 if (rIdent == aConvertSlots[lookupSlot])
1074 Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
1076 FmFormObj* pFormObject = nullptr;
1077 SdrObjListIter aPageIter( pFormPage );
1078 while ( aPageIter.IsMore() )
1080 SdrObject* pCurrent = aPageIter.Next();
1081 pFormObject = FmFormObj::GetFormObject( pCurrent );
1082 if ( !pFormObject )
1083 continue;
1085 Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
1086 if ( xCurrentNormalized.get() == xNormalizedObject.get() )
1087 break;
1089 pFormObject = nullptr;
1092 if ( !pFormObject )
1093 return false;
1095 OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
1096 const Reference<XComponentContext>& xContext = comphelper::getProcessComponentContext();
1097 Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
1098 if (!xNewModel.is())
1099 return false;
1101 Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
1103 // transfer properties
1104 Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
1105 Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
1108 lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
1109 TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
1111 Sequence< css::script::ScriptEventDescriptor> aOldScripts;
1112 Reference< XChild> xChild(xOldModel, UNO_QUERY);
1113 if (xChild.is())
1115 Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
1117 // remember old script events
1118 Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
1119 if (xParent.is() && xEvManager.is())
1121 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1122 if (nIndex>=0 && nIndex<xParent->getCount())
1123 aOldScripts = xEvManager->getScriptEvents(nIndex);
1126 // replace the model within the parent container
1127 Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
1128 if (xIndexParent.is())
1130 // the form container works with FormComponents
1131 Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
1132 DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
1133 Any aNewModel(xComponent);
1137 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1138 if (nIndex>=0 && nIndex<xParent->getCount())
1139 xIndexParent->replaceByIndex(nIndex, aNewModel);
1140 else
1142 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1143 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1144 if (xNewComponent.is())
1145 xNewComponent->dispose();
1146 return false;
1149 catch(Exception&)
1151 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1152 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1153 if (xNewComponent.is())
1154 xNewComponent->dispose();
1155 return false;
1161 // special handling for the LabelControl-property : can only be set when the model is placed
1162 // within the forms hierarchy
1163 if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
1167 xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
1169 catch(Exception&)
1175 // set new model
1176 pFormObject->SetChanged();
1177 pFormObject->SetUnoControlModel(xNewModel);
1179 // transfer script events
1180 // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
1181 if (aOldScripts.hasElements())
1183 // find the control for the model
1184 Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
1186 const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
1188 Reference< XControl> xControl;
1189 auto pControl = std::find_if(aControls.begin(), aControls.end(),
1190 [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
1191 if (pControl != aControls.end())
1192 xControl = *pControl;
1193 TransferEventScripts(xNewModel, xControl, aOldScripts);
1196 // transfer value bindings, if possible
1198 Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
1199 Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
1200 if ( xOldBindable.is() )
1204 if ( xNewBindable.is() )
1205 xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
1206 xOldBindable->setValueBinding( nullptr );
1208 catch(const Exception&)
1210 DBG_UNHANDLED_EXCEPTION("svx");
1214 // same for list entry sources
1216 Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
1217 Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
1218 if ( xOldSink.is() )
1222 if ( xNewSink.is() )
1223 xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
1224 xOldSink->setListEntrySource( nullptr );
1226 catch(const Exception&)
1228 DBG_UNHANDLED_EXCEPTION("svx");
1233 // create an undo action
1234 FmFormModel* pModel = m_pShell->GetFormModel();
1235 DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
1236 if (pModel && pModel->IsUndoEnabled() )
1238 pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
1240 else
1242 FmUndoModelReplaceAction::DisposeElement( xOldModel );
1245 return true;
1248 return false;
1251 bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::u16string_view rIdent)
1253 if ( m_aCurrentSelection.empty() )
1254 return false;
1256 InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
1257 Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
1258 if ( !xElementInfo.is() )
1259 // no service info -> cannot determine this
1260 return false;
1262 if ( ++aCheck != m_aCurrentSelection.end() )
1263 // more than one element
1264 return false;
1266 if ( Reference< XForm >::query( xElementInfo ).is() )
1267 // it's a form
1268 return false;
1270 SdrObjKind nObjectType = getControlTypeByObject( xElementInfo );
1272 if ( ( SdrObjKind::FormHidden == nObjectType )
1273 || ( SdrObjKind::FormControl == nObjectType )
1274 || ( SdrObjKind::FormGrid == nObjectType )
1276 return false; // those types cannot be converted
1278 DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
1279 "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
1281 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1282 if (rIdent == aConvertSlots[i])
1283 return nObjectTypes[i] != nObjectType;
1285 return true; // all other slots: assume "yes"
1288 void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu)
1290 for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
1292 // the context is already of a type that corresponds to the entry -> disable
1293 OUString sIdent(aConvertSlots[i]);
1294 rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
1298 void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
1300 if (impl_checkDisposed_Lock())
1301 return;
1303 Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
1304 if (!xControlModels.is())
1305 return;
1307 for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
1309 Reference< XPropertySet> xModelSet;
1310 xControlModels->getByIndex(i) >>= xModelSet;
1311 if (!xModelSet.is())
1312 continue;
1314 if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
1315 continue;
1316 sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
1317 if (FormComponentType::GRIDCONTROL != nClassId)
1318 continue;
1320 if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
1321 continue;
1323 switch (nSync)
1325 case LoopGridsSync::DISABLE_SYNC:
1327 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
1329 break;
1330 case LoopGridsSync::FORCE_SYNC:
1332 Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
1333 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1334 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
1336 break;
1337 case LoopGridsSync::ENABLE_SYNC:
1339 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1341 break;
1344 if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
1346 xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
1347 Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
1348 if (xModelPropState.is())
1349 xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
1350 else
1351 xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
1357 Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
1359 if (impl_checkDisposed_Lock())
1360 return nullptr;
1362 SdrPageView* pPageView = nullptr;
1363 if ( m_pShell && m_pShell->GetFormView() )
1364 pPageView = m_pShell->GetFormView()->GetSdrPageView();
1366 Reference< XControlContainer> xControlContainer;
1367 if ( pPageView )
1368 xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
1370 return xControlContainer;
1374 void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
1376 if (impl_checkDisposed_Lock())
1377 return;
1379 OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
1380 if ( !_rxForForm.is() )
1381 return;
1385 Reference< XWindow > xParentWindow;
1386 if (m_pShell->GetViewShell())
1387 xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame().GetWindow() );
1389 Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
1390 comphelper::getProcessComponentContext(),
1391 _rxForForm, getControlContainerForView_Lock(), xParentWindow
1394 (void)xDialog->execute();
1396 catch( const Exception& )
1398 TOOLS_WARN_EXCEPTION( "svx", "FmXFormShell::ExecuteTabOrderDialog" );
1403 void FmXFormShell::ExecuteSearch_Lock()
1405 if (impl_checkDisposed_Lock())
1406 return;
1408 // a collection of all (logical) forms
1409 FmFormArray().swap(m_aSearchForms);
1410 ::std::vector< OUString > aContextNames;
1411 impl_collectFormSearchContexts_nothrow_Lock(
1412 m_pShell->GetCurPage()->GetForms(), u"",
1413 m_aSearchForms, aContextNames);
1415 if ( m_aSearchForms.size() != aContextNames.size() )
1417 SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
1418 return;
1421 // filter out the forms which do not contain valid controls at all
1423 FmFormArray aValidForms;
1424 ::std::vector< OUString > aValidContexts;
1425 FmFormArray::const_iterator form = m_aSearchForms.begin();
1426 ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
1427 for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
1429 FmSearchContext aTestContext;
1430 aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
1431 sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
1432 if ( nValidControls > 0 )
1434 aValidForms.push_back( *form );
1435 aValidContexts.push_back( *contextName );
1439 m_aSearchForms.swap( aValidForms );
1440 aContextNames.swap( aValidContexts );
1443 if (m_aSearchForms.empty() )
1445 // there are no controls that meet all the conditions for a search
1446 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
1447 VclMessageType::Warning, VclButtonsType::Ok,
1448 SvxResId(RID_STR_NODATACONTROLS)));
1449 xBox->run();
1450 return;
1453 // now I need another 'initial context'
1454 sal_Int16 nInitialContext = 0;
1455 Reference<XForm> xActiveForm(getActiveForm_Lock());
1456 for ( size_t i=0; i<m_aSearchForms.size(); ++i )
1458 if (m_aSearchForms.at(i) == xActiveForm)
1460 nInitialContext = static_cast<sal_Int16>(i);
1461 break;
1465 // If the dialog should initially offer the text of the active control,
1466 // this must have an XTextComponent interface. An addition, this makes
1467 // sense only if the current field is also bound to a table (or whatever) field.
1468 OUString strActiveField;
1469 OUString strInitialText;
1470 // ... this I get from my FormController
1471 DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
1472 Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
1473 if (xActiveControl.is())
1475 // the control can tell me its model ...
1476 Reference< XControlModel> xActiveModel( xActiveControl->getModel());
1477 DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
1479 // I ask the model for the ControlSource property ...
1480 Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
1481 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
1483 Reference< XPropertySet> xField;
1484 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1485 if (xField.is()) // (only when the thing is really bound)
1487 // and the control itself for a TextComponent interface (so that I can pick up the text there)
1488 Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
1489 if (xText.is())
1491 strActiveField = getLabelName(xProperties);
1492 strInitialText = xText->getText();
1496 else
1498 // the control itself has no ControlSource, but maybe it is a GridControl
1499 Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
1500 if (xGrid.is())
1502 // for strActiveField I need the ControlSource of the column,
1503 // for that the columns container, for that the GridPeer
1504 Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
1505 Reference< XIndexAccess> xColumns;
1506 if (xGridPeer.is())
1507 xColumns = xGridPeer->getColumns();
1509 sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
1510 sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
1511 Reference< XPropertySet> xCurrentCol;
1512 if(xColumns.is())
1513 xColumns->getByIndex(nModelCol) >>= xCurrentCol;
1514 if (xCurrentCol.is())
1515 strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
1517 // the text of the current column
1518 Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
1519 Reference< XInterface> xCurControl;
1520 xColControls->getByIndex(nViewCol) >>= xCurControl;
1521 OUString sInitialText;
1522 if (IsSearchableControl(xCurControl, &sInitialText))
1523 strInitialText = sInitialText;
1528 // taking care of possible GridControls that I know
1529 LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
1531 // Now I am ready for the dialogue.
1532 // When the potential deadlocks caused by the use of the solar mutex in
1533 // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
1534 // placed here, because the search in a separate thread is nevertheless
1535 // somewhat more fluid. Should be, however, somehow made dependent of the
1536 // underlying cursor. DAO for example is not thread-safe.
1537 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1538 ScopedVclPtr<AbstractFmSearchDialog> pDialog(
1539 pFact->CreateFmSearchDialog(
1540 m_pShell->GetViewShell()->GetViewFrame().GetFrameWeld(),
1541 strInitialText, aContextNames, nInitialContext,
1542 LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
1543 pDialog->SetActiveField( strActiveField );
1544 pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
1545 pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
1546 pDialog->Execute();
1547 pDialog.disposeAndClear();
1549 // restore GridControls again
1550 LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
1552 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
1553 // because I marked controls in OnFoundData (if I was there)
1557 bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
1559 if (impl_checkDisposed_Lock())
1560 return false;
1562 if (m_pShell->IsDesignMode())
1563 // in the design mode (without active controls) the main document is to take care of it
1564 return false;
1566 Reference<XForm> xForm(getActiveForm_Lock());
1567 if (!xForm.is())
1568 // no current form (in particular no current control) -> the main document is to take care
1569 return false;
1571 Reference< XRowSet> xDB(xForm, UNO_QUERY);
1572 DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
1574 Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
1575 if (xSupplier.is())
1577 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1578 if (xSet.is())
1582 Any aVal( xSet->getPropertyValue(u"TwoDigitDateStart"_ustr) );
1583 aVal >>= n;
1584 return true;
1586 catch(Exception&)
1592 return false;
1596 void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
1598 if (impl_checkDisposed_Lock())
1599 return;
1601 Reference<XForm> xActiveForm(getActiveForm_Lock());
1602 Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
1603 if ( xActiveRowSet.is() )
1605 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
1606 if (xSupplier.is())
1608 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1609 if (xSet.is())
1613 xSet->setPropertyValue(u"TwoDigitDateStart"_ustr, Any(sal_uInt16(n)));
1615 catch(Exception&)
1617 TOOLS_WARN_EXCEPTION("svx.form", "");
1621 return;
1625 // no active form found -> iterate through all current forms
1626 Reference< XIndexAccess> xCurrentForms( m_xForms);
1627 if (!xCurrentForms.is())
1628 { // in the alive mode, my forms are not set, but the ones on the page are
1629 if (m_pShell->GetCurPage())
1630 xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
1632 if (!xCurrentForms.is())
1633 return;
1635 ::comphelper::IndexAccessIterator aIter(xCurrentForms);
1636 Reference< XInterface> xCurrentElement( aIter.Next());
1637 while (xCurrentElement.is())
1639 // is the current element a DatabaseForm?
1640 Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
1641 if ( xElementAsRowSet.is() )
1643 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
1644 if (!xSupplier.is())
1645 continue;
1647 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1648 if (xSet.is())
1652 xSet->setPropertyValue(u"TwoDigitDateStart"_ustr, Any(sal_uInt16(n)));
1654 catch(Exception&)
1656 TOOLS_WARN_EXCEPTION("svx.form", "");
1661 xCurrentElement = aIter.Next();
1666 void FmXFormShell::CloseExternalFormViewer_Lock()
1668 if (impl_checkDisposed_Lock())
1669 return;
1671 if (!m_xExternalViewController.is())
1672 return;
1674 Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
1675 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
1676 if (!xCommLink.is())
1677 return;
1679 xExternalViewFrame->setComponent(nullptr,nullptr);
1680 ::comphelper::disposeComponent(xExternalViewFrame);
1681 m_xExternalViewController = nullptr;
1682 m_xExtViewTriggerController = nullptr;
1683 m_xExternalDisplayedForm = nullptr;
1687 Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _xForm) const
1689 if (impl_checkDisposed_Lock())
1690 return nullptr;
1692 Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1693 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1695 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1696 return m_xExternalDisplayedForm;
1698 return _xForm;
1702 Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
1704 if (impl_checkDisposed_Lock())
1705 return nullptr;
1707 Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1708 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1710 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1711 return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
1713 return _xForm;
1717 namespace
1719 bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
1721 return ( _nWhich == SID_FM_RECORD_FIRST )
1722 || ( _nWhich == SID_FM_RECORD_PREV )
1723 || ( _nWhich == SID_FM_RECORD_NEXT )
1724 || ( _nWhich == SID_FM_RECORD_LAST )
1725 || ( _nWhich == SID_FM_RECORD_NEW );
1730 bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState ) const
1732 const svx::ControllerFeatures& rController =
1733 lcl_isNavigationRelevant( _nSlot )
1734 ? getNavControllerFeatures_Lock()
1735 : getActiveControllerFeatures_Lock();
1737 if ( !_pCompleteState )
1738 return rController->isEnabled( _nSlot );
1740 rController->getState( _nSlot, *_pCompleteState );
1741 return _pCompleteState->Enabled;
1745 void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
1747 const svx::ControllerFeatures& rController =
1748 lcl_isNavigationRelevant( _nSlot )
1749 ? getNavControllerFeatures_Lock()
1750 : getActiveControllerFeatures_Lock();
1752 rController->execute( _nSlot );
1754 if ( _nSlot != SID_FM_RECORD_UNDO )
1755 return;
1757 // if we're doing an UNDO, *and* if the affected form is the form which we also display
1758 // as external view, then we need to reset the controls of the external form, too
1759 if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
1760 return;
1762 Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
1763 if ( !xContainer.is() )
1764 return;
1766 Reference< XReset > xReset;
1767 for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
1769 if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
1771 // no resets on sub forms
1772 Reference< XForm > xAsForm( xReset, UNO_QUERY );
1773 if ( !xAsForm.is() )
1774 xReset->reset();
1779 void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
1781 if ( !m_xActiveController.is() )
1782 return;
1784 if ( _bListen )
1785 m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
1786 else
1787 m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
1790 void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
1792 if (impl_checkDisposed_Lock())
1793 return;
1795 if (m_bChangingDesignMode)
1796 return;
1797 DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
1799 // if the routine has been called a second time,
1800 // the focus should no longer be transferred
1801 if (m_bInActivate)
1803 m_bSetFocus = xController != m_xActiveController;
1804 return;
1807 if (xController == m_xActiveController)
1808 return;
1810 // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
1811 Reference< XResultSet> xNavigationForm;
1812 if (m_xNavigationController.is())
1813 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1815 m_bInActivate = true;
1817 // check if the 2 controllers serve different forms
1818 Reference< XResultSet> xOldForm;
1819 if (m_xActiveController.is())
1820 xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
1821 Reference< XResultSet> xNewForm;
1822 if (xController.is())
1823 xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
1824 xOldForm = getInternalForm_Lock(xOldForm);
1825 xNewForm = getInternalForm_Lock(xNewForm);
1827 bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
1828 bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
1829 // we save the content of the old form if we move to a new form, and saving old content is allowed
1831 if ( m_xActiveController.is() && bNeedSave )
1833 // save content on change of the controller; a commit has already been executed
1834 if ( m_aActiveControllerFeatures->commitCurrentControl() )
1836 m_bSetFocus = true;
1837 if ( m_aActiveControllerFeatures->isModifiedRow() )
1839 bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
1840 bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
1841 if ( !bResult && m_bSetFocus )
1843 // if we couldn't save the current record, set the focus back to the
1844 // current control
1845 Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
1846 if ( xWindow.is() )
1847 xWindow->setFocus();
1848 m_bInActivate = false;
1849 return;
1851 else if ( bResult && bIsNew )
1853 Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
1854 if ( xCursor.is() )
1856 DO_SAFE( xCursor->last(); );
1863 stopListening_Lock();
1865 impl_switchActiveControllerListening_Lock(false);
1867 m_aActiveControllerFeatures.dispose();
1868 m_xActiveController = xController;
1869 if ( m_xActiveController.is() )
1870 m_aActiveControllerFeatures.assign( m_xActiveController );
1872 impl_switchActiveControllerListening_Lock(true);
1874 if ( m_xActiveController.is() )
1875 m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
1876 else
1877 m_xActiveForm = nullptr;
1879 startListening_Lock();
1881 // activate all dispatchers belonging to form of the new navigation controller
1882 xNavigationForm = nullptr;
1883 if (m_xNavigationController.is())
1884 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1886 m_bInActivate = false;
1888 m_pShell->UIFeatureChanged();
1889 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
1891 InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
1894 void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
1896 _rSelection = m_aCurrentSelection;
1899 bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
1901 m_aLastKnownMarkedControls.clear();
1903 if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
1904 collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
1906 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1910 bool FmXFormShell::selectLastMarkedControls_Lock()
1912 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1916 bool FmXFormShell::setCurrentSelection_Lock( InterfaceBag&& _rSelection )
1918 if (impl_checkDisposed_Lock())
1919 return false;
1921 DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
1923 if ( _rSelection.empty() && m_aCurrentSelection.empty() )
1924 // nothing to do
1925 return false;
1927 if ( _rSelection.size() == m_aCurrentSelection.size() )
1929 InterfaceBag::const_iterator aNew = _rSelection.begin();
1930 InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
1931 for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
1933 OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
1934 OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
1936 if ( aNew->get() != aOld->get() )
1937 break;
1940 if ( aNew == _rSelection.end() )
1941 // both bags equal
1942 return false;
1945 // the following is some strange code to ensure that when you have two grid controls in a document,
1946 // only one of them can have a selected column.
1947 // TODO: this should happen elsewhere, but not here - shouldn't it?
1948 if ( !m_aCurrentSelection.empty() )
1950 Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
1951 Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
1953 // is there nothing to be selected, or the parents differ, and the parent of the current object
1954 // is a selection supplier, then deselect
1955 if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
1957 Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
1958 if ( xSel.is() )
1959 xSel->select( Any() );
1963 m_aCurrentSelection = std::move(_rSelection);
1965 // determine the form which all the selected objects belong to, if any
1966 Reference< XForm > xNewCurrentForm;
1967 for (const auto& rpSelection : m_aCurrentSelection)
1969 Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
1970 OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
1972 if ( !xNewCurrentForm.is() )
1973 { // the first form we encountered
1974 xNewCurrentForm = std::move(xThisRoundsForm);
1976 else if ( xNewCurrentForm != xThisRoundsForm )
1977 { // different forms -> no "current form" at all
1978 xNewCurrentForm.clear();
1979 break;
1983 if ( !m_aCurrentSelection.empty() )
1984 impl_updateCurrentForm_Lock(xNewCurrentForm);
1986 // ensure some slots are updated
1987 for (sal_Int16 i : SelObjectSlotMap)
1988 InvalidateSlot_Lock(i, false);
1990 return true;
1994 bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
1996 return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
2000 void FmXFormShell::forgetCurrentForm_Lock()
2002 if ( !m_xCurrentForm.is() )
2003 return;
2005 // reset ...
2006 impl_updateCurrentForm_Lock(nullptr);
2008 // ... and try finding a new current form
2009 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
2010 impl_defaultCurrentForm_nothrow_Lock();
2014 void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
2016 if (impl_checkDisposed_Lock())
2017 return;
2019 m_xCurrentForm = _rxNewCurForm;
2021 // propagate to the FormPage(Impl)
2022 FmFormPage* pPage = m_pShell->GetCurPage();
2023 if ( pPage )
2024 pPage->GetImpl().setCurForm( m_xCurrentForm );
2026 // ensure the UI which depends on the current form is up-to-date
2027 for (sal_Int16 i : DlgSlotMap)
2028 InvalidateSlot_Lock(i, false);
2032 void FmXFormShell::startListening_Lock()
2034 if (impl_checkDisposed_Lock())
2035 return;
2037 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2038 if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
2040 Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
2041 if (xActiveFormSet.is())
2043 // if there is a data source, then build the listener
2044 // TODO: this is strange - shouldn't this depend on a isLoaded instead of
2045 // a "has command value"? Finally, the command value only means that it was
2046 // intended to be loaded, not that it actually *is* loaded
2047 OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
2048 if (!aSource.isEmpty())
2050 m_bDatabaseBar = true;
2052 xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2054 switch (m_eNavigate)
2056 case NavigationBarMode_PARENT:
2058 // search for the controller via which navigation is possible
2059 Reference< XChild> xChild = m_xActiveController;
2060 Reference< runtime::XFormController > xParent;
2061 while (xChild.is())
2063 xChild.set(xChild->getParent(), UNO_QUERY);
2064 xParent.set(xChild, UNO_QUERY);
2065 Reference< XPropertySet> xParentSet;
2066 if (xParent.is())
2067 xParentSet.set(xParent->getModel(), UNO_QUERY);
2068 if (xParentSet.is())
2070 xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2071 if (m_eNavigate == NavigationBarMode_CURRENT)
2072 break;
2075 m_xNavigationController = std::move(xParent);
2077 break;
2079 case NavigationBarMode_CURRENT:
2080 m_xNavigationController = m_xActiveController;
2081 break;
2083 default:
2084 m_xNavigationController = nullptr;
2085 m_bDatabaseBar = false;
2088 m_aNavControllerFeatures.dispose();
2089 if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
2090 m_aNavControllerFeatures.assign( m_xNavigationController );
2092 // because of RecordCount, listen at the controller which controls the navigation
2093 Reference< XPropertySet> xNavigationSet;
2094 if (m_xNavigationController.is())
2096 xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
2097 if (xNavigationSet.is())
2098 xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
2100 return;
2105 m_eNavigate = NavigationBarMode_NONE;
2106 m_bDatabaseBar = false;
2107 m_xNavigationController = nullptr;
2111 void FmXFormShell::stopListening_Lock()
2113 if (impl_checkDisposed_Lock())
2114 return;
2116 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2117 if ( xDatabaseForm.is() )
2119 if (m_xNavigationController.is())
2121 Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
2122 if (xSet.is())
2123 xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2128 m_bDatabaseBar = false;
2129 m_eNavigate = NavigationBarMode_NONE;
2130 m_xNavigationController = nullptr;
2133 void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
2135 if (impl_checkDisposed_Lock())
2136 return;
2138 // if the window is already visible, only update the state
2139 bool bHasChild = m_pShell->GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_SHOW_PROPERTIES );
2140 if ( bHasChild && bShow )
2141 UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
2143 // else toggle state
2144 else
2145 m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2147 InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
2148 InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
2151 IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
2153 if (impl_checkDisposed_Lock())
2154 return;
2156 DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
2157 "FmXFormShell::OnFoundData : invalid context!");
2158 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2159 DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
2161 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2162 if (!xCursor.is())
2163 return; // what should I do there?
2165 // to the record
2168 xCursor->moveToBookmark(rfriWhere.aPosition);
2170 catch(const SQLException&)
2172 OSL_FAIL("Can position on bookmark!");
2175 LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
2177 // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
2178 SAL_WARN_IF(o3tl::make_unsigned(rfriWhere.nFieldPos) >=
2179 m_arrSearchedControls.size(),
2180 "svx.form", "FmXFormShell::OnFoundData : invalid index!");
2181 SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
2183 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2184 m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
2186 FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
2187 Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
2188 DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
2189 if ( !xControlModel.is() )
2190 return;
2192 // disable the permanent cursor for the last grid we found a record
2193 if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
2195 Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
2196 xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any( false ) );
2197 Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
2198 if (xOldSetState.is())
2199 xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
2200 else
2201 xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
2204 // if the field is in a GridControl, I have to additionally go into the corresponding column there
2205 sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
2206 if (nGridColumn != -1)
2207 { // unfortunately, I have to first get the control again
2208 Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
2209 Reference< XGrid> xGrid(xControl, UNO_QUERY);
2210 DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
2211 // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
2213 // enable a permanent cursor for the grid so we can see the found text
2214 Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
2215 DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
2216 xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, Any( true ) );
2217 xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, Any( COL_LIGHTRED ) );
2218 m_xLastGridFound = std::move(xControlModel);
2220 if ( xGrid.is() )
2221 xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
2224 // As the cursor has been repositioned, I have (in positioned) invalidated
2225 // my form bar slots. But that does not take effect here unfortunately, as
2226 // generally the (modal) search dialog is of course at the top ... So, force ...
2227 sal_uInt16 nPos = 0;
2228 while (DatabaseSlotMap[nPos])
2229 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(DatabaseSlotMap[nPos++]);
2230 // unfortunately the update goes against the invalidate with only individual slots
2233 IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
2235 if (impl_checkDisposed_Lock())
2236 return;
2238 DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
2239 "FmXFormShell::OnCanceledNotFound : invalid context!");
2240 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2241 DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
2243 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2244 if (!xCursor.is())
2245 return; // what should I do there?
2247 // to the record
2250 xCursor->moveToBookmark(rfriWhere.aPosition);
2252 catch(const SQLException&)
2254 OSL_FAIL("Can position on bookmark!");
2258 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2262 IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
2264 if (impl_checkDisposed_Lock())
2265 return 0;
2267 DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
2268 Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
2269 DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
2271 Reference< XResultSet> xIter(xForm, UNO_QUERY);
2272 DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
2275 // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
2276 OUString strFieldList, sFieldDisplayNames;
2277 m_arrSearchedControls.clear();
2278 m_arrRelativeGridColumn.clear();
2280 // small problem: To mark found fields, I need SdrObjects. To determine which controls
2281 // to include in the search, I need Controls (that is, XControl interfaces). So I have
2282 // to iterate over one of them and get the other in some way. Unfortunately, there is
2283 // no direct connection between the two worlds (except from a GetUnoControl to a
2284 // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
2285 // However I can get to the Model from the Control and also from the SdrObject, and in
2286 // this way the assignment SdrObject<->Control is possible with a double loop.
2287 // The alternative to this (ugly but certainly not entirely fixable) solution would be
2288 // to renounce the caching of the SdrObjects, which would lead to significant extra
2289 // work in OnFoundData (since there I'd have to get the SdrObject first thing every
2290 // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
2291 // do that here.
2293 Reference< XNameAccess> xValidFormFields;
2294 Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
2295 DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
2296 if (xSupplyCols.is())
2297 xValidFormFields = xSupplyCols->getColumns();
2298 DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
2300 // current Page/Controller
2301 FmFormPage* pCurrentPage = m_pShell->GetCurPage();
2302 assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
2303 // Search all SdrControls of this page...
2304 OUString sControlSource, aName;
2306 SdrObjListIter aPageIter( pCurrentPage );
2307 while ( aPageIter.IsMore() )
2309 SdrObject* pCurrent = aPageIter.Next();
2310 FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
2311 // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
2313 if ( !pFormObject )
2314 continue;
2316 // the current object's model, in different tastes
2317 Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
2318 Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
2319 DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
2320 if ( !xCurrentFormComponent.is() )
2321 continue;
2323 // does the component belong to the form which we're interested in?
2324 if ( xCurrentFormComponent->getParent() != xForm )
2325 continue;
2327 // ... ask for the ControlSource property
2328 SearchableControlIterator iter( xCurrentFormComponent );
2329 Reference< XControl> xControl;
2330 // the control that has model xControlModel
2331 // (the following while can be passed through several times, without the Control
2332 // being modified, so I don't have to search every time from scratch)
2334 Reference< XInterface > xSearchable( iter.Next() );
2335 while ( xSearchable.is() )
2337 sControlSource = iter.getCurrentValue();
2338 if ( sControlSource.isEmpty() )
2340 // the current element has no ControlSource, so it is a GridControl (that
2341 // is the only thing that still permits the SearchableControlIteratore)
2342 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2343 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2345 Reference< XGridPeer> xGridPeer;
2346 if ( xControl.is() )
2347 xGridPeer.set( xControl->getPeer(), UNO_QUERY );
2350 if (!xGridPeer.is())
2351 break;
2353 Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
2354 if (!xPeerContainer.is())
2355 break;
2357 Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
2358 DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
2359 // the case 'no columns' should be indicated with an empty container, I think ...
2360 DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
2362 Reference< XInterface> xCurrentColumn;
2363 for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
2365 xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
2366 if (!xCurrentColumn.is())
2367 continue;
2369 // can we use this column control for searching ?
2370 if (!IsSearchableControl(xCurrentColumn))
2371 continue;
2373 sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
2374 Reference< XPropertySet> xCurrentColModel;
2375 xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
2376 aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
2377 // the cursor has a field matching the control source ?
2378 if (xValidFormFields->hasByName(aName))
2380 strFieldList += aName + ";";
2382 sFieldDisplayNames +=
2383 ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
2384 ";";
2386 rfmscContextInfo.arrFields.push_back(xCurrentColumn);
2388 // and the SdrOject to the Field
2389 m_arrSearchedControls.push_back(pCurrent);
2390 // the number of the column
2391 m_arrRelativeGridColumn.push_back(nViewPos);
2394 } while (false);
2396 else
2398 if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
2400 // now I need the Control to SdrObject
2401 if (!xControl.is())
2403 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2404 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2407 if (IsSearchableControl(xControl))
2409 // all tests passed -> take along in the list
2410 strFieldList += sControlSource + ";";
2412 // the label which should appear for the control :
2413 sFieldDisplayNames +=
2414 getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
2415 ";";
2417 // mark the SdrObject (accelerates the treatment in OnFoundData)
2418 m_arrSearchedControls.push_back(pCurrent);
2420 // the number of the column (here a dummy, since it is only interesting for GridControls)
2421 m_arrRelativeGridColumn.push_back(-1);
2423 // and for the formatted search...
2424 rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
2429 xSearchable = iter.Next();
2433 strFieldList = comphelper::string::stripEnd(strFieldList, ';');
2434 sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
2436 if (rfmscContextInfo.arrFields.empty())
2438 rfmscContextInfo.arrFields.clear();
2439 rfmscContextInfo.xCursor = nullptr;
2440 rfmscContextInfo.strUsedFields.clear();
2441 return 0;
2444 rfmscContextInfo.xCursor = std::move(xIter);
2445 rfmscContextInfo.strUsedFields = strFieldList;
2446 rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
2448 // 66463 - 31.05.99 - FS
2449 // when the cursor is a non-STANDARD RecordMode, set it back
2450 Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
2451 Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
2452 if (xUpdateCursor.is() && xCursorSet.is())
2454 if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
2455 xUpdateCursor->moveToCurrentRow();
2456 else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
2457 xUpdateCursor->cancelRowUpdates();
2460 return rfmscContextInfo.arrFields.size();
2463 // XContainerListener
2465 void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
2467 SolarMutexGuard g;
2469 if (impl_checkDisposed_Lock())
2470 return;
2472 // new object to listen to
2473 Reference< XInterface> xTemp;
2474 evt.Element >>= xTemp;
2475 AddElement_Lock(xTemp);
2477 m_pShell->DetermineForms(true);
2481 void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
2483 SolarMutexGuard g;
2485 if (impl_checkDisposed_Lock() )
2486 return;
2488 Reference< XInterface> xTemp;
2489 evt.ReplacedElement >>= xTemp;
2490 RemoveElement_Lock(xTemp);
2491 evt.Element >>= xTemp;
2492 AddElement_Lock(xTemp);
2496 void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
2498 SolarMutexGuard g;
2500 if (impl_checkDisposed_Lock())
2501 return;
2503 Reference< XInterface> xTemp;
2504 evt.Element >>= xTemp;
2505 RemoveElement_Lock(xTemp);
2507 m_pShell->DetermineForms(true);
2511 void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
2513 if (impl_checkDisposed_Lock())
2514 return;
2516 Reference< XIndexAccess > xForms;
2518 FmFormPage* pPage = m_pShell->GetCurPage();
2519 if ( pPage && m_pShell->m_bDesignMode )
2520 xForms = pPage->GetForms( false );
2522 if ( m_xForms != xForms )
2524 RemoveElement_Lock( m_xForms );
2525 m_xForms = std::move(xForms);
2526 AddElement_Lock(m_xForms);
2529 SolarMutexGuard g;
2530 m_pShell->DetermineForms( _bInvalidate );
2534 void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
2536 if (impl_checkDisposed_Lock())
2537 return;
2538 impl_AddElement_nothrow(_xElement);
2541 void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
2543 // listen at the container
2544 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2545 if (xContainer.is())
2547 const sal_uInt32 nCount = xContainer->getCount();
2548 Reference< XInterface> xElement;
2549 for (sal_uInt32 i = 0; i < nCount; ++i)
2551 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2552 impl_AddElement_nothrow(xElement);
2555 const Reference< XContainer> xCont(Element, UNO_QUERY);
2556 if (xCont.is())
2557 xCont->addContainerListener(this);
2560 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2561 if (xSelSupplier.is())
2562 xSelSupplier->addSelectionChangeListener(this);
2566 void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
2568 if (impl_checkDisposed_Lock())
2569 return;
2570 impl_RemoveElement_nothrow_Lock(Element);
2573 void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
2575 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2576 if (xSelSupplier.is())
2577 xSelSupplier->removeSelectionChangeListener(this);
2579 // remove connection to children
2580 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2581 if (xContainer.is())
2583 const Reference< XContainer> xCont(Element, UNO_QUERY);
2584 if (xCont.is())
2585 xCont->removeContainerListener(this);
2587 const sal_uInt32 nCount = xContainer->getCount();
2588 Reference< XInterface> xElement;
2589 for (sal_uInt32 i = 0; i < nCount; i++)
2591 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2592 impl_RemoveElement_nothrow_Lock(xElement);
2596 auto wasSelectedPos = m_aCurrentSelection.find( Element );
2597 if ( wasSelectedPos != m_aCurrentSelection.end() )
2598 m_aCurrentSelection.erase( wasSelectedPos );
2602 void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
2604 SolarMutexGuard g;
2606 if (impl_checkDisposed_Lock())
2607 return;
2609 Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
2610 Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
2611 // a selection was removed, this can only be done by the shell
2612 if ( !xSelObj.is() )
2613 return;
2615 EnableTrackProperties_Lock(false);
2617 bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
2619 InterfaceBag aNewSelection;
2620 aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
2622 if (setCurrentSelection_Lock(std::move(aNewSelection)) && IsPropBrwOpen_Lock())
2623 ShowSelectionProperties_Lock(true);
2625 EnableTrackProperties_Lock(true);
2627 if ( bMarkChanged )
2628 m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
2632 IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
2634 if (impl_checkDisposed_Lock())
2635 return;
2637 if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
2638 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
2642 void FmXFormShell::SetSelectionDelayed_Lock()
2644 if (impl_checkDisposed_Lock())
2645 return;
2647 if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
2648 m_aMarkTimer.Start();
2652 void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
2654 if (impl_checkDisposed_Lock())
2655 return;
2657 DetermineSelection_Lock(rMarkList);
2658 m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
2661 void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
2663 if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
2664 ShowSelectionProperties_Lock(true);
2667 bool FmXFormShell::IsPropBrwOpen_Lock() const
2669 if (impl_checkDisposed_Lock())
2670 return false;
2672 return m_pShell->GetViewShell() &&
2673 m_pShell->GetViewShell()->GetViewFrame().HasChildWindow(SID_FM_SHOW_PROPERTIES);
2676 class FmXFormShell::SuspendPropertyTracking
2678 private:
2679 FmXFormShell& m_rShell;
2680 bool m_bEnabled;
2682 public:
2683 explicit SuspendPropertyTracking( FmXFormShell& _rShell )
2684 :m_rShell( _rShell )
2685 ,m_bEnabled( false )
2687 if (m_rShell.IsTrackPropertiesEnabled_Lock())
2689 m_rShell.EnableTrackProperties_Lock(false);
2690 m_bEnabled = true;
2694 ~SuspendPropertyTracking( )
2696 if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
2697 m_rShell.EnableTrackProperties_Lock(true);
2701 void FmXFormShell::SetDesignMode_Lock(bool bDesign)
2703 if (impl_checkDisposed_Lock())
2704 return;
2706 DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
2707 m_bChangingDesignMode = true;
2709 // 67506 - 15.07.99 - FS
2710 // if we're switching off the design mode we have to force the property browser to be closed
2711 // so it can commit it's changes _before_ we load the forms
2712 if (!bDesign)
2714 m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame().HasChildWindow(SID_FM_SHOW_PROPERTIES);
2715 if (m_bHadPropertyBrowserInDesignMode)
2716 m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2719 FmFormView* pFormView = m_pShell->GetFormView();
2720 if (bDesign)
2722 // we are currently filtering, so stop filtering
2723 if (m_bFilterMode)
2724 stopFiltering_Lock(false);
2726 // unsubscribe from the objects of my MarkList
2727 pFormView->GetImpl()->stopMarkListWatching();
2729 else
2731 m_aMarkTimer.Stop();
2733 SuspendPropertyTracking aSuspend( *this );
2734 pFormView->GetImpl()->saveMarkList();
2737 if (bDesign && m_xExternalViewController.is())
2738 CloseExternalFormViewer_Lock();
2740 pFormView->ChangeDesignMode(bDesign);
2742 // notify listeners
2743 FmDesignModeChangedHint aChangedHint( bDesign );
2744 m_pShell->Broadcast(aChangedHint);
2746 m_pShell->m_bDesignMode = bDesign;
2747 UpdateForms_Lock(false);
2749 m_pTextShell->designModeChanged();
2751 if (bDesign)
2753 SdrMarkList aList;
2755 // during changing the mark list, don't track the selected objects in the property browser
2756 SuspendPropertyTracking aSuspend( *this );
2757 // restore the marks
2758 pFormView->GetImpl()->restoreMarkList( aList );
2761 // synchronize with the restored mark list
2762 if ( aList.GetMarkCount() )
2763 SetSelection_Lock(aList);
2765 else
2767 // subscribe to the model of the view (so that I'm informed when someone deletes
2768 // during the alive mode controls that I had saved in the saveMarklist (60343)
2769 pFormView->GetImpl()->startMarkListWatching();
2772 m_pShell->UIFeatureChanged();
2774 // 67506 - 15.07.99 - FS
2775 if (bDesign && m_bHadPropertyBrowserInDesignMode)
2777 // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
2778 // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
2779 // That's why we use an asynchron execution on the dispatcher.
2780 // (And that's why this has to be done AFTER the UIFeatureChanged.)
2781 m_pShell->GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
2783 m_bChangingDesignMode = false;
2786 Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
2788 if (impl_checkDisposed_Lock())
2789 return nullptr;
2791 Reference< XControl > xControl;
2794 Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
2796 const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
2797 // ... that I can then search
2798 for (Reference< XControl > const & control : seqControls)
2800 xControl.set( control, UNO_SET_THROW );
2801 Reference< XControlModel > xCurrentModel( xControl->getModel() );
2802 if ( xCurrentModel == i_rxModel )
2803 break;
2804 xControl.clear();
2807 if ( !xControl.is() )
2809 // fallback (some controls might not have been created, yet, since they were never visible so far)
2810 Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
2811 const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
2812 ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
2814 const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
2815 ENSURE_OR_THROW( pSdrView, "no current view" );
2817 xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow->GetOutDev() ), UNO_SET_THROW );
2820 catch( const Exception& )
2822 DBG_UNHANDLED_EXCEPTION("svx");
2825 OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
2826 return xControl;
2829 // note: _out_rForms is a member so needs lock
2830 void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
2831 std::u16string_view _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
2835 Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
2836 if ( !xContainer.is() )
2837 return;
2839 sal_Int32 nCount( xContainer->getCount() );
2840 if ( nCount == 0 )
2841 return;
2843 OUString sCurrentFormName;
2844 OUStringBuffer aNextLevelPrefix;
2845 for ( sal_Int32 i=0; i<nCount; ++i )
2847 // is the current child a form?
2848 Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
2849 if ( !xCurrentAsForm.is() )
2850 continue;
2852 Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
2853 sCurrentFormName = xNamed->getName();
2855 // the name of the current form
2856 OUString sCompleteCurrentName( sCurrentFormName );
2857 if ( !_rCurrentLevelPrefix.empty() )
2859 sCompleteCurrentName += OUString::Concat(" (") + _rCurrentLevelPrefix + ")";
2862 // the prefix for the next level
2863 aNextLevelPrefix = _rCurrentLevelPrefix;
2864 if ( !_rCurrentLevelPrefix.empty() )
2865 aNextLevelPrefix.append( '/' );
2866 aNextLevelPrefix.append( sCurrentFormName );
2868 // remember both the form and its "display name"
2869 _out_rForms.push_back( xCurrentAsForm );
2870 _out_rNames.push_back( sCompleteCurrentName );
2872 // and descend
2873 impl_collectFormSearchContexts_nothrow_Lock(
2874 xCurrentAsForm, aNextLevelPrefix,
2875 _out_rForms, _out_rNames);
2876 aNextLevelPrefix.setLength(0);
2879 catch( const Exception& )
2881 DBG_UNHANDLED_EXCEPTION("svx");
2885 void FmXFormShell::startFiltering_Lock()
2887 if (impl_checkDisposed_Lock())
2888 return;
2890 // setting all forms in filter mode
2891 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2893 // if the active controller is our external one we have to use the trigger controller
2894 Reference< XControlContainer> xContainer;
2895 if (getActiveController_Lock() == m_xExternalViewController)
2897 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
2898 xContainer = m_xExtViewTriggerController->getContainer();
2900 else
2901 xContainer = getActiveController_Lock()->getContainer();
2903 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow( xContainer );
2904 if ( pAdapter.is() )
2906 const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
2907 for (const auto& rpController : rControllerList)
2909 Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
2910 if (xModeSelector.is())
2911 xModeSelector->setMode( u"FilterMode"_ustr );
2915 m_bFilterMode = true;
2917 m_pShell->UIFeatureChanged();
2918 SfxViewFrame& rViewFrame = m_pShell->GetViewShell()->GetViewFrame();
2919 rViewFrame.GetBindings().InvalidateShell( *m_pShell );
2921 if ( rViewFrame.KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
2922 && !rViewFrame.HasChildWindow( SID_FM_FILTER_NAVIGATOR )
2925 rViewFrame.ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
2929 static void saveFilter(const Reference< runtime::XFormController >& _rxController)
2931 Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
2932 Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
2934 // call the subcontroller
2935 Reference< runtime::XFormController > xController;
2936 for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
2938 _rxController->getByIndex(i) >>= xController;
2939 saveFilter(xController);
2945 xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
2946 xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, Any( true ) );
2948 catch (const Exception& )
2950 DBG_UNHANDLED_EXCEPTION("svx");
2956 void FmXFormShell::stopFiltering_Lock(bool bSave)
2958 if (impl_checkDisposed_Lock())
2959 return;
2961 m_bFilterMode = false;
2963 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2965 // if the active controller is our external one we have to use the trigger controller
2966 Reference< XControlContainer> xContainer;
2967 if (getActiveController_Lock() == m_xExternalViewController)
2969 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
2970 xContainer = m_xExtViewTriggerController->getContainer();
2972 else
2973 xContainer = getActiveController_Lock()->getContainer();
2975 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow(xContainer);
2976 if ( pAdapter.is() )
2978 const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
2979 ::std::vector < OUString > aOriginalFilters;
2980 ::std::vector < bool > aOriginalApplyFlags;
2982 if (bSave)
2984 for (const auto& rpController : rControllerList)
2986 // remember the current filter settings in case we're going to reload the forms below (which may fail)
2989 Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
2990 aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
2991 aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
2993 catch(Exception&)
2995 OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
2996 // put dummies into the arrays so the they have the right size
2998 if (aOriginalFilters.size() == aOriginalApplyFlags.size())
2999 // the first getPropertyValue failed -> use two dummies
3000 aOriginalFilters.emplace_back( );
3001 aOriginalApplyFlags.push_back( false );
3003 saveFilter(rpController);
3006 for (const auto& rController : rControllerList)
3009 Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
3010 if (xModeSelector.is())
3011 xModeSelector->setMode( u"DataMode"_ustr );
3013 if (bSave) // execute the filter
3015 const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
3016 for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
3017 j != rControllers.end(); ++j)
3019 Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
3020 if (!xReload.is())
3021 continue;
3022 Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
3026 xReload->reload();
3028 catch(Exception&)
3030 TOOLS_WARN_EXCEPTION("svx.form", "");
3033 if (!isRowSetAlive(xFormSet))
3034 { // something went wrong -> restore the original state
3035 OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
3036 bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
3039 xFormSet->setPropertyValue(FM_PROP_FILTER, Any(sOriginalFilter));
3040 xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, Any(bOriginalApplyFlag));
3041 xReload->reload();
3043 catch(const Exception&)
3045 DBG_UNHANDLED_EXCEPTION("svx");
3052 m_pShell->UIFeatureChanged();
3053 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
3057 void FmXFormShell::CreateExternalView_Lock()
3059 if (impl_checkDisposed_Lock())
3060 return;
3062 DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
3064 // the frame the external view is displayed in
3065 bool bAlreadyExistent = m_xExternalViewController.is();
3066 Reference< css::frame::XFrame> xExternalViewFrame;
3068 Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
3069 // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
3071 // _first_ check if we have any valid fields we can use for the grid view
3072 // FS - 21.10.99 - 69219
3074 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3075 bool bHaveUsableControls = false;
3076 for (;;)
3078 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3079 if (!xCurrentModelSet.is())
3080 break;
3081 // the FmXBoundFormFieldIterator only supplies controls with a valid control source
3082 // so we just have to check the field type
3083 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3084 switch (nClassId)
3086 case FormComponentType::IMAGECONTROL:
3087 case FormComponentType::CONTROL:
3088 continue;
3090 bHaveUsableControls = true;
3091 break;
3094 if (!bHaveUsableControls)
3096 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
3097 VclMessageType::Warning, VclButtonsType::Ok,
3098 SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
3099 xBox->run();
3100 return;
3104 // load the component for external form views
3105 if (!bAlreadyExistent)
3107 OUString sFrameName(u"_beamer"_ustr);
3108 URL aWantToDispatch;
3109 aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
3111 Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
3112 Reference< css::frame::XDispatch> xDisp;
3113 if (xProv.is())
3114 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
3115 css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
3116 if (xDisp.is())
3118 xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
3121 // with this the component should be loaded, now search the frame where it resides in
3122 xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
3123 if (xExternalViewFrame.is())
3125 m_xExternalViewController = xExternalViewFrame->getController();
3126 if (m_xExternalViewController.is())
3127 m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
3130 else
3132 xExternalViewFrame = m_xExternalViewController->getFrame();
3133 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3135 // if we display the active form we interpret the slot as "remove it"
3136 Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
3137 if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
3139 if (m_xExternalViewController == getActiveController_Lock())
3141 Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
3142 ControllerFeatures aHelper( xAsFormController );
3143 (void)aHelper->commitCurrentControl();
3146 Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
3147 CloseExternalFormViewer_Lock();
3148 setActiveController_Lock(xNewController);
3149 return;
3152 URL aClearURL;
3153 aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
3155 Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
3156 if (xClear.is())
3157 xClear->dispatch(aClearURL, Sequence< PropertyValue>());
3160 // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
3161 // instance for which this "external view" was triggered
3163 // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
3164 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3166 if (m_xExternalViewController.is())
3168 DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
3169 // collect the dispatchers we will need
3170 URL aAddColumnURL;
3171 aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
3172 Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
3173 URL aAttachURL;
3174 aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
3175 Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
3177 if (xAddColumnDispatch.is() && xAttachDispatch.is())
3179 DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
3180 // first : dispatch the descriptions for the columns to add
3181 sal_Int16 nAddedColumns = 0;
3183 // for radio buttons we need some special structures
3184 typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
3185 typedef std::map< OUString, OUString > FmMapUString2UString;
3186 typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
3188 MapUString2UstringSeq aRadioValueLists;
3189 MapUString2UstringSeq aRadioListSources;
3190 FmMapUString2UString aRadioControlSources;
3191 FmMapUString2Int16 aRadioPositions;
3193 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3194 OUString sColumnType,aGroupName,sControlSource;
3195 Sequence< Property> aProps;
3196 for (;;)
3198 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3199 if (!xCurrentModelSet.is())
3200 break;
3201 OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
3202 // create a description of the column to be created
3203 // first : determine it's type
3205 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3206 switch (nClassId)
3208 case FormComponentType::RADIOBUTTON:
3210 // get the label of the button (this is the access key for our structures)
3211 aGroupName = getLabelName(xCurrentModelSet);
3213 // add the reference value of the radio button to the list source sequence
3214 Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
3215 sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
3216 aThisGroupLabels.realloc(nNewSizeL);
3217 aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
3219 // add the label to the value list sequence
3220 Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
3221 sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
3222 aThisGroupControlSources.realloc(nNewSizeC);
3223 aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
3225 // remember the controls source of the radio group
3226 sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
3227 if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
3228 aRadioControlSources[aGroupName] = sControlSource;
3229 #ifdef DBG_UTIL
3230 else
3231 DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
3232 "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
3233 // (radio buttons with the same name should have the same control source)
3234 #endif
3235 // remember the position within the columns
3236 if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
3237 aRadioPositions[aGroupName] = nAddedColumns;
3239 // any further handling is done below
3241 continue;
3243 case FormComponentType::IMAGECONTROL:
3244 case FormComponentType::CONTROL:
3245 // no grid columns for these types (though they have a control source)
3246 continue;
3247 case FormComponentType::CHECKBOX:
3248 sColumnType = FM_COL_CHECKBOX; break;
3249 case FormComponentType::LISTBOX:
3250 sColumnType = FM_COL_LISTBOX; break;
3251 case FormComponentType::COMBOBOX:
3252 sColumnType = FM_COL_COMBOBOX; break;
3253 case FormComponentType::DATEFIELD:
3254 sColumnType = FM_COL_DATEFIELD; break;
3255 case FormComponentType::TIMEFIELD:
3256 sColumnType = FM_COL_TIMEFIELD; break;
3257 case FormComponentType::NUMERICFIELD:
3258 sColumnType = FM_COL_NUMERICFIELD; break;
3259 case FormComponentType::CURRENCYFIELD:
3260 sColumnType = FM_COL_CURRENCYFIELD; break;
3261 case FormComponentType::PATTERNFIELD:
3262 sColumnType = FM_COL_PATTERNFIELD; break;
3264 case FormComponentType::TEXTFIELD:
3266 sColumnType = FM_COL_TEXTFIELD;
3267 // we know at least two different controls which are TextFields : the basic edit field and the formatted
3268 // field. we distinguish them by their service name
3269 Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
3270 if (xInfo.is())
3272 SdrObjKind nObjectType = getControlTypeByObject(xInfo);
3273 if (SdrObjKind::FormFormattedField == nObjectType)
3274 sColumnType = FM_COL_FORMATTEDFIELD;
3277 break;
3278 default:
3279 sColumnType = FM_COL_TEXTFIELD; break;
3282 const sal_Int16 nDispatchArgs = 3;
3283 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3284 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3286 // properties describing "meta data" about the column
3287 // the type
3288 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3289 pDispatchArgs->Value <<= sColumnType;
3290 ++pDispatchArgs;
3292 // the pos : append the col
3293 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3294 pDispatchArgs->Value <<= nAddedColumns;
3295 ++pDispatchArgs;
3297 // the properties to forward to the new column
3298 Sequence< PropertyValue> aColumnProps(1);
3299 PropertyValue* pColumnProps = aColumnProps.getArray();
3301 // the label
3302 pColumnProps->Name = FM_PROP_LABEL;
3303 pColumnProps->Value <<= getLabelName(xCurrentModelSet);
3304 ++pColumnProps;
3306 // for all other props : transfer them
3307 Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
3308 DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
3309 aProps = xControlModelInfo->getProperties();
3311 // realloc the control description sequence
3312 sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
3313 aColumnProps.realloc(nExistentDescs + aProps.getLength());
3314 pColumnProps = aColumnProps.getArray() + nExistentDescs;
3316 for (const Property& rProp : aProps)
3318 if (rProp.Name == FM_PROP_LABEL)
3319 // already set
3320 continue;
3321 if (rProp.Name == FM_PROP_DEFAULTCONTROL)
3322 // allow the column's own "default control"
3323 continue;
3324 if (rProp.Attributes & PropertyAttribute::READONLY)
3325 // assume that properties which are readonly for the control are ro for the column to be created, too
3326 continue;
3328 pColumnProps->Name = rProp.Name;
3329 pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
3330 ++pColumnProps;
3332 aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
3334 // columns props are a dispatch argument
3335 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3336 pDispatchArgs->Value <<= aColumnProps;
3337 ++pDispatchArgs;
3338 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3339 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3341 // dispatch the "add column"
3342 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3343 ++nAddedColumns;
3346 // now for the radio button handling
3347 sal_Int16 nOffset(0);
3348 // properties describing the "direct" column properties
3349 const sal_Int16 nListBoxDescription = 6;
3350 Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
3351 for (const auto& rCtrlSource : aRadioControlSources)
3353 PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
3354 // label
3355 pListBoxDescription->Name = FM_PROP_LABEL;
3356 pListBoxDescription->Value <<= rCtrlSource.first;
3357 ++pListBoxDescription;
3359 // control source
3360 pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
3361 pListBoxDescription->Value <<= rCtrlSource.second;
3362 ++pListBoxDescription;
3364 // bound column
3365 pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
3366 pListBoxDescription->Value <<= sal_Int16(1);
3367 ++pListBoxDescription;
3369 // content type
3370 pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
3371 pListBoxDescription->Value <<= ListSourceType_VALUELIST;
3372 ++pListBoxDescription;
3374 // list source
3375 MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
3376 assert(aCurrentListSource != aRadioListSources.end() &&
3377 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3378 pListBoxDescription->Name = FM_PROP_LISTSOURCE;
3379 pListBoxDescription->Value <<= (*aCurrentListSource).second;
3380 ++pListBoxDescription;
3382 // value list
3383 MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
3384 assert(aCurrentValueList != aRadioValueLists.end() && "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3385 pListBoxDescription->Name = FM_PROP_STRINGITEMLIST;
3386 pListBoxDescription->Value <<= (*aCurrentValueList).second;
3387 ++pListBoxDescription;
3389 DBG_ASSERT(nListBoxDescription == (pListBoxDescription - aListBoxDescription.getConstArray()),
3390 "FmXFormShell::CreateExternalView : forgot to adjust nListBoxDescription ?");
3392 // properties describing the column "meta data"
3393 const sal_Int16 nDispatchArgs = 3;
3394 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3395 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3397 // column type : listbox
3398 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3399 pDispatchArgs->Value <<= u"" FM_COL_LISTBOX ""_ustr;
3400 // pDispatchArgs->Value <<= (OUString)FM_COL_LISTBOX;
3401 ++pDispatchArgs;
3403 // column position
3404 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3405 FmMapUString2Int16::const_iterator aOffset = aRadioPositions.find(rCtrlSource.first);
3406 assert(aOffset != aRadioPositions.end() && "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3407 sal_Int16 nPosition = (*aOffset).second;
3408 nPosition = nPosition + nOffset;
3409 // we already inserted nOffset additional columns...
3410 pDispatchArgs->Value <<= nPosition;
3411 ++pDispatchArgs;
3413 // the
3414 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3415 pDispatchArgs->Value <<= aListBoxDescription;
3416 ++pDispatchArgs;
3417 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3418 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3420 // dispatch the "add column"
3421 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3422 ++nAddedColumns;
3423 ++nOffset;
3427 DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
3428 // we should have checked if we have any usable controls (see above).
3430 // "load" the "form" of the external view
3431 PropertyValue aArg;
3432 aArg.Name = FMARG_ATTACHTO_MASTERFORM;
3433 Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
3434 aArg.Value <<= xForm;
3436 m_xExternalDisplayedForm = std::move(xForm);
3437 // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
3438 // which needs the m_xExternalDisplayedForm
3440 xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
3442 m_xExtViewTriggerController = std::move(xCurrentNavController);
3444 // we want to know modifications done in the external view
3445 // if the external controller is a XFormController we can use all our default handlings for it
3446 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
3447 OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
3448 if (xFormController.is())
3449 xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
3452 #ifdef DBG_UTIL
3453 else
3455 OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
3457 #endif
3458 InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
3462 void FmXFormShell::implAdjustConfigCache_Lock()
3464 const bool bFuzzing(comphelper::IsFuzzing());
3465 if (bFuzzing)
3466 return;
3468 // get (cache) the wizard usage flag
3469 Sequence< OUString > aNames { u"FormControlPilotsEnabled"_ustr };
3470 Sequence< Any > aFlags = GetProperties(aNames);
3471 if (1 == aFlags.getLength())
3472 m_bUseWizards = ::cppu::any2bool(aFlags[0]);
3476 void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
3478 DBG_TESTSOLARMUTEX();
3479 if (impl_checkDisposed_Lock())
3480 return;
3482 for (const OUString& rName : _rPropertyNames)
3483 if (rName == "FormControlPilotsEnabled")
3485 implAdjustConfigCache_Lock();
3486 InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
3490 void FmXFormShell::ImplCommit()
3495 void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
3497 m_bUseWizards = _bUseThem;
3499 Sequence< OUString > aNames { u"FormControlPilotsEnabled"_ustr };
3500 Sequence< Any > aValues{ Any(m_bUseWizards) };
3501 PutProperties(aNames, aValues);
3505 void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
3508 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3510 _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
3513 // if we have an async load operation pending for the 0-th page for this view,
3514 // we need to cancel this
3515 if (FmFormPage* pPage = _rCurrentView.GetCurPage())
3517 // move all events from our queue to a new one, omit the events for the deactivated
3518 // page
3519 ::std::queue< FmLoadAction > aNewEvents;
3520 while ( !m_aLoadingPages.empty() )
3522 FmLoadAction aAction = m_aLoadingPages.front();
3523 m_aLoadingPages.pop();
3524 if ( pPage != aAction.pPage )
3526 aNewEvents.push( aAction );
3528 else
3530 Application::RemoveUserEvent( aAction.nEventId );
3533 m_aLoadingPages = std::move(aNewEvents);
3535 // remove callbacks at the page
3536 pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
3538 UpdateForms_Lock(true);
3542 IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
3544 if (impl_checkDisposed_Lock())
3545 return;
3547 m_nActivationEvent = nullptr;
3548 SfxObjectShell* pDocument = m_pShell->GetObjectShell();
3550 if ( pDocument && !pDocument->HasName() )
3552 if (isEnhancedForm_Lock())
3554 // show the data navigator
3555 if ( !m_pShell->GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
3556 m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
3562 IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
3564 UpdateForms_Lock(true);
3568 void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
3570 FmFormPage* pPage = _rCurrentView.GetCurPage();
3572 // activate our view if we are activated ourself
3573 // FS - 30.06.99 - 67308
3574 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3576 // load forms for the page the current view belongs to
3577 if ( pPage )
3579 if ( !pPage->GetImpl().hasEverBeenActivated() )
3580 loadForms_Lock(pPage, LoadFormsFlags::Load
3581 | (_bSyncAction ? LoadFormsFlags::Sync
3582 : LoadFormsFlags::Async));
3583 pPage->GetImpl().setHasBeenActivated( );
3586 // first-time initializations for the views
3587 if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
3589 auto* pFormModel = dynamic_cast<FmFormModel*>(&_rCurrentView.GetModel());
3590 _rCurrentView.GetImpl()->onFirstViewActivation(pFormModel);
3591 _rCurrentView.GetImpl()->setHasBeenActivated();
3594 // activate the current view
3595 _rCurrentView.GetImpl()->Activate( _bSyncAction );
3598 // set callbacks at the page
3599 if ( pPage )
3601 pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
3604 UpdateForms_Lock(true);
3606 if ( m_bFirstActivation )
3608 m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
3609 m_bFirstActivation = false;
3612 // find a default "current form", if there is none, yet
3613 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
3614 impl_defaultCurrentForm_nothrow_Lock();
3618 void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
3620 if (impl_checkDisposed_Lock())
3621 return;
3623 if ( m_xCurrentForm.is() )
3624 // no action required
3625 return;
3627 FmFormView* pFormView = m_pShell->GetFormView();
3628 FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
3629 if ( !pPage )
3630 return;
3634 Reference< XIndexAccess > xForms = pPage->GetForms( false );
3635 if ( !xForms.is() || !xForms->hasElements() )
3636 return;
3638 Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
3639 impl_updateCurrentForm_Lock(xNewCurrentForm);
3641 catch( const Exception& )
3643 DBG_UNHANDLED_EXCEPTION("svx");
3648 void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
3650 if (!_rxModels.is())
3652 OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
3653 return;
3656 sal_Int32 nCount = _rxModels->getCount();
3657 Reference< XPropertySet > xCurrent;
3658 Reference< XPropertySetInfo > xCurrentInfo;
3659 Reference< XPropertySet > xBoundField;
3661 for (sal_Int32 i=0; i<nCount; ++i)
3663 _rxModels->getByIndex(i) >>= xCurrent;
3664 if (xCurrent.is())
3665 xCurrentInfo = xCurrent->getPropertySetInfo();
3666 else
3667 xCurrentInfo.clear();
3668 if (!xCurrentInfo.is())
3669 continue;
3671 if (xCurrentInfo->hasPropertyByName(FM_PROP_CLASSID))
3672 { // it's a control model
3674 // check if this control is bound to a living database field
3675 if (xCurrentInfo->hasPropertyByName(FM_PROP_BOUNDFIELD))
3676 xCurrent->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xBoundField;
3677 else
3678 xBoundField.clear();
3680 // reset only if it's *not* bound
3681 bool bReset = !xBoundField.is();
3683 // and additionally, check if it has an external value binding
3684 Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
3685 if ( xBindable.is() && xBindable->getValueBinding().is() )
3686 bReset = false;
3688 if ( bReset )
3690 Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
3691 if ( xControlReset.is() )
3692 xControlReset->reset();
3695 else
3697 Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
3698 if (xContainer.is())
3699 smartControlReset(xContainer);
3705 IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
3707 FmLoadAction aAction = m_aLoadingPages.front();
3708 m_aLoadingPages.pop();
3710 loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
3714 namespace
3716 bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
3718 // determines whether a form should be loaded or not
3719 // if there is no datasource or connection there is no reason to load a form
3720 Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
3721 if ( !xSet.is() )
3722 return false;
3725 Reference< XConnection > xConn;
3726 if ( isEmbeddedInDatabase( _rxLoadable, xConn ) )
3727 return true;
3729 // is there already an active connection
3730 xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
3731 if ( xConn.is() )
3732 return true;
3734 OUString sPropertyValue;
3735 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
3736 if ( !sPropertyValue.isEmpty() )
3737 return true;
3739 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
3740 if ( !sPropertyValue.isEmpty() )
3741 return true;
3743 catch(const Exception&)
3745 DBG_UNHANDLED_EXCEPTION("svx");
3747 return false;
3752 void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
3754 DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
3755 "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
3757 if ( _nBehaviour & LoadFormsFlags::Async )
3759 m_aLoadingPages.push( FmLoadAction(
3760 _pPage,
3761 _nBehaviour,
3762 Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
3763 ) );
3764 return;
3767 DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
3768 if ( !_pPage )
3769 return;
3771 // lock the undo env so the forms can change non-transient properties while loading
3772 // (without this my doc's modified flag would be set)
3773 FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
3774 rFmFormModel.GetUndoEnv().Lock();
3776 // load all forms
3777 Reference< XIndexAccess > xForms = _pPage->GetForms( false );
3779 if ( xForms.is() )
3781 Reference< XLoadable > xForm;
3782 for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
3784 xForms->getByIndex( j ) >>= xForm;
3785 bool bFormWasLoaded = false;
3786 // a database form must be loaded for
3789 if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
3791 if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
3792 xForm->load();
3794 else
3796 if ( xForm->isLoaded() )
3798 bFormWasLoaded = true;
3799 xForm->unload();
3803 catch( const Exception& )
3805 DBG_UNHANDLED_EXCEPTION("svx");
3808 // reset the form if it was loaded
3809 if ( bFormWasLoaded )
3811 Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
3812 DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
3813 if ( xContainer.is() )
3814 smartControlReset( xContainer );
3819 // unlock the environment
3820 rFmFormModel.GetUndoEnv().UnLock();
3824 void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
3826 DBG_TESTSOLARMUTEX();
3827 m_pTextShell->ExecuteTextAttribute( _rReq );
3831 void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
3833 DBG_TESTSOLARMUTEX();
3834 m_pTextShell->GetTextAttributeState( _rSet );
3838 bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
3840 DBG_TESTSOLARMUTEX();
3841 return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
3845 void FmXFormShell::ForgetActiveControl_Lock()
3847 DBG_TESTSOLARMUTEX();
3848 m_pTextShell->ForgetActiveControl();
3852 void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
3854 DBG_TESTSOLARMUTEX();
3855 m_pTextShell->SetControlActivationHandler( _rHdl );
3858 void FmXFormShell::handleShowPropertiesRequest_Lock()
3860 if (onlyControlsAreMarked_Lock())
3861 ShowSelectionProperties_Lock( true );
3865 void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
3867 // catch simple double clicks
3868 if (_rViewEvent.mnMouseClicks == 2 && _rViewEvent.mnMouseCode == MOUSE_LEFT)
3870 if ( _rViewEvent.meHit == SdrHitKind::MarkedObject )
3872 if (onlyControlsAreMarked_Lock())
3873 ShowSelectionProperties_Lock( true );
3879 bool FmXFormShell::HasControlFocus_Lock() const
3881 bool bHasControlFocus = false;
3885 Reference<runtime::XFormController> xController(getActiveController_Lock());
3886 Reference< XControl > xCurrentControl;
3887 if ( xController.is() )
3888 xCurrentControl.set( xController->getCurrentControl() );
3889 if ( xCurrentControl.is() )
3891 Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
3892 bHasControlFocus = xPeerWindow->hasFocus();
3895 catch( const Exception& )
3897 DBG_UNHANDLED_EXCEPTION("svx");
3900 return bHasControlFocus;
3904 SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
3905 :IndexAccessIterator(xStartingPoint)
3910 bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
3912 // if the thing has a ControlSource and a BoundField property
3913 Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
3914 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
3916 // and the BoundField is valid
3917 Reference< XPropertySet> xField;
3918 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3919 if (xField.is())
3921 // we take it
3922 m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
3923 return true;
3927 // if it is a grid control
3928 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
3930 Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
3931 if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
3933 m_sCurrentValue.clear();
3934 return true;
3938 return false;
3942 bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
3944 return true;
3947 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */