Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / form / fmshimp.cxx
blob8e7278f7e3fed6460498c804b2c9eeceb11adb73
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 <unotools/configmgr.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_Int16 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_Int16 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 rtl::OUStringConstExpr 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();
289 std::optional<SdrObjListIter> oGroupIterator;
290 if ( pCurrent->IsGroupObject() )
292 oGroupIterator.emplace( pCurrent->GetSubList() );
293 pCurrent = oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
296 while ( pCurrent )
298 FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
299 // note this will de-reference virtual objects, if necessary/possible
300 if ( pAsFormObject )
302 Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
303 // the UNO_QUERY is important for normalization
304 if ( xControlModel.is() )
305 _rInterfaces.insert( xControlModel );
308 // next element
309 pCurrent = oGroupIterator && oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr;
315 sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
319 if (rColumns.is())
321 // loop through all columns
322 sal_Int32 i;
323 Reference< XPropertySet> xCur;
324 for (i=0; i<rColumns->getCount(); ++i)
326 rColumns->getByIndex(i) >>= xCur;
327 if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
329 // for every visible col : if nViewPos is greater zero, decrement it, else we
330 // have found the model position
331 if (!nViewPos)
332 break;
333 else
334 --nViewPos;
337 if (i<rColumns->getCount())
338 return i;
341 catch(const Exception&)
343 DBG_UNHANDLED_EXCEPTION("svx");
345 return -1;
349 void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
350 const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
352 // first check if we have a XEventAttacherManager for the model
353 Reference< XChild> xModelChild(xModel, UNO_QUERY);
354 if (!xModelChild.is())
355 return; // nothing to do
357 Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
358 if (!xEventManager.is())
359 return; // nothing to do
361 if (!rTransferIfAvailable.hasElements())
362 return; // nothing to do
364 // check for the index of the model within its parent
365 Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
366 if (!xParentIndex.is())
367 return; // nothing to do
368 sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
369 if (nIndex<0 || nIndex>=xParentIndex->getCount())
370 return; // nothing to do
372 // then we need information about the listeners supported by the control and the model
373 Sequence< Type> aModelListeners;
374 Sequence< Type> aControlListeners;
376 Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
378 if (xModel.is())
380 Any aModel(xModel);
381 aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
384 if (xControl.is())
386 Any aControl(xControl);
387 aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
390 sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
391 if (!nMaxNewLen)
392 return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
394 Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
395 ScriptEventDescriptor* pTransferable = aTransferable.getArray();
397 for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
399 // search the model/control idl classes for the event described by pCurrent
400 for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
402 for (const Type& rCurrentListener : *pCurrentArray)
404 OUString aListener = rCurrentListener.getTypeName();
405 if (!aListener.isEmpty())
406 aListener = aListener.copy(aListener.lastIndexOf('.')+1);
408 if (aListener == rCurrent.ListenerType)
409 // the current ScriptEventDescriptor doesn't match the current listeners class
410 continue;
412 // now check the methods
413 Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
415 if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
417 // we can transfer the script event : the model (control) supports it
418 *pTransferable = rCurrent;
419 ++pTransferable;
420 break;
426 sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
427 aTransferable.realloc(nRealNewLen);
429 xEventManager->registerScriptEvents(nIndex, aTransferable);
433 OUString getServiceNameByControlType(SdrObjKind nType)
435 switch (nType)
437 case SdrObjKind::FormEdit : return FM_COMPONENT_TEXTFIELD;
438 case SdrObjKind::FormButton : return FM_COMPONENT_COMMANDBUTTON;
439 case SdrObjKind::FormFixedText : return FM_COMPONENT_FIXEDTEXT;
440 case SdrObjKind::FormListbox : return FM_COMPONENT_LISTBOX;
441 case SdrObjKind::FormCheckbox : return FM_COMPONENT_CHECKBOX;
442 case SdrObjKind::FormRadioButton : return FM_COMPONENT_RADIOBUTTON;
443 case SdrObjKind::FormGroupBox : return FM_COMPONENT_GROUPBOX;
444 case SdrObjKind::FormCombobox : return FM_COMPONENT_COMBOBOX;
445 case SdrObjKind::FormGrid : return FM_COMPONENT_GRIDCONTROL;
446 case SdrObjKind::FormImageButton : return FM_COMPONENT_IMAGEBUTTON;
447 case SdrObjKind::FormFileControl : return FM_COMPONENT_FILECONTROL;
448 case SdrObjKind::FormDateField : return FM_COMPONENT_DATEFIELD;
449 case SdrObjKind::FormTimeField : return FM_COMPONENT_TIMEFIELD;
450 case SdrObjKind::FormNumericField : return FM_COMPONENT_NUMERICFIELD;
451 case SdrObjKind::FormCurrencyField : return FM_COMPONENT_CURRENCYFIELD;
452 case SdrObjKind::FormPatternField : return FM_COMPONENT_PATTERNFIELD;
453 case SdrObjKind::FormHidden : return FM_COMPONENT_HIDDENCONTROL;
454 case SdrObjKind::FormImageControl : return FM_COMPONENT_IMAGECONTROL;
455 case SdrObjKind::FormFormattedField : return FM_COMPONENT_FORMATTEDFIELD;
456 case SdrObjKind::FormScrollbar : return FM_SUN_COMPONENT_SCROLLBAR;
457 case SdrObjKind::FormSpinButton : return FM_SUN_COMPONENT_SPINBUTTON;
458 case SdrObjKind::FormNavigationBar : return FM_SUN_COMPONENT_NAVIGATIONBAR;
459 default:;
461 return OUString();
467 // check if the control has one of the interfaces we can use for searching
468 // *_pCurrentText will be filled with the current text of the control (as used when searching this control)
469 bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
470 OUString* _pCurrentText )
472 if ( !_rxControl.is() )
473 return false;
475 Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
476 if ( xAsText.is() )
478 if ( _pCurrentText )
479 *_pCurrentText = xAsText->getText();
480 return true;
483 Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
484 if ( xListBox.is() )
486 if ( _pCurrentText )
487 *_pCurrentText = xListBox->getSelectedItem();
488 return true;
491 Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
492 if ( xCheckBox.is() )
494 if ( _pCurrentText )
496 switch ( static_cast<::TriState>(xCheckBox->getState()) )
498 case TRISTATE_FALSE: *_pCurrentText = "0"; break;
499 case TRISTATE_TRUE: *_pCurrentText = "1"; break;
500 default: _pCurrentText->clear(); break;
503 return true;
506 return false;
510 bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
512 if (_rContainer == m_xStartingPoint)
513 // would be quite stupid to step over the root...
514 return true;
516 return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
520 bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
522 if (!_rElement.is())
523 // NULL element
524 return false;
526 if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
527 // a forms or a grid
528 return false;
530 Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
531 if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
532 // no "BoundField" property
533 return false;
535 Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
536 if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
537 // void or invalid property value
538 return false;
540 return aVal.hasValue();
544 static bool isControlList(const SdrMarkList& rMarkList)
546 // the list contains only controls and at least one control
547 const size_t nMarkCount = rMarkList.GetMarkCount();
548 bool bControlList = nMarkCount != 0;
550 bool bHadAnyLeafs = false;
552 for (size_t i = 0; i < nMarkCount && bControlList; ++i)
554 SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
555 E3dObject* pAs3DObject = DynCastE3dObject( pObj);
556 // E3dObject's do not contain any 2D-objects (by definition)
557 // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
558 // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
559 // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
560 // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
561 // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
562 // And this would be wrong :)
563 // 03.02.00 - 72529 - FS
564 if (!pAs3DObject)
566 if (pObj->IsGroupObject())
568 SdrObjListIter aIter(pObj->GetSubList());
569 while (aIter.IsMore() && bControlList)
571 bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
572 bHadAnyLeafs = true;
575 else
577 bHadAnyLeafs = true;
578 bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
583 return bControlList && bHadAnyLeafs;
587 static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
589 Reference< XForm > xForm( _rxElement, UNO_QUERY );
590 if ( xForm.is() )
591 return xForm;
593 Reference< XChild > xChild( _rxElement, UNO_QUERY );
594 if ( xChild.is() )
595 return GetForm( xChild->getParent() );
597 return Reference< XForm >();
600 FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
601 :FmXFormShell_BD_BASE( _rMutex )
605 FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
606 :FmXFormShell_BASE(m_aMutex)
607 ,FmXFormShell_CFGBASE("Office.Common/Misc", ConfigItemMode::NONE)
608 ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
609 ,m_eNavigate( NavigationBarMode_NONE )
610 ,m_nInvalidationEvent( nullptr )
611 ,m_nActivationEvent( nullptr )
612 ,m_pShell( &_rShell )
613 ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
614 ,m_aActiveControllerFeatures( this )
615 ,m_aNavControllerFeatures( this )
616 ,m_eDocumentType( eUnknownDocumentType )
617 ,m_nLockSlotInvalidation( 0 )
618 ,m_bHadPropertyBrowserInDesignMode( false )
619 ,m_bTrackProperties( true )
620 ,m_bUseWizards( true )
621 ,m_bDatabaseBar( false )
622 ,m_bInActivate( false )
623 ,m_bSetFocus( false )
624 ,m_bFilterMode( false )
625 ,m_bChangingDesignMode( false )
626 ,m_bPreparedClose( false )
627 ,m_bFirstActivation( true )
629 m_aMarkTimer.SetTimeout(100);
630 m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
632 m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
634 // to prevent deletion of this we acquire our refcounter once
635 osl_atomic_increment(&m_refCount);
637 // correct the refcounter
638 osl_atomic_decrement(&m_refCount);
640 // cache the current configuration settings we're interested in
641 implAdjustConfigCache_Lock();
642 // and register for changes on this settings
643 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
644 EnableNotification(aNames);
648 FmXFormShell::~FmXFormShell()
653 Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
655 Reference< css::frame::XModel > xModel;
657 // determine the type of document we live in
660 Reference< css::frame::XController > xController;
661 if ( m_xAttachedFrame.is() )
662 xController = m_xAttachedFrame->getController();
663 if ( xController.is() )
664 xModel = xController->getModel();
666 catch( const Exception& )
668 DBG_UNHANDLED_EXCEPTION("svx");
670 return xModel;
674 bool FmXFormShell::isEnhancedForm_Lock() const
676 return getDocumentType_Lock() == eEnhancedForm;
680 bool FmXFormShell::impl_checkDisposed_Lock() const
682 DBG_TESTSOLARMUTEX();
683 if ( !m_pShell )
685 OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
686 return true;
688 return false;
692 ::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
694 if ( m_eDocumentType != eUnknownDocumentType )
695 return m_eDocumentType;
697 // determine the type of document we live in
698 Reference<css::frame::XModel> xModel = getContextDocument_Lock();
699 if ( xModel.is() )
700 m_eDocumentType = DocumentClassification::classifyDocument( xModel );
701 else
703 OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
704 m_eDocumentType = eTextDocument;
705 // fallback, just to have a defined state
708 return m_eDocumentType;
712 bool FmXFormShell::IsReadonlyDoc_Lock() const
714 if (impl_checkDisposed_Lock())
715 return true;
717 FmFormModel* pModel = m_pShell->GetFormModel();
718 if ( pModel && pModel->GetObjectShell() )
719 return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
720 return true;
723 // EventListener
725 void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
727 SolarMutexGuard g;
729 if (m_xActiveController == e.Source)
731 // the controller will release, then release everything
732 stopListening_Lock();
733 m_xActiveForm = nullptr;
734 m_xActiveController = nullptr;
735 m_xNavigationController = nullptr;
737 m_aActiveControllerFeatures.dispose();
738 m_aNavControllerFeatures.dispose();
740 if ( m_pShell )
741 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
744 if (e.Source != m_xExternalViewController)
745 return;
747 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
748 OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
749 if (xFormController.is())
750 xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
752 if (m_xExternalViewController.is())
753 m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
755 m_xExternalViewController = nullptr;
756 m_xExternalDisplayedForm = nullptr;
757 m_xExtViewTriggerController = nullptr;
759 InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
763 void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
765 SolarMutexGuard g;
767 if (impl_checkDisposed_Lock())
768 return;
770 if (evt.PropertyName == FM_PROP_ROWCOUNT)
772 // The update following this forces a re-painting of the corresponding
773 // slots. But if I am not in the MainThread of the application (because,
774 // for example, a cursor is counting data sets at the moment and always
775 // gives me this PropertyChanges), this can clash with normal paints in
776 // the MainThread of the application. (Such paints happen, for example,
777 // if one simply places another application over the office and switches
778 // back again).
779 // Therefore the use of the SolarMutex, which safeguards that.
780 comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
781 if (rSolarSafety.tryToAcquire())
783 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
784 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(SID_FM_RECORD_TOTAL);
785 rSolarSafety.release();
787 else
789 // with the following the slot is invalidated asynchron
790 LockSlotInvalidation_Lock(true);
791 InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
792 LockSlotInvalidation_Lock(false);
796 // this may be called from a non-main-thread so invalidate the shell asynchronously
797 LockSlotInvalidation_Lock(true);
798 InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
799 LockSlotInvalidation_Lock(false);
803 void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
805 SolarMutexGuard g;
807 if (impl_checkDisposed_Lock())
808 return;
810 OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
812 if (!m_pShell->GetViewShell())
813 return;
815 // unfortunately, SFX requires sal_uInt16
816 ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );
818 // furthermore, SFX wants a terminating 0
819 aSlotIds.push_back( 0 );
821 // and, last but not least, SFX wants the ids to be sorted
822 ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
824 sal_uInt16 *pSlotIds = aSlotIds.data();
825 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( pSlotIds );
829 void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
831 SolarMutexGuard g;
833 if (impl_checkDisposed_Lock())
834 return;
836 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
837 m_pTextShell->formActivated( xController );
838 setActiveController_Lock(xController);
842 void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
844 SolarMutexGuard g;
846 if (impl_checkDisposed_Lock())
847 return;
849 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
850 m_pTextShell->formDeactivated( xController );
854 void FmXFormShell::disposing()
856 SolarMutexGuard g;
858 FmXFormShell_BASE::disposing();
860 if ( m_pShell && !m_pShell->IsDesignMode() )
861 setActiveController_Lock(nullptr, true);
862 // do NOT save the content of the old form (the second parameter tells this)
863 // if we're here, then we expect that PrepareClose has been called, and thus the user
864 // got a chance to commit or reject any changes. So in case we're here and there
865 // are still uncommitted changes, the user explicitly wanted this.
867 m_pTextShell->dispose();
869 m_xAttachedFrame = nullptr;
871 CloseExternalFormViewer_Lock();
873 while ( !m_aLoadingPages.empty() )
875 Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
876 m_aLoadingPages.pop();
880 if (m_nInvalidationEvent)
882 Application::RemoveUserEvent(m_nInvalidationEvent);
883 m_nInvalidationEvent = nullptr;
885 if ( m_nActivationEvent )
887 Application::RemoveUserEvent( m_nActivationEvent );
888 m_nActivationEvent = nullptr;
893 DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
894 // should have been deleted while being disposed
896 m_aMarkTimer.Stop();
899 DisableNotification();
901 RemoveElement_Lock(m_xForms);
902 m_xForms.clear();
904 impl_switchActiveControllerListening_Lock(false);
905 m_xActiveController = nullptr;
906 m_xActiveForm = nullptr;
908 m_pShell = nullptr;
909 m_xNavigationController = nullptr;
910 m_xCurrentForm = nullptr;
911 m_xLastGridFound = nullptr;
912 m_xAttachedFrame = nullptr;
913 m_xExternalViewController = nullptr;
914 m_xExtViewTriggerController = nullptr;
915 m_xExternalDisplayedForm = nullptr;
917 InterfaceBag().swap(m_aCurrentSelection);
919 m_aActiveControllerFeatures.dispose();
920 m_aNavControllerFeatures.dispose();
924 void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
926 if (impl_checkDisposed_Lock())
927 return;
929 if ( m_nLockSlotInvalidation )
931 OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
932 InvalidateSlot_Lock(_nId, false);
934 else
936 OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
937 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate( _nId, true, true );
938 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update( _nId );
942 void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
944 if (impl_checkDisposed_Lock())
945 return;
947 if (m_nLockSlotInvalidation)
949 sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
950 m_arrInvalidSlots.emplace_back(nId, nFlags );
952 else
953 if (nId)
954 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(nId, true, bWithId);
955 else
956 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
959 void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
961 if (impl_checkDisposed_Lock())
962 return;
964 DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
966 if (bLock)
967 ++m_nLockSlotInvalidation;
968 else if (!--m_nLockSlotInvalidation)
970 // (asynchronously) invalidate everything accumulated during the locked phase
971 if (!m_nInvalidationEvent)
972 m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
976 IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
978 if (impl_checkDisposed_Lock())
979 return;
981 m_nInvalidationEvent = nullptr;
983 for (const auto& rInvalidSlot : m_arrInvalidSlots)
985 if (rInvalidSlot.id)
986 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
987 else
988 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
990 m_arrInvalidSlots.clear();
993 void FmXFormShell::ForceUpdateSelection_Lock()
995 if (impl_checkDisposed_Lock())
996 return;
998 if (IsSelectionUpdatePending_Lock())
1000 m_aMarkTimer.Stop();
1002 // optionally turn off the invalidation of slots which is implicitly done by SetSelection
1003 LockSlotInvalidation_Lock(true);
1005 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
1007 LockSlotInvalidation_Lock(false);
1011 void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
1013 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1015 // the corresponding image at it
1016 rNewMenu.append(OUString(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), aImgIds[i]);
1020 OUString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
1022 static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
1024 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1026 if (nSlot == SelObjectSlotMap[i])
1027 return OUString(aConvertSlots[i]);
1030 return {};
1033 bool FmXFormShell::isControlConversionSlot(std::u16string_view rIdent)
1035 for (const auto& rConvertSlot : aConvertSlots)
1036 if (rIdent == rConvertSlot)
1037 return true;
1038 return false;
1041 void FmXFormShell::executeControlConversionSlot_Lock(std::u16string_view rIdent)
1043 OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
1044 InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
1045 if ( aSelectedElement == m_aCurrentSelection.end() )
1046 return;
1048 executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
1051 bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, std::u16string_view rIdent)
1053 if (impl_checkDisposed_Lock())
1054 return false;
1056 OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
1057 if ( !_rxObject.is() )
1058 return false;
1060 SdrPage* pPage = m_pShell->GetCurPage();
1061 FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1062 OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
1063 if ( !pFormPage )
1064 return false;
1066 OSL_ENSURE( isSolelySelected_Lock(_rxObject),
1067 "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
1069 for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
1071 if (rIdent == aConvertSlots[lookupSlot])
1073 Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
1075 FmFormObj* pFormObject = nullptr;
1076 SdrObjListIter aPageIter( pFormPage );
1077 while ( aPageIter.IsMore() )
1079 SdrObject* pCurrent = aPageIter.Next();
1080 pFormObject = FmFormObj::GetFormObject( pCurrent );
1081 if ( !pFormObject )
1082 continue;
1084 Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
1085 if ( xCurrentNormalized.get() == xNormalizedObject.get() )
1086 break;
1088 pFormObject = nullptr;
1091 if ( !pFormObject )
1092 return false;
1094 OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
1095 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1096 Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
1097 if (!xNewModel.is())
1098 return false;
1100 Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
1102 // transfer properties
1103 Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
1104 Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
1107 lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
1108 TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
1110 Sequence< css::script::ScriptEventDescriptor> aOldScripts;
1111 Reference< XChild> xChild(xOldModel, UNO_QUERY);
1112 if (xChild.is())
1114 Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
1116 // remember old script events
1117 Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
1118 if (xParent.is() && xEvManager.is())
1120 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1121 if (nIndex>=0 && nIndex<xParent->getCount())
1122 aOldScripts = xEvManager->getScriptEvents(nIndex);
1125 // replace the model within the parent container
1126 Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
1127 if (xIndexParent.is())
1129 // the form container works with FormComponents
1130 Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
1131 DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
1132 Any aNewModel(xComponent);
1136 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1137 if (nIndex>=0 && nIndex<xParent->getCount())
1138 xIndexParent->replaceByIndex(nIndex, aNewModel);
1139 else
1141 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1142 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1143 if (xNewComponent.is())
1144 xNewComponent->dispose();
1145 return false;
1148 catch(Exception&)
1150 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1151 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1152 if (xNewComponent.is())
1153 xNewComponent->dispose();
1154 return false;
1160 // special handling for the LabelControl-property : can only be set when the model is placed
1161 // within the forms hierarchy
1162 if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
1166 xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
1168 catch(Exception&)
1174 // set new model
1175 pFormObject->SetChanged();
1176 pFormObject->SetUnoControlModel(xNewModel);
1178 // transfer script events
1179 // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
1180 if (aOldScripts.hasElements())
1182 // find the control for the model
1183 Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
1185 const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
1187 Reference< XControl> xControl;
1188 auto pControl = std::find_if(aControls.begin(), aControls.end(),
1189 [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
1190 if (pControl != aControls.end())
1191 xControl = *pControl;
1192 TransferEventScripts(xNewModel, xControl, aOldScripts);
1195 // transfer value bindings, if possible
1197 Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
1198 Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
1199 if ( xOldBindable.is() )
1203 if ( xNewBindable.is() )
1204 xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
1205 xOldBindable->setValueBinding( nullptr );
1207 catch(const Exception&)
1209 DBG_UNHANDLED_EXCEPTION("svx");
1213 // same for list entry sources
1215 Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
1216 Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
1217 if ( xOldSink.is() )
1221 if ( xNewSink.is() )
1222 xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
1223 xOldSink->setListEntrySource( nullptr );
1225 catch(const Exception&)
1227 DBG_UNHANDLED_EXCEPTION("svx");
1232 // create an undo action
1233 FmFormModel* pModel = m_pShell->GetFormModel();
1234 DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
1235 if (pModel && pModel->IsUndoEnabled() )
1237 pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
1239 else
1241 FmUndoModelReplaceAction::DisposeElement( xOldModel );
1244 return true;
1247 return false;
1250 bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::u16string_view rIdent)
1252 if ( m_aCurrentSelection.empty() )
1253 return false;
1255 InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
1256 Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
1257 if ( !xElementInfo.is() )
1258 // no service info -> cannot determine this
1259 return false;
1261 if ( ++aCheck != m_aCurrentSelection.end() )
1262 // more than one element
1263 return false;
1265 if ( Reference< XForm >::query( xElementInfo ).is() )
1266 // it's a form
1267 return false;
1269 SdrObjKind nObjectType = getControlTypeByObject( xElementInfo );
1271 if ( ( SdrObjKind::FormHidden == nObjectType )
1272 || ( SdrObjKind::FormControl == nObjectType )
1273 || ( SdrObjKind::FormGrid == nObjectType )
1275 return false; // those types cannot be converted
1277 DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
1278 "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
1280 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1281 if (rIdent == aConvertSlots[i])
1282 return nObjectTypes[i] != nObjectType;
1284 return true; // all other slots: assume "yes"
1287 void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu)
1289 for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
1291 // the context is already of a type that corresponds to the entry -> disable
1292 OUString sIdent(aConvertSlots[i]);
1293 rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
1297 void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
1299 if (impl_checkDisposed_Lock())
1300 return;
1302 Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
1303 if (!xControlModels.is())
1304 return;
1306 for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
1308 Reference< XPropertySet> xModelSet;
1309 xControlModels->getByIndex(i) >>= xModelSet;
1310 if (!xModelSet.is())
1311 continue;
1313 if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
1314 continue;
1315 sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
1316 if (FormComponentType::GRIDCONTROL != nClassId)
1317 continue;
1319 if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
1320 continue;
1322 switch (nSync)
1324 case LoopGridsSync::DISABLE_SYNC:
1326 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
1328 break;
1329 case LoopGridsSync::FORCE_SYNC:
1331 Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
1332 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1333 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
1335 break;
1336 case LoopGridsSync::ENABLE_SYNC:
1338 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1340 break;
1343 if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
1345 xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
1346 Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
1347 if (xModelPropState.is())
1348 xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
1349 else
1350 xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
1356 Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
1358 if (impl_checkDisposed_Lock())
1359 return nullptr;
1361 SdrPageView* pPageView = nullptr;
1362 if ( m_pShell && m_pShell->GetFormView() )
1363 pPageView = m_pShell->GetFormView()->GetSdrPageView();
1365 Reference< XControlContainer> xControlContainer;
1366 if ( pPageView )
1367 xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
1369 return xControlContainer;
1373 void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
1375 if (impl_checkDisposed_Lock())
1376 return;
1378 OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
1379 if ( !_rxForForm.is() )
1380 return;
1384 Reference< XWindow > xParentWindow;
1385 if (m_pShell->GetViewShell())
1386 xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame().GetWindow() );
1388 Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
1389 comphelper::getProcessComponentContext(),
1390 _rxForForm, getControlContainerForView_Lock(), xParentWindow
1393 xDialog->execute();
1395 catch( const Exception& )
1397 TOOLS_WARN_EXCEPTION( "svx", "FmXFormShell::ExecuteTabOrderDialog" );
1402 void FmXFormShell::ExecuteSearch_Lock()
1404 if (impl_checkDisposed_Lock())
1405 return;
1407 // a collection of all (logical) forms
1408 FmFormArray().swap(m_aSearchForms);
1409 ::std::vector< OUString > aContextNames;
1410 impl_collectFormSearchContexts_nothrow_Lock(
1411 m_pShell->GetCurPage()->GetForms(), u"",
1412 m_aSearchForms, aContextNames);
1414 if ( m_aSearchForms.size() != aContextNames.size() )
1416 SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
1417 return;
1420 // filter out the forms which do not contain valid controls at all
1422 FmFormArray aValidForms;
1423 ::std::vector< OUString > aValidContexts;
1424 FmFormArray::const_iterator form = m_aSearchForms.begin();
1425 ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
1426 for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
1428 FmSearchContext aTestContext;
1429 aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
1430 sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
1431 if ( nValidControls > 0 )
1433 aValidForms.push_back( *form );
1434 aValidContexts.push_back( *contextName );
1438 m_aSearchForms.swap( aValidForms );
1439 aContextNames.swap( aValidContexts );
1442 if (m_aSearchForms.empty() )
1444 // there are no controls that meet all the conditions for a search
1445 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
1446 VclMessageType::Warning, VclButtonsType::Ok,
1447 SvxResId(RID_STR_NODATACONTROLS)));
1448 xBox->run();
1449 return;
1452 // now I need another 'initial context'
1453 sal_Int16 nInitialContext = 0;
1454 Reference<XForm> xActiveForm(getActiveForm_Lock());
1455 for ( size_t i=0; i<m_aSearchForms.size(); ++i )
1457 if (m_aSearchForms.at(i) == xActiveForm)
1459 nInitialContext = static_cast<sal_Int16>(i);
1460 break;
1464 // If the dialog should initially offer the text of the active control,
1465 // this must have an XTextComponent interface. An addition, this makes
1466 // sense only if the current field is also bound to a table (or whatever) field.
1467 OUString strActiveField;
1468 OUString strInitialText;
1469 // ... this I get from my FormController
1470 DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
1471 Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
1472 if (xActiveControl.is())
1474 // the control can tell me its model ...
1475 Reference< XControlModel> xActiveModel( xActiveControl->getModel());
1476 DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
1478 // I ask the model for the ControlSource property ...
1479 Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
1480 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
1482 Reference< XPropertySet> xField;
1483 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1484 if (xField.is()) // (only when the thing is really bound)
1486 // and the control itself for a TextComponent interface (so that I can pick up the text there)
1487 Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
1488 if (xText.is())
1490 strActiveField = getLabelName(xProperties);
1491 strInitialText = xText->getText();
1495 else
1497 // the control itself has no ControlSource, but maybe it is a GridControl
1498 Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
1499 if (xGrid.is())
1501 // for strActiveField I need the ControlSource of the column,
1502 // for that the columns container, for that the GridPeer
1503 Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
1504 Reference< XIndexAccess> xColumns;
1505 if (xGridPeer.is())
1506 xColumns = xGridPeer->getColumns();
1508 sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
1509 sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
1510 Reference< XPropertySet> xCurrentCol;
1511 if(xColumns.is())
1512 xColumns->getByIndex(nModelCol) >>= xCurrentCol;
1513 if (xCurrentCol.is())
1514 strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
1516 // the text of the current column
1517 Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
1518 Reference< XInterface> xCurControl;
1519 xColControls->getByIndex(nViewCol) >>= xCurControl;
1520 OUString sInitialText;
1521 if (IsSearchableControl(xCurControl, &sInitialText))
1522 strInitialText = sInitialText;
1527 // taking care of possible GridControls that I know
1528 LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
1530 // Now I am ready for the dialogue.
1531 // When the potential deadlocks caused by the use of the solar mutex in
1532 // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
1533 // placed here, because the search in a separate thread is nevertheless
1534 // somewhat more fluid. Should be, however, somehow made dependent of the
1535 // underlying cursor. DAO for example is not thread-safe.
1536 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1537 ScopedVclPtr<AbstractFmSearchDialog> pDialog(
1538 pFact->CreateFmSearchDialog(
1539 m_pShell->GetViewShell()->GetViewFrame().GetFrameWeld(),
1540 strInitialText, aContextNames, nInitialContext,
1541 LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
1542 pDialog->SetActiveField( strActiveField );
1543 pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
1544 pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
1545 pDialog->Execute();
1546 pDialog.disposeAndClear();
1548 // restore GridControls again
1549 LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
1551 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
1552 // because I marked controls in OnFoundData (if I was there)
1556 bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
1558 if (impl_checkDisposed_Lock())
1559 return false;
1561 if (m_pShell->IsDesignMode())
1562 // in the design mode (without active controls) the main document is to take care of it
1563 return false;
1565 Reference<XForm> xForm(getActiveForm_Lock());
1566 if (!xForm.is())
1567 // no current form (in particular no current control) -> the main document is to take care
1568 return false;
1570 Reference< XRowSet> xDB(xForm, UNO_QUERY);
1571 DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
1573 Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
1574 if (xSupplier.is())
1576 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1577 if (xSet.is())
1581 Any aVal( xSet->getPropertyValue("TwoDigitDateStart") );
1582 aVal >>= n;
1583 return true;
1585 catch(Exception&)
1591 return false;
1595 void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
1597 if (impl_checkDisposed_Lock())
1598 return;
1600 Reference<XForm> xActiveForm(getActiveForm_Lock());
1601 Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
1602 if ( xActiveRowSet.is() )
1604 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
1605 if (xSupplier.is())
1607 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1608 if (xSet.is())
1612 xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
1614 catch(Exception&)
1616 TOOLS_WARN_EXCEPTION("svx.form", "");
1620 return;
1624 // no active form found -> iterate through all current forms
1625 Reference< XIndexAccess> xCurrentForms( m_xForms);
1626 if (!xCurrentForms.is())
1627 { // in the alive mode, my forms are not set, but the ones on the page are
1628 if (m_pShell->GetCurPage())
1629 xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
1631 if (!xCurrentForms.is())
1632 return;
1634 ::comphelper::IndexAccessIterator aIter(xCurrentForms);
1635 Reference< XInterface> xCurrentElement( aIter.Next());
1636 while (xCurrentElement.is())
1638 // is the current element a DatabaseForm?
1639 Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
1640 if ( xElementAsRowSet.is() )
1642 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
1643 if (!xSupplier.is())
1644 continue;
1646 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1647 if (xSet.is())
1651 xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
1653 catch(Exception&)
1655 TOOLS_WARN_EXCEPTION("svx.form", "");
1660 xCurrentElement = aIter.Next();
1665 void FmXFormShell::CloseExternalFormViewer_Lock()
1667 if (impl_checkDisposed_Lock())
1668 return;
1670 if (!m_xExternalViewController.is())
1671 return;
1673 Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
1674 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
1675 if (!xCommLink.is())
1676 return;
1678 xExternalViewFrame->setComponent(nullptr,nullptr);
1679 ::comphelper::disposeComponent(xExternalViewFrame);
1680 m_xExternalViewController = nullptr;
1681 m_xExtViewTriggerController = nullptr;
1682 m_xExternalDisplayedForm = nullptr;
1686 Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _xForm) const
1688 if (impl_checkDisposed_Lock())
1689 return nullptr;
1691 Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1692 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1694 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1695 return m_xExternalDisplayedForm;
1697 return _xForm;
1701 Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
1703 if (impl_checkDisposed_Lock())
1704 return nullptr;
1706 Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1707 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1709 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1710 return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
1712 return _xForm;
1716 namespace
1718 bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
1720 return ( _nWhich == SID_FM_RECORD_FIRST )
1721 || ( _nWhich == SID_FM_RECORD_PREV )
1722 || ( _nWhich == SID_FM_RECORD_NEXT )
1723 || ( _nWhich == SID_FM_RECORD_LAST )
1724 || ( _nWhich == SID_FM_RECORD_NEW );
1729 bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState ) const
1731 const svx::ControllerFeatures& rController =
1732 lcl_isNavigationRelevant( _nSlot )
1733 ? getNavControllerFeatures_Lock()
1734 : getActiveControllerFeatures_Lock();
1736 if ( !_pCompleteState )
1737 return rController->isEnabled( _nSlot );
1739 rController->getState( _nSlot, *_pCompleteState );
1740 return _pCompleteState->Enabled;
1744 void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
1746 const svx::ControllerFeatures& rController =
1747 lcl_isNavigationRelevant( _nSlot )
1748 ? getNavControllerFeatures_Lock()
1749 : getActiveControllerFeatures_Lock();
1751 rController->execute( _nSlot );
1753 if ( _nSlot != SID_FM_RECORD_UNDO )
1754 return;
1756 // if we're doing an UNDO, *and* if the affected form is the form which we also display
1757 // as external view, then we need to reset the controls of the external form, too
1758 if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
1759 return;
1761 Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
1762 if ( !xContainer.is() )
1763 return;
1765 Reference< XReset > xReset;
1766 for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
1768 if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
1770 // no resets on sub forms
1771 Reference< XForm > xAsForm( xReset, UNO_QUERY );
1772 if ( !xAsForm.is() )
1773 xReset->reset();
1778 void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
1780 if ( !m_xActiveController.is() )
1781 return;
1783 if ( _bListen )
1784 m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
1785 else
1786 m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
1789 void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
1791 if (impl_checkDisposed_Lock())
1792 return;
1794 if (m_bChangingDesignMode)
1795 return;
1796 DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
1798 // if the routine has been called a second time,
1799 // the focus should no longer be transferred
1800 if (m_bInActivate)
1802 m_bSetFocus = xController != m_xActiveController;
1803 return;
1806 if (xController == m_xActiveController)
1807 return;
1809 // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
1810 Reference< XResultSet> xNavigationForm;
1811 if (m_xNavigationController.is())
1812 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1814 m_bInActivate = true;
1816 // check if the 2 controllers serve different forms
1817 Reference< XResultSet> xOldForm;
1818 if (m_xActiveController.is())
1819 xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
1820 Reference< XResultSet> xNewForm;
1821 if (xController.is())
1822 xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
1823 xOldForm = getInternalForm_Lock(xOldForm);
1824 xNewForm = getInternalForm_Lock(xNewForm);
1826 bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
1827 bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
1828 // we save the content of the old form if we move to a new form, and saving old content is allowed
1830 if ( m_xActiveController.is() && bNeedSave )
1832 // save content on change of the controller; a commit has already been executed
1833 if ( m_aActiveControllerFeatures->commitCurrentControl() )
1835 m_bSetFocus = true;
1836 if ( m_aActiveControllerFeatures->isModifiedRow() )
1838 bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
1839 bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
1840 if ( !bResult && m_bSetFocus )
1842 // if we couldn't save the current record, set the focus back to the
1843 // current control
1844 Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
1845 if ( xWindow.is() )
1846 xWindow->setFocus();
1847 m_bInActivate = false;
1848 return;
1850 else if ( bResult && bIsNew )
1852 Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
1853 if ( xCursor.is() )
1855 DO_SAFE( xCursor->last(); );
1862 stopListening_Lock();
1864 impl_switchActiveControllerListening_Lock(false);
1866 m_aActiveControllerFeatures.dispose();
1867 m_xActiveController = xController;
1868 if ( m_xActiveController.is() )
1869 m_aActiveControllerFeatures.assign( m_xActiveController );
1871 impl_switchActiveControllerListening_Lock(true);
1873 if ( m_xActiveController.is() )
1874 m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
1875 else
1876 m_xActiveForm = nullptr;
1878 startListening_Lock();
1880 // activate all dispatchers belonging to form of the new navigation controller
1881 xNavigationForm = nullptr;
1882 if (m_xNavigationController.is())
1883 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1885 m_bInActivate = false;
1887 m_pShell->UIFeatureChanged();
1888 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
1890 InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
1893 void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
1895 _rSelection = m_aCurrentSelection;
1898 bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
1900 m_aLastKnownMarkedControls.clear();
1902 if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
1903 collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
1905 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1909 bool FmXFormShell::selectLastMarkedControls_Lock()
1911 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1915 bool FmXFormShell::setCurrentSelection_Lock( InterfaceBag&& _rSelection )
1917 if (impl_checkDisposed_Lock())
1918 return false;
1920 DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
1922 if ( _rSelection.empty() && m_aCurrentSelection.empty() )
1923 // nothing to do
1924 return false;
1926 if ( _rSelection.size() == m_aCurrentSelection.size() )
1928 InterfaceBag::const_iterator aNew = _rSelection.begin();
1929 InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
1930 for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
1932 OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
1933 OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
1935 if ( aNew->get() != aOld->get() )
1936 break;
1939 if ( aNew == _rSelection.end() )
1940 // both bags equal
1941 return false;
1944 // the following is some strange code to ensure that when you have two grid controls in a document,
1945 // only one of them can have a selected column.
1946 // TODO: this should happen elsewhere, but not here - shouldn't it?
1947 if ( !m_aCurrentSelection.empty() )
1949 Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
1950 Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
1952 // is there nothing to be selected, or the parents differ, and the parent of the current object
1953 // is a selection supplier, then deselect
1954 if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
1956 Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
1957 if ( xSel.is() )
1958 xSel->select( Any() );
1962 m_aCurrentSelection = _rSelection;
1964 // determine the form which all the selected objects belong to, if any
1965 Reference< XForm > xNewCurrentForm;
1966 for (const auto& rpSelection : m_aCurrentSelection)
1968 Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
1969 OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
1971 if ( !xNewCurrentForm.is() )
1972 { // the first form we encountered
1973 xNewCurrentForm = xThisRoundsForm;
1975 else if ( xNewCurrentForm != xThisRoundsForm )
1976 { // different forms -> no "current form" at all
1977 xNewCurrentForm.clear();
1978 break;
1982 if ( !m_aCurrentSelection.empty() )
1983 impl_updateCurrentForm_Lock(xNewCurrentForm);
1985 // ensure some slots are updated
1986 for (sal_Int16 i : SelObjectSlotMap)
1987 InvalidateSlot_Lock(i, false);
1989 return true;
1993 bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
1995 return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
1999 void FmXFormShell::forgetCurrentForm_Lock()
2001 if ( !m_xCurrentForm.is() )
2002 return;
2004 // reset ...
2005 impl_updateCurrentForm_Lock(nullptr);
2007 // ... and try finding a new current form
2008 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
2009 impl_defaultCurrentForm_nothrow_Lock();
2013 void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
2015 if (impl_checkDisposed_Lock())
2016 return;
2018 m_xCurrentForm = _rxNewCurForm;
2020 // propagate to the FormPage(Impl)
2021 FmFormPage* pPage = m_pShell->GetCurPage();
2022 if ( pPage )
2023 pPage->GetImpl().setCurForm( m_xCurrentForm );
2025 // ensure the UI which depends on the current form is up-to-date
2026 for (sal_Int16 i : DlgSlotMap)
2027 InvalidateSlot_Lock(i, false);
2031 void FmXFormShell::startListening_Lock()
2033 if (impl_checkDisposed_Lock())
2034 return;
2036 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2037 if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
2039 Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
2040 if (xActiveFormSet.is())
2042 // if there is a data source, then build the listener
2043 // TODO: this is strange - shouldn't this depend on a isLoaded instead of
2044 // a "has command value"? Finally, the command value only means that it was
2045 // intended to be loaded, not that it actually *is* loaded
2046 OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
2047 if (!aSource.isEmpty())
2049 m_bDatabaseBar = true;
2051 xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2053 switch (m_eNavigate)
2055 case NavigationBarMode_PARENT:
2057 // search for the controller via which navigation is possible
2058 Reference< XChild> xChild = m_xActiveController;
2059 Reference< runtime::XFormController > xParent;
2060 while (xChild.is())
2062 xChild.set(xChild->getParent(), UNO_QUERY);
2063 xParent.set(xChild, UNO_QUERY);
2064 Reference< XPropertySet> xParentSet;
2065 if (xParent.is())
2066 xParentSet.set(xParent->getModel(), UNO_QUERY);
2067 if (xParentSet.is())
2069 xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2070 if (m_eNavigate == NavigationBarMode_CURRENT)
2071 break;
2074 m_xNavigationController = xParent;
2076 break;
2078 case NavigationBarMode_CURRENT:
2079 m_xNavigationController = m_xActiveController;
2080 break;
2082 default:
2083 m_xNavigationController = nullptr;
2084 m_bDatabaseBar = false;
2087 m_aNavControllerFeatures.dispose();
2088 if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
2089 m_aNavControllerFeatures.assign( m_xNavigationController );
2091 // because of RecordCount, listen at the controller which controls the navigation
2092 Reference< XPropertySet> xNavigationSet;
2093 if (m_xNavigationController.is())
2095 xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
2096 if (xNavigationSet.is())
2097 xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
2099 return;
2104 m_eNavigate = NavigationBarMode_NONE;
2105 m_bDatabaseBar = false;
2106 m_xNavigationController = nullptr;
2110 void FmXFormShell::stopListening_Lock()
2112 if (impl_checkDisposed_Lock())
2113 return;
2115 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2116 if ( xDatabaseForm.is() )
2118 if (m_xNavigationController.is())
2120 Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
2121 if (xSet.is())
2122 xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2127 m_bDatabaseBar = false;
2128 m_eNavigate = NavigationBarMode_NONE;
2129 m_xNavigationController = nullptr;
2132 void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
2134 if (impl_checkDisposed_Lock())
2135 return;
2137 // if the window is already visible, only update the state
2138 bool bHasChild = m_pShell->GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_SHOW_PROPERTIES );
2139 if ( bHasChild && bShow )
2140 UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
2142 // else toggle state
2143 else
2144 m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2146 InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
2147 InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
2150 IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
2152 if (impl_checkDisposed_Lock())
2153 return;
2155 DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
2156 "FmXFormShell::OnFoundData : invalid context!");
2157 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2158 DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
2160 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2161 if (!xCursor.is())
2162 return; // what should I do there?
2164 // to the record
2167 xCursor->moveToBookmark(rfriWhere.aPosition);
2169 catch(const SQLException&)
2171 OSL_FAIL("Can position on bookmark!");
2174 LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
2176 // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
2177 SAL_WARN_IF(o3tl::make_unsigned(rfriWhere.nFieldPos) >=
2178 m_arrSearchedControls.size(),
2179 "svx.form", "FmXFormShell::OnFoundData : invalid index!");
2180 SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
2182 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2183 m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
2185 FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
2186 Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
2187 DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
2188 if ( !xControlModel.is() )
2189 return;
2191 // disable the permanent cursor for the last grid we found a record
2192 if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
2194 Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
2195 xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any( false ) );
2196 Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
2197 if (xOldSetState.is())
2198 xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
2199 else
2200 xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
2203 // if the field is in a GridControl, I have to additionally go into the corresponding column there
2204 sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
2205 if (nGridColumn != -1)
2206 { // unfortunately, I have to first get the control again
2207 Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
2208 Reference< XGrid> xGrid(xControl, UNO_QUERY);
2209 DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
2210 // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
2212 // enable a permanent cursor for the grid so we can see the found text
2213 Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
2214 DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
2215 xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, Any( true ) );
2216 xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, Any( COL_LIGHTRED ) );
2217 m_xLastGridFound = xControlModel;
2219 if ( xGrid.is() )
2220 xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
2223 // As the cursor has been repositioned, I have (in positioned) invalidated
2224 // my form bar slots. But that does not take effect here unfortunately, as
2225 // generally the (modal) search dialog is of course at the top ... So, force ...
2226 sal_uInt16 nPos = 0;
2227 while (DatabaseSlotMap[nPos])
2228 m_pShell->GetViewShell()->GetViewFrame().GetBindings().Update(DatabaseSlotMap[nPos++]);
2229 // unfortunately the update goes against the invalidate with only individual slots
2232 IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
2234 if (impl_checkDisposed_Lock())
2235 return;
2237 DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
2238 "FmXFormShell::OnCanceledNotFound : invalid context!");
2239 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2240 DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
2242 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2243 if (!xCursor.is())
2244 return; // what should I do there?
2246 // to the record
2249 xCursor->moveToBookmark(rfriWhere.aPosition);
2251 catch(const SQLException&)
2253 OSL_FAIL("Can position on bookmark!");
2257 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2261 IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
2263 if (impl_checkDisposed_Lock())
2264 return 0;
2266 DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
2267 Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
2268 DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
2270 Reference< XResultSet> xIter(xForm, UNO_QUERY);
2271 DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
2274 // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
2275 OUString strFieldList, sFieldDisplayNames;
2276 m_arrSearchedControls.clear();
2277 m_arrRelativeGridColumn.clear();
2279 // small problem: To mark found fields, I need SdrObjects. To determine which controls
2280 // to include in the search, I need Controls (that is, XControl interfaces). So I have
2281 // to iterate over one of them and get the other in some way. Unfortunately, there is
2282 // no direct connection between the two worlds (except from a GetUnoControl to a
2283 // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
2284 // However I can get to the Model from the Control and also from the SdrObject, and in
2285 // this way the assignment SdrObject<->Control is possible with a double loop.
2286 // The alternative to this (ugly but certainly not entirely fixable) solution would be
2287 // to renounce the caching of the SdrObjects, which would lead to significant extra
2288 // work in OnFoundData (since there I'd have to get the SdrObject first thing every
2289 // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
2290 // do that here.
2292 Reference< XNameAccess> xValidFormFields;
2293 Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
2294 DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
2295 if (xSupplyCols.is())
2296 xValidFormFields = xSupplyCols->getColumns();
2297 DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
2299 // current Page/Controller
2300 FmFormPage* pCurrentPage = m_pShell->GetCurPage();
2301 assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
2302 // Search all SdrControls of this page...
2303 OUString sControlSource, aName;
2305 SdrObjListIter aPageIter( pCurrentPage );
2306 while ( aPageIter.IsMore() )
2308 SdrObject* pCurrent = aPageIter.Next();
2309 FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
2310 // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
2312 if ( !pFormObject )
2313 continue;
2315 // the current object's model, in different tastes
2316 Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
2317 Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
2318 DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
2319 if ( !xCurrentFormComponent.is() )
2320 continue;
2322 // does the component belong to the form which we're interested in?
2323 if ( xCurrentFormComponent->getParent() != xForm )
2324 continue;
2326 // ... ask for the ControlSource property
2327 SearchableControlIterator iter( xCurrentFormComponent );
2328 Reference< XControl> xControl;
2329 // the control that has model xControlModel
2330 // (the following while can be passed through several times, without the Control
2331 // being modified, so I don't have to search every time from scratch)
2333 Reference< XInterface > xSearchable( iter.Next() );
2334 while ( xSearchable.is() )
2336 sControlSource = iter.getCurrentValue();
2337 if ( sControlSource.isEmpty() )
2339 // the current element has no ControlSource, so it is a GridControl (that
2340 // is the only thing that still permits the SearchableControlIteratore)
2341 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2342 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2344 Reference< XGridPeer> xGridPeer;
2345 if ( xControl.is() )
2346 xGridPeer.set( xControl->getPeer(), UNO_QUERY );
2349 if (!xGridPeer.is())
2350 break;
2352 Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
2353 if (!xPeerContainer.is())
2354 break;
2356 Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
2357 DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
2358 // the case 'no columns' should be indicated with an empty container, I think ...
2359 DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
2361 Reference< XInterface> xCurrentColumn;
2362 for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
2364 xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
2365 if (!xCurrentColumn.is())
2366 continue;
2368 // can we use this column control for searching ?
2369 if (!IsSearchableControl(xCurrentColumn))
2370 continue;
2372 sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
2373 Reference< XPropertySet> xCurrentColModel;
2374 xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
2375 aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
2376 // the cursor has a field matching the control source ?
2377 if (xValidFormFields->hasByName(aName))
2379 strFieldList += aName + ";";
2381 sFieldDisplayNames +=
2382 ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
2383 ";";
2385 rfmscContextInfo.arrFields.push_back(xCurrentColumn);
2387 // and the SdrOject to the Field
2388 m_arrSearchedControls.push_back(pCurrent);
2389 // the number of the column
2390 m_arrRelativeGridColumn.push_back(nViewPos);
2393 } while (false);
2395 else
2397 if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
2399 // now I need the Control to SdrObject
2400 if (!xControl.is())
2402 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2403 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2406 if (IsSearchableControl(xControl))
2408 // all tests passed -> take along in the list
2409 strFieldList += sControlSource + ";";
2411 // the label which should appear for the control :
2412 sFieldDisplayNames +=
2413 getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
2414 ";";
2416 // mark the SdrObject (accelerates the treatment in OnFoundData)
2417 m_arrSearchedControls.push_back(pCurrent);
2419 // the number of the column (here a dummy, since it is only interesting for GridControls)
2420 m_arrRelativeGridColumn.push_back(-1);
2422 // and for the formatted search...
2423 rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
2428 xSearchable = iter.Next();
2432 strFieldList = comphelper::string::stripEnd(strFieldList, ';');
2433 sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
2435 if (rfmscContextInfo.arrFields.empty())
2437 rfmscContextInfo.arrFields.clear();
2438 rfmscContextInfo.xCursor = nullptr;
2439 rfmscContextInfo.strUsedFields.clear();
2440 return 0;
2443 rfmscContextInfo.xCursor = xIter;
2444 rfmscContextInfo.strUsedFields = strFieldList;
2445 rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
2447 // 66463 - 31.05.99 - FS
2448 // when the cursor is a non-STANDARD RecordMode, set it back
2449 Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
2450 Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
2451 if (xUpdateCursor.is() && xCursorSet.is())
2453 if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
2454 xUpdateCursor->moveToCurrentRow();
2455 else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
2456 xUpdateCursor->cancelRowUpdates();
2459 return rfmscContextInfo.arrFields.size();
2462 // XContainerListener
2464 void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
2466 SolarMutexGuard g;
2468 if (impl_checkDisposed_Lock())
2469 return;
2471 // new object to listen to
2472 Reference< XInterface> xTemp;
2473 evt.Element >>= xTemp;
2474 AddElement_Lock(xTemp);
2476 m_pShell->DetermineForms(true);
2480 void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
2482 SolarMutexGuard g;
2484 if (impl_checkDisposed_Lock() )
2485 return;
2487 Reference< XInterface> xTemp;
2488 evt.ReplacedElement >>= xTemp;
2489 RemoveElement_Lock(xTemp);
2490 evt.Element >>= xTemp;
2491 AddElement_Lock(xTemp);
2495 void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
2497 SolarMutexGuard g;
2499 if (impl_checkDisposed_Lock())
2500 return;
2502 Reference< XInterface> xTemp;
2503 evt.Element >>= xTemp;
2504 RemoveElement_Lock(xTemp);
2506 m_pShell->DetermineForms(true);
2510 void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
2512 if (impl_checkDisposed_Lock())
2513 return;
2515 Reference< XIndexAccess > xForms;
2517 FmFormPage* pPage = m_pShell->GetCurPage();
2518 if ( pPage && m_pShell->m_bDesignMode )
2519 xForms = pPage->GetForms( false );
2521 if ( m_xForms != xForms )
2523 RemoveElement_Lock( m_xForms );
2524 m_xForms = xForms;
2525 AddElement_Lock(m_xForms);
2528 SolarMutexGuard g;
2529 m_pShell->DetermineForms( _bInvalidate );
2533 void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
2535 if (impl_checkDisposed_Lock())
2536 return;
2537 impl_AddElement_nothrow(_xElement);
2540 void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
2542 // listen at the container
2543 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2544 if (xContainer.is())
2546 const sal_uInt32 nCount = xContainer->getCount();
2547 Reference< XInterface> xElement;
2548 for (sal_uInt32 i = 0; i < nCount; ++i)
2550 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2551 impl_AddElement_nothrow(xElement);
2554 const Reference< XContainer> xCont(Element, UNO_QUERY);
2555 if (xCont.is())
2556 xCont->addContainerListener(this);
2559 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2560 if (xSelSupplier.is())
2561 xSelSupplier->addSelectionChangeListener(this);
2565 void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
2567 if (impl_checkDisposed_Lock())
2568 return;
2569 impl_RemoveElement_nothrow_Lock(Element);
2572 void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
2574 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2575 if (xSelSupplier.is())
2576 xSelSupplier->removeSelectionChangeListener(this);
2578 // remove connection to children
2579 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2580 if (xContainer.is())
2582 const Reference< XContainer> xCont(Element, UNO_QUERY);
2583 if (xCont.is())
2584 xCont->removeContainerListener(this);
2586 const sal_uInt32 nCount = xContainer->getCount();
2587 Reference< XInterface> xElement;
2588 for (sal_uInt32 i = 0; i < nCount; i++)
2590 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2591 impl_RemoveElement_nothrow_Lock(xElement);
2595 auto wasSelectedPos = m_aCurrentSelection.find( Element );
2596 if ( wasSelectedPos != m_aCurrentSelection.end() )
2597 m_aCurrentSelection.erase( wasSelectedPos );
2601 void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
2603 SolarMutexGuard g;
2605 if (impl_checkDisposed_Lock())
2606 return;
2608 Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
2609 Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
2610 // a selection was removed, this can only be done by the shell
2611 if ( !xSelObj.is() )
2612 return;
2614 EnableTrackProperties_Lock(false);
2616 bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
2618 InterfaceBag aNewSelection;
2619 aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
2621 if (setCurrentSelection_Lock(std::move(aNewSelection)) && IsPropBrwOpen_Lock())
2622 ShowSelectionProperties_Lock(true);
2624 EnableTrackProperties_Lock(true);
2626 if ( bMarkChanged )
2627 m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
2631 IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
2633 if (impl_checkDisposed_Lock())
2634 return;
2636 if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
2637 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
2641 void FmXFormShell::SetSelectionDelayed_Lock()
2643 if (impl_checkDisposed_Lock())
2644 return;
2646 if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
2647 m_aMarkTimer.Start();
2651 void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
2653 if (impl_checkDisposed_Lock())
2654 return;
2656 DetermineSelection_Lock(rMarkList);
2657 m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
2660 void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
2662 if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
2663 ShowSelectionProperties_Lock(true);
2666 bool FmXFormShell::IsPropBrwOpen_Lock() const
2668 if (impl_checkDisposed_Lock())
2669 return false;
2671 return m_pShell->GetViewShell() &&
2672 m_pShell->GetViewShell()->GetViewFrame().HasChildWindow(SID_FM_SHOW_PROPERTIES);
2675 class FmXFormShell::SuspendPropertyTracking
2677 private:
2678 FmXFormShell& m_rShell;
2679 bool m_bEnabled;
2681 public:
2682 explicit SuspendPropertyTracking( FmXFormShell& _rShell )
2683 :m_rShell( _rShell )
2684 ,m_bEnabled( false )
2686 if (m_rShell.IsTrackPropertiesEnabled_Lock())
2688 m_rShell.EnableTrackProperties_Lock(false);
2689 m_bEnabled = true;
2693 ~SuspendPropertyTracking( )
2695 if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
2696 m_rShell.EnableTrackProperties_Lock(true);
2700 void FmXFormShell::SetDesignMode_Lock(bool bDesign)
2702 if (impl_checkDisposed_Lock())
2703 return;
2705 DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
2706 m_bChangingDesignMode = true;
2708 // 67506 - 15.07.99 - FS
2709 // if we're switching off the design mode we have to force the property browser to be closed
2710 // so it can commit it's changes _before_ we load the forms
2711 if (!bDesign)
2713 m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame().HasChildWindow(SID_FM_SHOW_PROPERTIES);
2714 if (m_bHadPropertyBrowserInDesignMode)
2715 m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2718 FmFormView* pFormView = m_pShell->GetFormView();
2719 if (bDesign)
2721 // we are currently filtering, so stop filtering
2722 if (m_bFilterMode)
2723 stopFiltering_Lock(false);
2725 // unsubscribe from the objects of my MarkList
2726 pFormView->GetImpl()->stopMarkListWatching();
2728 else
2730 m_aMarkTimer.Stop();
2732 SuspendPropertyTracking aSuspend( *this );
2733 pFormView->GetImpl()->saveMarkList();
2736 if (bDesign && m_xExternalViewController.is())
2737 CloseExternalFormViewer_Lock();
2739 pFormView->ChangeDesignMode(bDesign);
2741 // notify listeners
2742 FmDesignModeChangedHint aChangedHint( bDesign );
2743 m_pShell->Broadcast(aChangedHint);
2745 m_pShell->m_bDesignMode = bDesign;
2746 UpdateForms_Lock(false);
2748 m_pTextShell->designModeChanged();
2750 if (bDesign)
2752 SdrMarkList aList;
2754 // during changing the mark list, don't track the selected objects in the property browser
2755 SuspendPropertyTracking aSuspend( *this );
2756 // restore the marks
2757 pFormView->GetImpl()->restoreMarkList( aList );
2760 // synchronize with the restored mark list
2761 if ( aList.GetMarkCount() )
2762 SetSelection_Lock(aList);
2764 else
2766 // subscribe to the model of the view (so that I'm informed when someone deletes
2767 // during the alive mode controls that I had saved in the saveMarklist (60343)
2768 pFormView->GetImpl()->startMarkListWatching();
2771 m_pShell->UIFeatureChanged();
2773 // 67506 - 15.07.99 - FS
2774 if (bDesign && m_bHadPropertyBrowserInDesignMode)
2776 // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
2777 // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
2778 // That's why we use an asynchron execution on the dispatcher.
2779 // (And that's why this has to be done AFTER the UIFeatureChanged.)
2780 m_pShell->GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
2782 m_bChangingDesignMode = false;
2785 Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
2787 if (impl_checkDisposed_Lock())
2788 return nullptr;
2790 Reference< XControl > xControl;
2793 Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
2795 const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
2796 // ... that I can then search
2797 for (Reference< XControl > const & control : seqControls)
2799 xControl.set( control, UNO_SET_THROW );
2800 Reference< XControlModel > xCurrentModel( xControl->getModel() );
2801 if ( xCurrentModel == i_rxModel )
2802 break;
2803 xControl.clear();
2806 if ( !xControl.is() )
2808 // fallback (some controls might not have been created, yet, since they were never visible so far)
2809 Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
2810 const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
2811 ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
2813 const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
2814 ENSURE_OR_THROW( pSdrView, "no current view" );
2816 xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow->GetOutDev() ), UNO_SET_THROW );
2819 catch( const Exception& )
2821 DBG_UNHANDLED_EXCEPTION("svx");
2824 OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
2825 return xControl;
2828 // note: _out_rForms is a member so needs lock
2829 void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
2830 std::u16string_view _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
2834 Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
2835 if ( !xContainer.is() )
2836 return;
2838 sal_Int32 nCount( xContainer->getCount() );
2839 if ( nCount == 0 )
2840 return;
2842 OUString sCurrentFormName;
2843 OUStringBuffer aNextLevelPrefix;
2844 for ( sal_Int32 i=0; i<nCount; ++i )
2846 // is the current child a form?
2847 Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
2848 if ( !xCurrentAsForm.is() )
2849 continue;
2851 Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
2852 sCurrentFormName = xNamed->getName();
2854 // the name of the current form
2855 OUString sCompleteCurrentName( sCurrentFormName );
2856 if ( !_rCurrentLevelPrefix.empty() )
2858 sCompleteCurrentName += OUString::Concat(" (") + _rCurrentLevelPrefix + ")";
2861 // the prefix for the next level
2862 aNextLevelPrefix = _rCurrentLevelPrefix;
2863 if ( !_rCurrentLevelPrefix.empty() )
2864 aNextLevelPrefix.append( '/' );
2865 aNextLevelPrefix.append( sCurrentFormName );
2867 // remember both the form and its "display name"
2868 _out_rForms.push_back( xCurrentAsForm );
2869 _out_rNames.push_back( sCompleteCurrentName );
2871 // and descend
2872 impl_collectFormSearchContexts_nothrow_Lock(
2873 xCurrentAsForm, aNextLevelPrefix,
2874 _out_rForms, _out_rNames);
2875 aNextLevelPrefix.setLength(0);
2878 catch( const Exception& )
2880 DBG_UNHANDLED_EXCEPTION("svx");
2884 void FmXFormShell::startFiltering_Lock()
2886 if (impl_checkDisposed_Lock())
2887 return;
2889 // setting all forms in filter mode
2890 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2892 // if the active controller is our external one we have to use the trigger controller
2893 Reference< XControlContainer> xContainer;
2894 if (getActiveController_Lock() == m_xExternalViewController)
2896 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
2897 xContainer = m_xExtViewTriggerController->getContainer();
2899 else
2900 xContainer = getActiveController_Lock()->getContainer();
2902 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow( xContainer );
2903 if ( pAdapter.is() )
2905 const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
2906 for (const auto& rpController : rControllerList)
2908 Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
2909 if (xModeSelector.is())
2910 xModeSelector->setMode( "FilterMode" );
2914 m_bFilterMode = true;
2916 m_pShell->UIFeatureChanged();
2917 SfxViewFrame& rViewFrame = m_pShell->GetViewShell()->GetViewFrame();
2918 rViewFrame.GetBindings().InvalidateShell( *m_pShell );
2920 if ( rViewFrame.KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
2921 && !rViewFrame.HasChildWindow( SID_FM_FILTER_NAVIGATOR )
2924 rViewFrame.ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
2928 static void saveFilter(const Reference< runtime::XFormController >& _rxController)
2930 Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
2931 Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
2933 // call the subcontroller
2934 Reference< runtime::XFormController > xController;
2935 for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
2937 _rxController->getByIndex(i) >>= xController;
2938 saveFilter(xController);
2944 xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
2945 xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, Any( true ) );
2947 catch (const Exception& )
2949 DBG_UNHANDLED_EXCEPTION("svx");
2955 void FmXFormShell::stopFiltering_Lock(bool bSave)
2957 if (impl_checkDisposed_Lock())
2958 return;
2960 m_bFilterMode = false;
2962 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2964 // if the active controller is our external one we have to use the trigger controller
2965 Reference< XControlContainer> xContainer;
2966 if (getActiveController_Lock() == m_xExternalViewController)
2968 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
2969 xContainer = m_xExtViewTriggerController->getContainer();
2971 else
2972 xContainer = getActiveController_Lock()->getContainer();
2974 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow(xContainer);
2975 if ( pAdapter.is() )
2977 const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
2978 ::std::vector < OUString > aOriginalFilters;
2979 ::std::vector < bool > aOriginalApplyFlags;
2981 if (bSave)
2983 for (const auto& rpController : rControllerList)
2985 // remember the current filter settings in case we're going to reload the forms below (which may fail)
2988 Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
2989 aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
2990 aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
2992 catch(Exception&)
2994 OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
2995 // put dummies into the arrays so the they have the right size
2997 if (aOriginalFilters.size() == aOriginalApplyFlags.size())
2998 // the first getPropertyValue failed -> use two dummies
2999 aOriginalFilters.emplace_back( );
3000 aOriginalApplyFlags.push_back( false );
3002 saveFilter(rpController);
3005 for (const auto& rController : rControllerList)
3008 Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
3009 if (xModeSelector.is())
3010 xModeSelector->setMode( "DataMode" );
3012 if (bSave) // execute the filter
3014 const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
3015 for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
3016 j != rControllers.end(); ++j)
3018 Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
3019 if (!xReload.is())
3020 continue;
3021 Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
3025 xReload->reload();
3027 catch(Exception&)
3029 TOOLS_WARN_EXCEPTION("svx.form", "");
3032 if (!isRowSetAlive(xFormSet))
3033 { // something went wrong -> restore the original state
3034 OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
3035 bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
3038 xFormSet->setPropertyValue(FM_PROP_FILTER, Any(sOriginalFilter));
3039 xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, Any(bOriginalApplyFlag));
3040 xReload->reload();
3042 catch(const Exception&)
3044 DBG_UNHANDLED_EXCEPTION("svx");
3051 m_pShell->UIFeatureChanged();
3052 m_pShell->GetViewShell()->GetViewFrame().GetBindings().InvalidateShell(*m_pShell);
3056 void FmXFormShell::CreateExternalView_Lock()
3058 if (impl_checkDisposed_Lock())
3059 return;
3061 DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
3063 // the frame the external view is displayed in
3064 bool bAlreadyExistent = m_xExternalViewController.is();
3065 Reference< css::frame::XFrame> xExternalViewFrame;
3067 Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
3068 // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
3070 // _first_ check if we have any valid fields we can use for the grid view
3071 // FS - 21.10.99 - 69219
3073 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3074 bool bHaveUsableControls = false;
3075 for (;;)
3077 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3078 if (!xCurrentModelSet.is())
3079 break;
3080 // the FmXBoundFormFieldIterator only supplies controls with a valid control source
3081 // so we just have to check the field type
3082 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3083 switch (nClassId)
3085 case FormComponentType::IMAGECONTROL:
3086 case FormComponentType::CONTROL:
3087 continue;
3089 bHaveUsableControls = true;
3090 break;
3093 if (!bHaveUsableControls)
3095 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
3096 VclMessageType::Warning, VclButtonsType::Ok,
3097 SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
3098 xBox->run();
3099 return;
3103 // load the component for external form views
3104 if (!bAlreadyExistent)
3106 OUString sFrameName("_beamer");
3107 URL aWantToDispatch;
3108 aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
3110 Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
3111 Reference< css::frame::XDispatch> xDisp;
3112 if (xProv.is())
3113 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
3114 css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
3115 if (xDisp.is())
3117 xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
3120 // with this the component should be loaded, now search the frame where it resides in
3121 xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
3122 if (xExternalViewFrame.is())
3124 m_xExternalViewController = xExternalViewFrame->getController();
3125 if (m_xExternalViewController.is())
3126 m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
3129 else
3131 xExternalViewFrame = m_xExternalViewController->getFrame();
3132 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3134 // if we display the active form we interpret the slot as "remove it"
3135 Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
3136 if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
3138 if (m_xExternalViewController == getActiveController_Lock())
3140 Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
3141 ControllerFeatures aHelper( xAsFormController );
3142 (void)aHelper->commitCurrentControl();
3145 Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
3146 CloseExternalFormViewer_Lock();
3147 setActiveController_Lock(xNewController);
3148 return;
3151 URL aClearURL;
3152 aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
3154 Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
3155 if (xClear.is())
3156 xClear->dispatch(aClearURL, Sequence< PropertyValue>());
3159 // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
3160 // instance for which this "external view" was triggered
3162 // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
3163 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3165 if (m_xExternalViewController.is())
3167 DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
3168 // collect the dispatchers we will need
3169 URL aAddColumnURL;
3170 aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
3171 Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
3172 URL aAttachURL;
3173 aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
3174 Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
3176 if (xAddColumnDispatch.is() && xAttachDispatch.is())
3178 DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
3179 // first : dispatch the descriptions for the columns to add
3180 sal_Int16 nAddedColumns = 0;
3182 // for radio buttons we need some special structures
3183 typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
3184 typedef std::map< OUString, OUString > FmMapUString2UString;
3185 typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
3187 MapUString2UstringSeq aRadioValueLists;
3188 MapUString2UstringSeq aRadioListSources;
3189 FmMapUString2UString aRadioControlSources;
3190 FmMapUString2Int16 aRadioPositions;
3192 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3193 OUString sColumnType,aGroupName,sControlSource;
3194 Sequence< Property> aProps;
3195 for (;;)
3197 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3198 if (!xCurrentModelSet.is())
3199 break;
3200 OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
3201 // create a description of the column to be created
3202 // first : determine it's type
3204 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3205 switch (nClassId)
3207 case FormComponentType::RADIOBUTTON:
3209 // get the label of the button (this is the access key for our structures)
3210 aGroupName = getLabelName(xCurrentModelSet);
3212 // add the reference value of the radio button to the list source sequence
3213 Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
3214 sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
3215 aThisGroupLabels.realloc(nNewSizeL);
3216 aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
3218 // add the label to the value list sequence
3219 Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
3220 sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
3221 aThisGroupControlSources.realloc(nNewSizeC);
3222 aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
3224 // remember the controls source of the radio group
3225 sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
3226 if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
3227 aRadioControlSources[aGroupName] = sControlSource;
3228 #ifdef DBG_UTIL
3229 else
3230 DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
3231 "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
3232 // (radio buttons with the same name should have the same control source)
3233 #endif
3234 // remember the position within the columns
3235 if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
3236 aRadioPositions[aGroupName] = nAddedColumns;
3238 // any further handling is done below
3240 continue;
3242 case FormComponentType::IMAGECONTROL:
3243 case FormComponentType::CONTROL:
3244 // no grid columns for these types (though they have a control source)
3245 continue;
3246 case FormComponentType::CHECKBOX:
3247 sColumnType = FM_COL_CHECKBOX; break;
3248 case FormComponentType::LISTBOX:
3249 sColumnType = FM_COL_LISTBOX; break;
3250 case FormComponentType::COMBOBOX:
3251 sColumnType = FM_COL_COMBOBOX; break;
3252 case FormComponentType::DATEFIELD:
3253 sColumnType = FM_COL_DATEFIELD; break;
3254 case FormComponentType::TIMEFIELD:
3255 sColumnType = FM_COL_TIMEFIELD; break;
3256 case FormComponentType::NUMERICFIELD:
3257 sColumnType = FM_COL_NUMERICFIELD; break;
3258 case FormComponentType::CURRENCYFIELD:
3259 sColumnType = FM_COL_CURRENCYFIELD; break;
3260 case FormComponentType::PATTERNFIELD:
3261 sColumnType = FM_COL_PATTERNFIELD; break;
3263 case FormComponentType::TEXTFIELD:
3265 sColumnType = FM_COL_TEXTFIELD;
3266 // we know at least two different controls which are TextFields : the basic edit field and the formatted
3267 // field. we distinguish them by their service name
3268 Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
3269 if (xInfo.is())
3271 SdrObjKind nObjectType = getControlTypeByObject(xInfo);
3272 if (SdrObjKind::FormFormattedField == nObjectType)
3273 sColumnType = FM_COL_FORMATTEDFIELD;
3276 break;
3277 default:
3278 sColumnType = FM_COL_TEXTFIELD; break;
3281 const sal_Int16 nDispatchArgs = 3;
3282 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3283 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3285 // properties describing "meta data" about the column
3286 // the type
3287 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3288 pDispatchArgs->Value <<= sColumnType;
3289 ++pDispatchArgs;
3291 // the pos : append the col
3292 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3293 pDispatchArgs->Value <<= nAddedColumns;
3294 ++pDispatchArgs;
3296 // the properties to forward to the new column
3297 Sequence< PropertyValue> aColumnProps(1);
3298 PropertyValue* pColumnProps = aColumnProps.getArray();
3300 // the label
3301 pColumnProps->Name = FM_PROP_LABEL;
3302 pColumnProps->Value <<= getLabelName(xCurrentModelSet);
3303 ++pColumnProps;
3305 // for all other props : transfer them
3306 Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
3307 DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
3308 aProps = xControlModelInfo->getProperties();
3310 // realloc the control description sequence
3311 sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
3312 aColumnProps.realloc(nExistentDescs + aProps.getLength());
3313 pColumnProps = aColumnProps.getArray() + nExistentDescs;
3315 for (const Property& rProp : std::as_const(aProps))
3317 if (rProp.Name == FM_PROP_LABEL)
3318 // already set
3319 continue;
3320 if (rProp.Name == FM_PROP_DEFAULTCONTROL)
3321 // allow the column's own "default control"
3322 continue;
3323 if (rProp.Attributes & PropertyAttribute::READONLY)
3324 // assume that properties which are readonly for the control are ro for the column to be created, too
3325 continue;
3327 pColumnProps->Name = rProp.Name;
3328 pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
3329 ++pColumnProps;
3331 aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
3333 // columns props are a dispatch argument
3334 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3335 pDispatchArgs->Value <<= aColumnProps;
3336 ++pDispatchArgs;
3337 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3338 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3340 // dispatch the "add column"
3341 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3342 ++nAddedColumns;
3345 // now for the radio button handling
3346 sal_Int16 nOffset(0);
3347 // properties describing the "direct" column properties
3348 const sal_Int16 nListBoxDescription = 6;
3349 Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
3350 for (const auto& rCtrlSource : aRadioControlSources)
3352 PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
3353 // label
3354 pListBoxDescription->Name = FM_PROP_LABEL;
3355 pListBoxDescription->Value <<= rCtrlSource.first;
3356 ++pListBoxDescription;
3358 // control source
3359 pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
3360 pListBoxDescription->Value <<= rCtrlSource.second;
3361 ++pListBoxDescription;
3363 // bound column
3364 pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
3365 pListBoxDescription->Value <<= sal_Int16(1);
3366 ++pListBoxDescription;
3368 // content type
3369 pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
3370 pListBoxDescription->Value <<= ListSourceType_VALUELIST;
3371 ++pListBoxDescription;
3373 // list source
3374 MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
3375 DBG_ASSERT(aCurrentListSource != aRadioListSources.end(),
3376 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3377 pListBoxDescription->Name = FM_PROP_LISTSOURCE;
3378 pListBoxDescription->Value <<= (*aCurrentListSource).second;
3379 ++pListBoxDescription;
3381 // value list
3382 MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
3383 DBG_ASSERT(aCurrentValueList != aRadioValueLists.end(),
3384 "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 <<= OUString(FM_COL_LISTBOX);
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 DBG_ASSERT(aOffset != aRadioPositions.end(),
3407 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3408 sal_Int16 nPosition = (*aOffset).second;
3409 nPosition = nPosition + nOffset;
3410 // we already inserted nOffset additional columns...
3411 pDispatchArgs->Value <<= nPosition;
3412 ++pDispatchArgs;
3414 // the
3415 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3416 pDispatchArgs->Value <<= aListBoxDescription;
3417 ++pDispatchArgs;
3418 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3419 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3421 // dispatch the "add column"
3422 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3423 ++nAddedColumns;
3424 ++nOffset;
3428 DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
3429 // we should have checked if we have any usable controls (see above).
3431 // "load" the "form" of the external view
3432 PropertyValue aArg;
3433 aArg.Name = FMARG_ATTACHTO_MASTERFORM;
3434 Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
3435 aArg.Value <<= xForm;
3437 m_xExternalDisplayedForm = xForm;
3438 // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
3439 // which needs the m_xExternalDisplayedForm
3441 xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
3443 m_xExtViewTriggerController = xCurrentNavController;
3445 // we want to know modifications done in the external view
3446 // if the external controller is a XFormController we can use all our default handlings for it
3447 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
3448 OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
3449 if (xFormController.is())
3450 xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
3453 #ifdef DBG_UTIL
3454 else
3456 OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
3458 #endif
3459 InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
3463 void FmXFormShell::implAdjustConfigCache_Lock()
3465 const bool bFuzzing(utl::ConfigManager::IsFuzzing());
3466 if (bFuzzing)
3467 return;
3469 // get (cache) the wizard usage flag
3470 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3471 Sequence< Any > aFlags = GetProperties(aNames);
3472 if (1 == aFlags.getLength())
3473 m_bUseWizards = ::cppu::any2bool(aFlags[0]);
3477 void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
3479 DBG_TESTSOLARMUTEX();
3480 if (impl_checkDisposed_Lock())
3481 return;
3483 for (const OUString& rName : _rPropertyNames)
3484 if (rName == "FormControlPilotsEnabled")
3486 implAdjustConfigCache_Lock();
3487 InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
3491 void FmXFormShell::ImplCommit()
3496 void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
3498 m_bUseWizards = _bUseThem;
3500 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3501 Sequence< Any > aValues{ Any(m_bUseWizards) };
3502 PutProperties(aNames, aValues);
3506 void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
3509 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3511 _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
3514 // if we have an async load operation pending for the 0-th page for this view,
3515 // we need to cancel this
3516 if (FmFormPage* pPage = _rCurrentView.GetCurPage())
3518 // move all events from our queue to a new one, omit the events for the deactivated
3519 // page
3520 ::std::queue< FmLoadAction > aNewEvents;
3521 while ( !m_aLoadingPages.empty() )
3523 FmLoadAction aAction = m_aLoadingPages.front();
3524 m_aLoadingPages.pop();
3525 if ( pPage != aAction.pPage )
3527 aNewEvents.push( aAction );
3529 else
3531 Application::RemoveUserEvent( aAction.nEventId );
3534 m_aLoadingPages = aNewEvents;
3536 // remove callbacks at the page
3537 pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
3539 UpdateForms_Lock(true);
3543 IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
3545 if (impl_checkDisposed_Lock())
3546 return;
3548 m_nActivationEvent = nullptr;
3549 SfxObjectShell* pDocument = m_pShell->GetObjectShell();
3551 if ( pDocument && !pDocument->HasName() )
3553 if (isEnhancedForm_Lock())
3555 // show the data navigator
3556 if ( !m_pShell->GetViewShell()->GetViewFrame().HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
3557 m_pShell->GetViewShell()->GetViewFrame().ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
3563 IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
3565 UpdateForms_Lock(true);
3569 void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
3571 FmFormPage* pPage = _rCurrentView.GetCurPage();
3573 // activate our view if we are activated ourself
3574 // FS - 30.06.99 - 67308
3575 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3577 // load forms for the page the current view belongs to
3578 if ( pPage )
3580 if ( !pPage->GetImpl().hasEverBeenActivated() )
3581 loadForms_Lock(pPage, LoadFormsFlags::Load
3582 | (_bSyncAction ? LoadFormsFlags::Sync
3583 : LoadFormsFlags::Async));
3584 pPage->GetImpl().setHasBeenActivated( );
3587 // first-time initializations for the views
3588 if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
3590 auto* pFormModel = dynamic_cast<FmFormModel*>(&_rCurrentView.GetModel());
3591 _rCurrentView.GetImpl()->onFirstViewActivation(pFormModel);
3592 _rCurrentView.GetImpl()->setHasBeenActivated();
3595 // activate the current view
3596 _rCurrentView.GetImpl()->Activate( _bSyncAction );
3599 // set callbacks at the page
3600 if ( pPage )
3602 pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
3605 UpdateForms_Lock(true);
3607 if ( m_bFirstActivation )
3609 m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
3610 m_bFirstActivation = false;
3613 // find a default "current form", if there is none, yet
3614 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
3615 impl_defaultCurrentForm_nothrow_Lock();
3619 void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
3621 if (impl_checkDisposed_Lock())
3622 return;
3624 if ( m_xCurrentForm.is() )
3625 // no action required
3626 return;
3628 FmFormView* pFormView = m_pShell->GetFormView();
3629 FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
3630 if ( !pPage )
3631 return;
3635 Reference< XIndexAccess > xForms = pPage->GetForms( false );
3636 if ( !xForms.is() || !xForms->hasElements() )
3637 return;
3639 Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
3640 impl_updateCurrentForm_Lock(xNewCurrentForm);
3642 catch( const Exception& )
3644 DBG_UNHANDLED_EXCEPTION("svx");
3649 void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
3651 if (!_rxModels.is())
3653 OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
3654 return;
3657 sal_Int32 nCount = _rxModels->getCount();
3658 Reference< XPropertySet > xCurrent;
3659 Reference< XPropertySetInfo > xCurrentInfo;
3660 Reference< XPropertySet > xBoundField;
3662 for (sal_Int32 i=0; i<nCount; ++i)
3664 _rxModels->getByIndex(i) >>= xCurrent;
3665 if (xCurrent.is())
3666 xCurrentInfo = xCurrent->getPropertySetInfo();
3667 else
3668 xCurrentInfo.clear();
3669 if (!xCurrentInfo.is())
3670 continue;
3672 if (xCurrentInfo->hasPropertyByName(FM_PROP_CLASSID))
3673 { // it's a control model
3675 // check if this control is bound to a living database field
3676 if (xCurrentInfo->hasPropertyByName(FM_PROP_BOUNDFIELD))
3677 xCurrent->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xBoundField;
3678 else
3679 xBoundField.clear();
3681 // reset only if it's *not* bound
3682 bool bReset = !xBoundField.is();
3684 // and additionally, check if it has an external value binding
3685 Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
3686 if ( xBindable.is() && xBindable->getValueBinding().is() )
3687 bReset = false;
3689 if ( bReset )
3691 Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
3692 if ( xControlReset.is() )
3693 xControlReset->reset();
3696 else
3698 Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
3699 if (xContainer.is())
3700 smartControlReset(xContainer);
3706 IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
3708 FmLoadAction aAction = m_aLoadingPages.front();
3709 m_aLoadingPages.pop();
3711 loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
3715 namespace
3717 bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
3719 // determines whether a form should be loaded or not
3720 // if there is no datasource or connection there is no reason to load a form
3721 Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
3722 if ( !xSet.is() )
3723 return false;
3726 Reference< XConnection > xConn;
3727 if ( isEmbeddedInDatabase( _rxLoadable, xConn ) )
3728 return true;
3730 // is there already an active connection
3731 xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
3732 if ( xConn.is() )
3733 return true;
3735 OUString sPropertyValue;
3736 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
3737 if ( !sPropertyValue.isEmpty() )
3738 return true;
3740 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
3741 if ( !sPropertyValue.isEmpty() )
3742 return true;
3744 catch(const Exception&)
3746 DBG_UNHANDLED_EXCEPTION("svx");
3748 return false;
3753 void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
3755 DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
3756 "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
3758 if ( _nBehaviour & LoadFormsFlags::Async )
3760 m_aLoadingPages.push( FmLoadAction(
3761 _pPage,
3762 _nBehaviour,
3763 Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
3764 ) );
3765 return;
3768 DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
3769 if ( !_pPage )
3770 return;
3772 // lock the undo env so the forms can change non-transient properties while loading
3773 // (without this my doc's modified flag would be set)
3774 FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
3775 rFmFormModel.GetUndoEnv().Lock();
3777 // load all forms
3778 Reference< XIndexAccess > xForms = _pPage->GetForms( false );
3780 if ( xForms.is() )
3782 Reference< XLoadable > xForm;
3783 for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
3785 xForms->getByIndex( j ) >>= xForm;
3786 bool bFormWasLoaded = false;
3787 // a database form must be loaded for
3790 if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
3792 if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
3793 xForm->load();
3795 else
3797 if ( xForm->isLoaded() )
3799 bFormWasLoaded = true;
3800 xForm->unload();
3804 catch( const Exception& )
3806 DBG_UNHANDLED_EXCEPTION("svx");
3809 // reset the form if it was loaded
3810 if ( bFormWasLoaded )
3812 Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
3813 DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
3814 if ( xContainer.is() )
3815 smartControlReset( xContainer );
3820 // unlock the environment
3821 rFmFormModel.GetUndoEnv().UnLock();
3825 void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
3827 DBG_TESTSOLARMUTEX();
3828 m_pTextShell->ExecuteTextAttribute( _rReq );
3832 void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
3834 DBG_TESTSOLARMUTEX();
3835 m_pTextShell->GetTextAttributeState( _rSet );
3839 bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
3841 DBG_TESTSOLARMUTEX();
3842 return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
3846 void FmXFormShell::ForgetActiveControl_Lock()
3848 DBG_TESTSOLARMUTEX();
3849 m_pTextShell->ForgetActiveControl();
3853 void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
3855 DBG_TESTSOLARMUTEX();
3856 m_pTextShell->SetControlActivationHandler( _rHdl );
3859 void FmXFormShell::handleShowPropertiesRequest_Lock()
3861 if (onlyControlsAreMarked_Lock())
3862 ShowSelectionProperties_Lock( true );
3866 void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
3868 // catch simple double clicks
3869 if (_rViewEvent.mnMouseClicks == 2 && _rViewEvent.mnMouseCode == MOUSE_LEFT)
3871 if ( _rViewEvent.meHit == SdrHitKind::MarkedObject )
3873 if (onlyControlsAreMarked_Lock())
3874 ShowSelectionProperties_Lock( true );
3880 bool FmXFormShell::HasControlFocus_Lock() const
3882 bool bHasControlFocus = false;
3886 Reference<runtime::XFormController> xController(getActiveController_Lock());
3887 Reference< XControl > xCurrentControl;
3888 if ( xController.is() )
3889 xCurrentControl.set( xController->getCurrentControl() );
3890 if ( xCurrentControl.is() )
3892 Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
3893 bHasControlFocus = xPeerWindow->hasFocus();
3896 catch( const Exception& )
3898 DBG_UNHANDLED_EXCEPTION("svx");
3901 return bHasControlFocus;
3905 SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
3906 :IndexAccessIterator(xStartingPoint)
3911 bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
3913 // if the thing has a ControlSource and a BoundField property
3914 Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
3915 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
3917 // and the BoundField is valid
3918 Reference< XPropertySet> xField;
3919 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3920 if (xField.is())
3922 // we take it
3923 m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
3924 return true;
3928 // if it is a grid control
3929 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
3931 Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
3932 if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
3934 m_sCurrentValue.clear();
3935 return true;
3939 return false;
3943 bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
3945 return true;
3948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */