Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / svx / source / form / fmshimp.cxx
blob04390bde7063976f3c99f03be37a1d06b271ba1d
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 <vcl/settings.hxx>
96 #include <vcl/svapp.hxx>
97 #include <vcl/weld.hxx>
98 #include <vcl/window.hxx>
100 #include <algorithm>
101 #include <map>
102 #include <memory>
103 #include <string_view>
104 #include <vector>
106 // is used for Invalidate -> maintain it as well
107 const sal_uInt16 DatabaseSlotMap[] =
109 SID_FM_RECORD_FIRST,
110 SID_FM_RECORD_NEXT,
111 SID_FM_RECORD_PREV,
112 SID_FM_RECORD_LAST,
113 SID_FM_RECORD_NEW,
114 SID_FM_RECORD_DELETE,
115 SID_FM_RECORD_ABSOLUTE,
116 SID_FM_RECORD_TOTAL,
117 SID_FM_RECORD_SAVE,
118 SID_FM_RECORD_UNDO,
119 SID_FM_REMOVE_FILTER_SORT,
120 SID_FM_SORTUP,
121 SID_FM_SORTDOWN,
122 SID_FM_ORDERCRIT,
123 SID_FM_AUTOFILTER,
124 SID_FM_FORM_FILTERED,
125 SID_FM_REFRESH,
126 SID_FM_REFRESH_FORM_CONTROL,
127 SID_FM_SEARCH,
128 SID_FM_FILTER_START,
129 SID_FM_VIEW_AS_GRID,
133 // is used for Invalidate -> maintain it as well
134 // sort ascending !!!!!!
135 const sal_Int16 DlgSlotMap[] = // slots of the controller
137 SID_FM_CTL_PROPERTIES,
138 SID_FM_PROPERTIES,
139 SID_FM_TAB_DIALOG,
140 SID_FM_ADD_FIELD,
141 SID_FM_SHOW_FMEXPLORER,
142 SID_FM_FIELDS_CONTROL,
143 SID_FM_SHOW_PROPERTIES,
144 SID_FM_PROPERTY_CONTROL,
145 SID_FM_FMEXPLORER_CONTROL,
146 SID_FM_SHOW_DATANAVIGATOR,
147 SID_FM_DATANAVIGATOR_CONTROL,
151 const sal_Int16 SelObjectSlotMap[] = // slots depending on the SelObject
153 SID_FM_CONVERTTO_EDIT,
154 SID_FM_CONVERTTO_BUTTON,
155 SID_FM_CONVERTTO_FIXEDTEXT,
156 SID_FM_CONVERTTO_LISTBOX,
157 SID_FM_CONVERTTO_CHECKBOX,
158 SID_FM_CONVERTTO_RADIOBUTTON,
159 SID_FM_CONVERTTO_GROUPBOX,
160 SID_FM_CONVERTTO_COMBOBOX,
161 SID_FM_CONVERTTO_IMAGEBUTTON,
162 SID_FM_CONVERTTO_FILECONTROL,
163 SID_FM_CONVERTTO_DATE,
164 SID_FM_CONVERTTO_TIME,
165 SID_FM_CONVERTTO_NUMERIC,
166 SID_FM_CONVERTTO_CURRENCY,
167 SID_FM_CONVERTTO_PATTERN,
168 SID_FM_CONVERTTO_IMAGECONTROL,
169 SID_FM_CONVERTTO_FORMATTED,
170 SID_FM_CONVERTTO_SCROLLBAR,
171 SID_FM_CONVERTTO_SPINBUTTON,
172 SID_FM_CONVERTTO_NAVIGATIONBAR,
174 SID_FM_FMEXPLORER_CONTROL,
175 SID_FM_DATANAVIGATOR_CONTROL,
180 // the following arrays must be consistent, i.e., corresponding entries should
181 // be at the same relative position within their respective arrays
182 static const char* aConvertSlots[] =
184 "ConvertToEdit",
185 "ConvertToButton",
186 "ConvertToFixed",
187 "ConvertToList",
188 "ConvertToCheckBox",
189 "ConvertToRadio",
190 "ConvertToGroup",
191 "ConvertToCombo",
192 "ConvertToImageBtn",
193 "ConvertToFileControl",
194 "ConvertToDate",
195 "ConvertToTime",
196 "ConvertToNumeric",
197 "ConvertToCurrency",
198 "ConvertToPattern",
199 "ConvertToImageControl",
200 "ConvertToFormatted",
201 "ConvertToScrollBar",
202 "ConvertToSpinButton",
203 "ConvertToNavigationBar"
206 constexpr rtl::OUStringConstExpr aImgIds[] =
208 RID_SVXBMP_EDITBOX,
209 RID_SVXBMP_BUTTON,
210 RID_SVXBMP_FIXEDTEXT,
211 RID_SVXBMP_LISTBOX,
212 RID_SVXBMP_CHECKBOX,
213 RID_SVXBMP_RADIOBUTTON,
214 RID_SVXBMP_GROUPBOX,
215 RID_SVXBMP_COMBOBOX,
216 RID_SVXBMP_IMAGEBUTTON,
217 RID_SVXBMP_FILECONTROL,
218 RID_SVXBMP_DATEFIELD,
219 RID_SVXBMP_TIMEFIELD,
220 RID_SVXBMP_NUMERICFIELD,
221 RID_SVXBMP_CURRENCYFIELD,
222 RID_SVXBMP_PATTERNFIELD,
223 RID_SVXBMP_IMAGECONTROL,
224 RID_SVXBMP_FORMATTEDFIELD,
225 RID_SVXBMP_SCROLLBAR,
226 RID_SVXBMP_SPINBUTTON,
227 RID_SVXBMP_NAVIGATIONBAR
230 const SdrObjKind nObjectTypes[] =
232 SdrObjKind::FormEdit,
233 SdrObjKind::FormButton,
234 SdrObjKind::FormFixedText,
235 SdrObjKind::FormListbox,
236 SdrObjKind::FormCheckbox,
237 SdrObjKind::FormRadioButton,
238 SdrObjKind::FormGroupBox,
239 SdrObjKind::FormCombobox,
240 SdrObjKind::FormImageButton,
241 SdrObjKind::FormFileControl,
242 SdrObjKind::FormDateField,
243 SdrObjKind::FormTimeField,
244 SdrObjKind::FormNumericField,
245 SdrObjKind::FormCurrencyField,
246 SdrObjKind::FormPatternField,
247 SdrObjKind::FormImageControl,
248 SdrObjKind::FormFormattedField,
249 SdrObjKind::FormScrollbar,
250 SdrObjKind::FormSpinButton,
251 SdrObjKind::FormNavigationBar
254 using namespace ::com::sun::star;
255 using namespace ::com::sun::star::ui;
256 using namespace ::com::sun::star::uno;
257 using namespace ::com::sun::star::sdb;
258 using namespace ::com::sun::star::sdbc;
259 using namespace ::com::sun::star::sdbcx;
260 using namespace ::com::sun::star::beans;
261 using namespace ::com::sun::star::container;
262 using namespace ::com::sun::star::form;
263 using namespace ::com::sun::star::form::binding;
264 using namespace ::com::sun::star::form::runtime;
265 using namespace ::com::sun::star::awt;
266 using namespace ::com::sun::star::view;
267 using namespace ::com::sun::star::util;
268 using namespace ::com::sun::star::script;
269 using namespace ::svxform;
270 using namespace ::svx;
271 using namespace ::dbtools;
274 //= helper
276 namespace
279 void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag& /* [out] */ _rInterfaces )
281 _rInterfaces.clear();
283 const size_t nMarkCount = _rMarkList.GetMarkCount();
284 for ( size_t i = 0; i < nMarkCount; ++i)
286 SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
288 std::unique_ptr<SdrObjListIter> pGroupIterator;
289 if ( pCurrent->IsGroupObject() )
291 pGroupIterator.reset(new SdrObjListIter( pCurrent->GetSubList() ));
292 pCurrent = pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
295 while ( pCurrent )
297 FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
298 // note this will de-reference virtual objects, if necessary/possible
299 if ( pAsFormObject )
301 Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
302 // the UNO_QUERY is important for normalization
303 if ( xControlModel.is() )
304 _rInterfaces.insert( xControlModel );
307 // next element
308 pCurrent = pGroupIterator && pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
314 sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
318 if (rColumns.is())
320 // loop through all columns
321 sal_Int32 i;
322 Reference< XPropertySet> xCur;
323 for (i=0; i<rColumns->getCount(); ++i)
325 rColumns->getByIndex(i) >>= xCur;
326 if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
328 // for every visible col : if nViewPos is greater zero, decrement it, else we
329 // have found the model position
330 if (!nViewPos)
331 break;
332 else
333 --nViewPos;
336 if (i<rColumns->getCount())
337 return i;
340 catch(const Exception&)
342 DBG_UNHANDLED_EXCEPTION("svx");
344 return -1;
348 void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
349 const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
351 // first check if we have a XEventAttacherManager for the model
352 Reference< XChild> xModelChild(xModel, UNO_QUERY);
353 if (!xModelChild.is())
354 return; // nothing to do
356 Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
357 if (!xEventManager.is())
358 return; // nothing to do
360 if (!rTransferIfAvailable.hasElements())
361 return; // nothing to do
363 // check for the index of the model within its parent
364 Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
365 if (!xParentIndex.is())
366 return; // nothing to do
367 sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
368 if (nIndex<0 || nIndex>=xParentIndex->getCount())
369 return; // nothing to do
371 // then we need information about the listeners supported by the control and the model
372 Sequence< Type> aModelListeners;
373 Sequence< Type> aControlListeners;
375 Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
377 if (xModel.is())
379 Any aModel(xModel);
380 aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
383 if (xControl.is())
385 Any aControl(xControl);
386 aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
389 sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
390 if (!nMaxNewLen)
391 return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
393 Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
394 ScriptEventDescriptor* pTransferable = aTransferable.getArray();
396 for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
398 // search the model/control idl classes for the event described by pCurrent
399 for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
401 for (const Type& rCurrentListener : *pCurrentArray)
403 OUString aListener = rCurrentListener.getTypeName();
404 if (!aListener.isEmpty())
405 aListener = aListener.copy(aListener.lastIndexOf('.')+1);
407 if (aListener == rCurrent.ListenerType)
408 // the current ScriptEventDescriptor doesn't match the current listeners class
409 continue;
411 // now check the methods
412 Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
414 if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
416 // we can transfer the script event : the model (control) supports it
417 *pTransferable = rCurrent;
418 ++pTransferable;
419 break;
425 sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
426 aTransferable.realloc(nRealNewLen);
428 xEventManager->registerScriptEvents(nIndex, aTransferable);
432 OUString getServiceNameByControlType(SdrObjKind nType)
434 switch (nType)
436 case SdrObjKind::FormEdit : return FM_COMPONENT_TEXTFIELD;
437 case SdrObjKind::FormButton : return FM_COMPONENT_COMMANDBUTTON;
438 case SdrObjKind::FormFixedText : return FM_COMPONENT_FIXEDTEXT;
439 case SdrObjKind::FormListbox : return FM_COMPONENT_LISTBOX;
440 case SdrObjKind::FormCheckbox : return FM_COMPONENT_CHECKBOX;
441 case SdrObjKind::FormRadioButton : return FM_COMPONENT_RADIOBUTTON;
442 case SdrObjKind::FormGroupBox : return FM_COMPONENT_GROUPBOX;
443 case SdrObjKind::FormCombobox : return FM_COMPONENT_COMBOBOX;
444 case SdrObjKind::FormGrid : return FM_COMPONENT_GRIDCONTROL;
445 case SdrObjKind::FormImageButton : return FM_COMPONENT_IMAGEBUTTON;
446 case SdrObjKind::FormFileControl : return FM_COMPONENT_FILECONTROL;
447 case SdrObjKind::FormDateField : return FM_COMPONENT_DATEFIELD;
448 case SdrObjKind::FormTimeField : return FM_COMPONENT_TIMEFIELD;
449 case SdrObjKind::FormNumericField : return FM_COMPONENT_NUMERICFIELD;
450 case SdrObjKind::FormCurrencyField : return FM_COMPONENT_CURRENCYFIELD;
451 case SdrObjKind::FormPatternField : return FM_COMPONENT_PATTERNFIELD;
452 case SdrObjKind::FormHidden : return FM_COMPONENT_HIDDENCONTROL;
453 case SdrObjKind::FormImageControl : return FM_COMPONENT_IMAGECONTROL;
454 case SdrObjKind::FormFormattedField : return FM_COMPONENT_FORMATTEDFIELD;
455 case SdrObjKind::FormScrollbar : return FM_SUN_COMPONENT_SCROLLBAR;
456 case SdrObjKind::FormSpinButton : return FM_SUN_COMPONENT_SPINBUTTON;
457 case SdrObjKind::FormNavigationBar : return FM_SUN_COMPONENT_NAVIGATIONBAR;
458 default:;
460 return OUString();
466 // check if the control has one of the interfaces we can use for searching
467 // *_pCurrentText will be filled with the current text of the control (as used when searching this control)
468 bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
469 OUString* _pCurrentText )
471 if ( !_rxControl.is() )
472 return false;
474 Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
475 if ( xAsText.is() )
477 if ( _pCurrentText )
478 *_pCurrentText = xAsText->getText();
479 return true;
482 Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
483 if ( xListBox.is() )
485 if ( _pCurrentText )
486 *_pCurrentText = xListBox->getSelectedItem();
487 return true;
490 Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
491 if ( xCheckBox.is() )
493 if ( _pCurrentText )
495 switch ( static_cast<::TriState>(xCheckBox->getState()) )
497 case TRISTATE_FALSE: *_pCurrentText = "0"; break;
498 case TRISTATE_TRUE: *_pCurrentText = "1"; break;
499 default: _pCurrentText->clear(); break;
502 return true;
505 return false;
509 bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
511 if (_rContainer == m_xStartingPoint)
512 // would be quite stupid to step over the root...
513 return true;
515 return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
519 bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
521 if (!_rElement.is())
522 // NULL element
523 return false;
525 if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
526 // a forms or a grid
527 return false;
529 Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
530 if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
531 // no "BoundField" property
532 return false;
534 Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
535 if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
536 // void or invalid property value
537 return false;
539 return aVal.hasValue();
543 static bool isControlList(const SdrMarkList& rMarkList)
545 // the list contains only controls and at least one control
546 const size_t nMarkCount = rMarkList.GetMarkCount();
547 bool bControlList = nMarkCount != 0;
549 bool bHadAnyLeafs = false;
551 for (size_t i = 0; i < nMarkCount && bControlList; ++i)
553 SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
554 E3dObject* pAs3DObject = DynCastE3dObject( pObj);
555 // E3dObject's do not contain any 2D-objects (by definition)
556 // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
557 // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
558 // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
559 // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
560 // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
561 // And this would be wrong :)
562 // 03.02.00 - 72529 - FS
563 if (!pAs3DObject)
565 if (pObj->IsGroupObject())
567 SdrObjListIter aIter(pObj->GetSubList());
568 while (aIter.IsMore() && bControlList)
570 bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
571 bHadAnyLeafs = true;
574 else
576 bHadAnyLeafs = true;
577 bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
582 return bControlList && bHadAnyLeafs;
586 static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
588 Reference< XForm > xForm( _rxElement, UNO_QUERY );
589 if ( xForm.is() )
590 return xForm;
592 Reference< XChild > xChild( _rxElement, UNO_QUERY );
593 if ( xChild.is() )
594 return GetForm( xChild->getParent() );
596 return Reference< XForm >();
599 FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
600 :FmXFormShell_BD_BASE( _rMutex )
604 FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
605 :FmXFormShell_BASE(m_aMutex)
606 ,FmXFormShell_CFGBASE("Office.Common/Misc", ConfigItemMode::NONE)
607 ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
608 ,m_eNavigate( NavigationBarMode_NONE )
609 ,m_nInvalidationEvent( nullptr )
610 ,m_nActivationEvent( nullptr )
611 ,m_pShell( &_rShell )
612 ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
613 ,m_aActiveControllerFeatures( this )
614 ,m_aNavControllerFeatures( this )
615 ,m_eDocumentType( eUnknownDocumentType )
616 ,m_nLockSlotInvalidation( 0 )
617 ,m_bHadPropertyBrowserInDesignMode( false )
618 ,m_bTrackProperties( true )
619 ,m_bUseWizards( true )
620 ,m_bDatabaseBar( false )
621 ,m_bInActivate( false )
622 ,m_bSetFocus( false )
623 ,m_bFilterMode( false )
624 ,m_bChangingDesignMode( false )
625 ,m_bPreparedClose( false )
626 ,m_bFirstActivation( true )
628 m_aMarkTimer.SetTimeout(100);
629 m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
631 m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
633 // to prevent deletion of this we acquire our refcounter once
634 osl_atomic_increment(&m_refCount);
636 // correct the refcounter
637 osl_atomic_decrement(&m_refCount);
639 // cache the current configuration settings we're interested in
640 implAdjustConfigCache_Lock();
641 // and register for changes on this settings
642 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
643 EnableNotification(aNames);
647 FmXFormShell::~FmXFormShell()
652 Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
654 Reference< css::frame::XModel > xModel;
656 // determine the type of document we live in
659 Reference< css::frame::XController > xController;
660 if ( m_xAttachedFrame.is() )
661 xController = m_xAttachedFrame->getController();
662 if ( xController.is() )
663 xModel = xController->getModel();
665 catch( const Exception& )
667 DBG_UNHANDLED_EXCEPTION("svx");
669 return xModel;
673 bool FmXFormShell::isEnhancedForm_Lock() const
675 return getDocumentType_Lock() == eEnhancedForm;
679 bool FmXFormShell::impl_checkDisposed_Lock() const
681 DBG_TESTSOLARMUTEX();
682 if ( !m_pShell )
684 OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
685 return true;
687 return false;
691 ::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
693 if ( m_eDocumentType != eUnknownDocumentType )
694 return m_eDocumentType;
696 // determine the type of document we live in
697 Reference<css::frame::XModel> xModel = getContextDocument_Lock();
698 if ( xModel.is() )
699 m_eDocumentType = DocumentClassification::classifyDocument( xModel );
700 else
702 OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
703 m_eDocumentType = eTextDocument;
704 // fallback, just to have a defined state
707 return m_eDocumentType;
711 bool FmXFormShell::IsReadonlyDoc_Lock() const
713 if (impl_checkDisposed_Lock())
714 return true;
716 FmFormModel* pModel = m_pShell->GetFormModel();
717 if ( pModel && pModel->GetObjectShell() )
718 return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
719 return true;
722 // EventListener
724 void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
726 SolarMutexGuard g;
728 if (m_xActiveController == e.Source)
730 // the controller will release, then release everything
731 stopListening_Lock();
732 m_xActiveForm = nullptr;
733 m_xActiveController = nullptr;
734 m_xNavigationController = nullptr;
736 m_aActiveControllerFeatures.dispose();
737 m_aNavControllerFeatures.dispose();
739 if ( m_pShell )
740 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
743 if (e.Source != m_xExternalViewController)
744 return;
746 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
747 OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
748 if (xFormController.is())
749 xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
751 if (m_xExternalViewController.is())
752 m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
754 m_xExternalViewController = nullptr;
755 m_xExternalDisplayedForm = nullptr;
756 m_xExtViewTriggerController = nullptr;
758 InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
762 void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
764 SolarMutexGuard g;
766 if (impl_checkDisposed_Lock())
767 return;
769 if (evt.PropertyName == FM_PROP_ROWCOUNT)
771 // The update following this forces a re-painting of the corresponding
772 // slots. But if I am not in the MainThread of the application (because,
773 // for example, a cursor is counting data sets at the moment and always
774 // gives me this PropertyChanges), this can clash with normal paints in
775 // the MainThread of the application. (Such paints happen, for example,
776 // if one simply places another application over the office and switches
777 // back again).
778 // Therefore the use of the SolarMutex, which safeguards that.
779 comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
780 if (rSolarSafety.tryToAcquire())
782 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
783 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(SID_FM_RECORD_TOTAL);
784 rSolarSafety.release();
786 else
788 // with the following the slot is invalidated asynchron
789 LockSlotInvalidation_Lock(true);
790 InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
791 LockSlotInvalidation_Lock(false);
795 // this may be called from a non-main-thread so invalidate the shell asynchronously
796 LockSlotInvalidation_Lock(true);
797 InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
798 LockSlotInvalidation_Lock(false);
802 void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
804 SolarMutexGuard g;
806 if (impl_checkDisposed_Lock())
807 return;
809 OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
811 if ( !(m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()) )
812 return;
814 // unfortunately, SFX requires sal_uInt16
815 ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );
817 // furthermore, SFX wants a terminating 0
818 aSlotIds.push_back( 0 );
820 // and, last but not least, SFX wants the ids to be sorted
821 ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
823 sal_uInt16 *pSlotIds = aSlotIds.data();
824 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( pSlotIds );
828 void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
830 SolarMutexGuard g;
832 if (impl_checkDisposed_Lock())
833 return;
835 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
836 m_pTextShell->formActivated( xController );
837 setActiveController_Lock(xController);
841 void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
843 SolarMutexGuard g;
845 if (impl_checkDisposed_Lock())
846 return;
848 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
849 m_pTextShell->formDeactivated( xController );
853 void FmXFormShell::disposing()
855 SolarMutexGuard g;
857 FmXFormShell_BASE::disposing();
859 if ( m_pShell && !m_pShell->IsDesignMode() )
860 setActiveController_Lock(nullptr, true);
861 // do NOT save the content of the old form (the second parameter tells this)
862 // if we're here, then we expect that PrepareClose has been called, and thus the user
863 // got a chance to commit or reject any changes. So in case we're here and there
864 // are still uncommitted changes, the user explicitly wanted this.
866 m_pTextShell->dispose();
868 m_xAttachedFrame = nullptr;
870 CloseExternalFormViewer_Lock();
872 while ( !m_aLoadingPages.empty() )
874 Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
875 m_aLoadingPages.pop();
879 if (m_nInvalidationEvent)
881 Application::RemoveUserEvent(m_nInvalidationEvent);
882 m_nInvalidationEvent = nullptr;
884 if ( m_nActivationEvent )
886 Application::RemoveUserEvent( m_nActivationEvent );
887 m_nActivationEvent = nullptr;
892 DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
893 // should have been deleted while being disposed
895 m_aMarkTimer.Stop();
898 DisableNotification();
900 RemoveElement_Lock(m_xForms);
901 m_xForms.clear();
903 impl_switchActiveControllerListening_Lock(false);
904 m_xActiveController = nullptr;
905 m_xActiveForm = nullptr;
907 m_pShell = nullptr;
908 m_xNavigationController = nullptr;
909 m_xCurrentForm = nullptr;
910 m_xLastGridFound = nullptr;
911 m_xAttachedFrame = nullptr;
912 m_xExternalViewController = nullptr;
913 m_xExtViewTriggerController = nullptr;
914 m_xExternalDisplayedForm = nullptr;
916 InterfaceBag().swap(m_aCurrentSelection);
918 m_aActiveControllerFeatures.dispose();
919 m_aNavControllerFeatures.dispose();
923 void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
925 if (impl_checkDisposed_Lock())
926 return;
928 if ( m_nLockSlotInvalidation )
930 OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
931 InvalidateSlot_Lock(_nId, false);
933 else
935 OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
936 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( _nId, true, true );
937 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);
960 void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
962 if (impl_checkDisposed_Lock())
963 return;
965 DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
967 if (bLock)
968 ++m_nLockSlotInvalidation;
969 else if (!--m_nLockSlotInvalidation)
971 // (asynchronously) invalidate everything accumulated during the locked phase
972 if (!m_nInvalidationEvent)
973 m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
978 IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
980 if (impl_checkDisposed_Lock())
981 return;
983 m_nInvalidationEvent = nullptr;
985 for (const auto& rInvalidSlot : m_arrInvalidSlots)
987 if (rInvalidSlot.id)
988 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
989 else
990 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
992 m_arrInvalidSlots.clear();
996 void FmXFormShell::ForceUpdateSelection_Lock()
998 if (impl_checkDisposed_Lock())
999 return;
1001 if (IsSelectionUpdatePending_Lock())
1003 m_aMarkTimer.Stop();
1005 // optionally turn off the invalidation of slots which is implicitly done by SetSelection
1006 LockSlotInvalidation_Lock(true);
1008 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
1010 LockSlotInvalidation_Lock(false);
1014 void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
1016 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1018 // the corresponding image at it
1019 rNewMenu.append(OUString::createFromAscii(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), aImgIds[i]);
1023 OString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
1025 static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
1027 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1029 if (nSlot == SelObjectSlotMap[i])
1030 return aConvertSlots[i];
1033 return OString();
1036 bool FmXFormShell::isControlConversionSlot(std::string_view rIdent)
1038 for (const auto& rConvertSlot : aConvertSlots)
1039 if (rIdent == rConvertSlot)
1040 return true;
1041 return false;
1044 void FmXFormShell::executeControlConversionSlot_Lock(std::string_view rIdent)
1046 OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
1047 InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
1048 if ( aSelectedElement == m_aCurrentSelection.end() )
1049 return;
1051 executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
1054 bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, std::string_view rIdent)
1056 if (impl_checkDisposed_Lock())
1057 return false;
1059 OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
1060 if ( !_rxObject.is() )
1061 return false;
1063 SdrPage* pPage = m_pShell->GetCurPage();
1064 FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1065 OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
1066 if ( !pFormPage )
1067 return false;
1069 OSL_ENSURE( isSolelySelected_Lock(_rxObject),
1070 "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
1072 for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
1074 if (rIdent == aConvertSlots[lookupSlot])
1076 Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
1078 FmFormObj* pFormObject = nullptr;
1079 SdrObjListIter aPageIter( pFormPage );
1080 while ( aPageIter.IsMore() )
1082 SdrObject* pCurrent = aPageIter.Next();
1083 pFormObject = FmFormObj::GetFormObject( pCurrent );
1084 if ( !pFormObject )
1085 continue;
1087 Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
1088 if ( xCurrentNormalized.get() == xNormalizedObject.get() )
1089 break;
1091 pFormObject = nullptr;
1094 if ( !pFormObject )
1095 return false;
1097 OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
1098 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1099 Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
1100 if (!xNewModel.is())
1101 return false;
1103 Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
1105 // transfer properties
1106 Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
1107 Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
1110 lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
1111 TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
1113 Sequence< css::script::ScriptEventDescriptor> aOldScripts;
1114 Reference< XChild> xChild(xOldModel, UNO_QUERY);
1115 if (xChild.is())
1117 Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
1119 // remember old script events
1120 Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
1121 if (xParent.is() && xEvManager.is())
1123 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1124 if (nIndex>=0 && nIndex<xParent->getCount())
1125 aOldScripts = xEvManager->getScriptEvents(nIndex);
1128 // replace the model within the parent container
1129 Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
1130 if (xIndexParent.is())
1132 // the form container works with FormComponents
1133 Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
1134 DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
1135 Any aNewModel(xComponent);
1139 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1140 if (nIndex>=0 && nIndex<xParent->getCount())
1141 xIndexParent->replaceByIndex(nIndex, aNewModel);
1142 else
1144 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1145 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1146 if (xNewComponent.is())
1147 xNewComponent->dispose();
1148 return false;
1151 catch(Exception&)
1153 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1154 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1155 if (xNewComponent.is())
1156 xNewComponent->dispose();
1157 return false;
1163 // special handling for the LabelControl-property : can only be set when the model is placed
1164 // within the forms hierarchy
1165 if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
1169 xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
1171 catch(Exception&)
1177 // set new model
1178 pFormObject->SetChanged();
1179 pFormObject->SetUnoControlModel(xNewModel);
1181 // transfer script events
1182 // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
1183 if (aOldScripts.hasElements())
1185 // find the control for the model
1186 Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
1188 const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
1190 Reference< XControl> xControl;
1191 auto pControl = std::find_if(aControls.begin(), aControls.end(),
1192 [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
1193 if (pControl != aControls.end())
1194 xControl = *pControl;
1195 TransferEventScripts(xNewModel, xControl, aOldScripts);
1198 // transfer value bindings, if possible
1200 Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
1201 Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
1202 if ( xOldBindable.is() )
1206 if ( xNewBindable.is() )
1207 xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
1208 xOldBindable->setValueBinding( nullptr );
1210 catch(const Exception&)
1212 DBG_UNHANDLED_EXCEPTION("svx");
1216 // same for list entry sources
1218 Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
1219 Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
1220 if ( xOldSink.is() )
1224 if ( xNewSink.is() )
1225 xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
1226 xOldSink->setListEntrySource( nullptr );
1228 catch(const Exception&)
1230 DBG_UNHANDLED_EXCEPTION("svx");
1235 // create an undo action
1236 FmFormModel* pModel = m_pShell->GetFormModel();
1237 DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
1238 if (pModel && pModel->IsUndoEnabled() )
1240 pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
1242 else
1244 FmUndoModelReplaceAction::DisposeElement( xOldModel );
1247 return true;
1250 return false;
1253 bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::string_view rIdent)
1255 if ( m_aCurrentSelection.empty() )
1256 return false;
1258 InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
1259 Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
1260 if ( !xElementInfo.is() )
1261 // no service info -> cannot determine this
1262 return false;
1264 if ( ++aCheck != m_aCurrentSelection.end() )
1265 // more than one element
1266 return false;
1268 if ( Reference< XForm >::query( xElementInfo ).is() )
1269 // it's a form
1270 return false;
1272 SdrObjKind nObjectType = getControlTypeByObject( xElementInfo );
1274 if ( ( SdrObjKind::FormHidden == nObjectType )
1275 || ( SdrObjKind::FormControl == nObjectType )
1276 || ( SdrObjKind::FormGrid == nObjectType )
1278 return false; // those types cannot be converted
1280 DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
1281 "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
1283 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1284 if (rIdent == aConvertSlots[i])
1285 return nObjectTypes[i] != nObjectType;
1287 return true; // all other slots: assume "yes"
1290 void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu)
1292 for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
1294 // the context is already of a type that corresponds to the entry -> disable
1295 OString sIdent(aConvertSlots[i]);
1296 rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
1300 void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
1302 if (impl_checkDisposed_Lock())
1303 return;
1305 Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
1306 if (!xControlModels.is())
1307 return;
1309 for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
1311 Reference< XPropertySet> xModelSet;
1312 xControlModels->getByIndex(i) >>= xModelSet;
1313 if (!xModelSet.is())
1314 continue;
1316 if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
1317 continue;
1318 sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
1319 if (FormComponentType::GRIDCONTROL != nClassId)
1320 continue;
1322 if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
1323 continue;
1325 switch (nSync)
1327 case LoopGridsSync::DISABLE_SYNC:
1329 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
1331 break;
1332 case LoopGridsSync::FORCE_SYNC:
1334 Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
1335 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1336 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
1338 break;
1339 case LoopGridsSync::ENABLE_SYNC:
1341 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1343 break;
1346 if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
1348 xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
1349 Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
1350 if (xModelPropState.is())
1351 xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
1352 else
1353 xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
1359 Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
1361 if (impl_checkDisposed_Lock())
1362 return nullptr;
1364 SdrPageView* pPageView = nullptr;
1365 if ( m_pShell && m_pShell->GetFormView() )
1366 pPageView = m_pShell->GetFormView()->GetSdrPageView();
1368 Reference< XControlContainer> xControlContainer;
1369 if ( pPageView )
1370 xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
1372 return xControlContainer;
1376 void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
1378 if (impl_checkDisposed_Lock())
1379 return;
1381 OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
1382 if ( !_rxForForm.is() )
1383 return;
1387 Reference< XWindow > xParentWindow;
1388 if ( m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame() )
1389 xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame()->GetWindow() );
1391 Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
1392 comphelper::getProcessComponentContext(),
1393 _rxForForm, getControlContainerForView_Lock(), xParentWindow
1396 xDialog->execute();
1398 catch( const Exception& )
1400 TOOLS_WARN_EXCEPTION( "svx", "FmXFormShell::ExecuteTabOrderDialog" );
1405 void FmXFormShell::ExecuteSearch_Lock()
1407 if (impl_checkDisposed_Lock())
1408 return;
1410 // a collection of all (logical) forms
1411 FmFormArray().swap(m_aSearchForms);
1412 ::std::vector< OUString > aContextNames;
1413 impl_collectFormSearchContexts_nothrow_Lock(
1414 m_pShell->GetCurPage()->GetForms(), u"",
1415 m_aSearchForms, aContextNames);
1417 if ( m_aSearchForms.size() != aContextNames.size() )
1419 SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
1420 return;
1423 // filter out the forms which do not contain valid controls at all
1425 FmFormArray aValidForms;
1426 ::std::vector< OUString > aValidContexts;
1427 FmFormArray::const_iterator form = m_aSearchForms.begin();
1428 ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
1429 for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
1431 FmSearchContext aTestContext;
1432 aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
1433 sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
1434 if ( nValidControls > 0 )
1436 aValidForms.push_back( *form );
1437 aValidContexts.push_back( *contextName );
1441 m_aSearchForms.swap( aValidForms );
1442 aContextNames.swap( aValidContexts );
1445 if (m_aSearchForms.empty() )
1447 // there are no controls that meet all the conditions for a search
1448 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
1449 VclMessageType::Warning, VclButtonsType::Ok,
1450 SvxResId(RID_STR_NODATACONTROLS)));
1451 xBox->run();
1452 return;
1455 // now I need another 'initial context'
1456 sal_Int16 nInitialContext = 0;
1457 Reference<XForm> xActiveForm(getActiveForm_Lock());
1458 for ( size_t i=0; i<m_aSearchForms.size(); ++i )
1460 if (m_aSearchForms.at(i) == xActiveForm)
1462 nInitialContext = static_cast<sal_Int16>(i);
1463 break;
1467 // If the dialog should initially offer the text of the active control,
1468 // this must have an XTextComponent interface. An addition, this makes
1469 // sense only if the current field is also bound to a table (or whatever) field.
1470 OUString strActiveField;
1471 OUString strInitialText;
1472 // ... this I get from my FormController
1473 DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
1474 Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
1475 if (xActiveControl.is())
1477 // the control can tell me its model ...
1478 Reference< XControlModel> xActiveModel( xActiveControl->getModel());
1479 DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
1481 // I ask the model for the ControlSource property ...
1482 Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
1483 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
1485 Reference< XPropertySet> xField;
1486 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1487 if (xField.is()) // (only when the thing is really bound)
1489 // and the control itself for a TextComponent interface (so that I can pick up the text there)
1490 Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
1491 if (xText.is())
1493 strActiveField = getLabelName(xProperties);
1494 strInitialText = xText->getText();
1498 else
1500 // the control itself has no ControlSource, but maybe it is a GridControl
1501 Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
1502 if (xGrid.is())
1504 // for strActiveField I need the ControlSource of the column,
1505 // for that the columns container, for that the GridPeer
1506 Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
1507 Reference< XIndexAccess> xColumns;
1508 if (xGridPeer.is())
1509 xColumns = xGridPeer->getColumns();
1511 sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
1512 sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
1513 Reference< XPropertySet> xCurrentCol;
1514 if(xColumns.is())
1515 xColumns->getByIndex(nModelCol) >>= xCurrentCol;
1516 if (xCurrentCol.is())
1517 strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
1519 // the text of the current column
1520 Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
1521 Reference< XInterface> xCurControl;
1522 xColControls->getByIndex(nViewCol) >>= xCurControl;
1523 OUString sInitialText;
1524 if (IsSearchableControl(xCurControl, &sInitialText))
1525 strInitialText = sInitialText;
1530 // taking care of possible GridControls that I know
1531 LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
1533 // Now I am ready for the dialogue.
1534 // When the potential deadlocks caused by the use of the solar mutex in
1535 // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
1536 // placed here, because the search in a separate thread is nevertheless
1537 // somewhat more fluid. Should be, however, somehow made dependent of the
1538 // underlying cursor. DAO for example is not thread-safe.
1539 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1540 ScopedVclPtr<AbstractFmSearchDialog> pDialog(
1541 pFact->CreateFmSearchDialog(
1542 m_pShell->GetViewShell()->GetViewFrame()->GetFrameWeld(),
1543 strInitialText, aContextNames, nInitialContext,
1544 LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
1545 pDialog->SetActiveField( strActiveField );
1546 pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
1547 pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
1548 pDialog->Execute();
1549 pDialog.disposeAndClear();
1551 // restore GridControls again
1552 LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
1554 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
1555 // because I marked controls in OnFoundData (if I was there)
1559 bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
1561 if (impl_checkDisposed_Lock())
1562 return false;
1564 if (m_pShell->IsDesignMode())
1565 // in the design mode (without active controls) the main document is to take care of it
1566 return false;
1568 Reference<XForm> xForm(getActiveForm_Lock());
1569 if (!xForm.is())
1570 // no current form (in particular no current control) -> the main document is to take care
1571 return false;
1573 Reference< XRowSet> xDB(xForm, UNO_QUERY);
1574 DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
1576 Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
1577 if (xSupplier.is())
1579 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1580 if (xSet.is())
1584 Any aVal( xSet->getPropertyValue("TwoDigitDateStart") );
1585 aVal >>= n;
1586 return true;
1588 catch(Exception&)
1594 return false;
1598 void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
1600 if (impl_checkDisposed_Lock())
1601 return;
1603 Reference<XForm> xActiveForm(getActiveForm_Lock());
1604 Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
1605 if ( xActiveRowSet.is() )
1607 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
1608 if (xSupplier.is())
1610 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1611 if (xSet.is())
1615 xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
1617 catch(Exception&)
1619 TOOLS_WARN_EXCEPTION("svx.form", "");
1623 return;
1627 // no active form found -> iterate through all current forms
1628 Reference< XIndexAccess> xCurrentForms( m_xForms);
1629 if (!xCurrentForms.is())
1630 { // in the alive mode, my forms are not set, but the ones on the page are
1631 if (m_pShell->GetCurPage())
1632 xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
1634 if (!xCurrentForms.is())
1635 return;
1637 ::comphelper::IndexAccessIterator aIter(xCurrentForms);
1638 Reference< XInterface> xCurrentElement( aIter.Next());
1639 while (xCurrentElement.is())
1641 // is the current element a DatabaseForm?
1642 Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
1643 if ( xElementAsRowSet.is() )
1645 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
1646 if (!xSupplier.is())
1647 continue;
1649 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1650 if (xSet.is())
1654 xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
1656 catch(Exception&)
1658 TOOLS_WARN_EXCEPTION("svx.form", "");
1663 xCurrentElement = aIter.Next();
1668 void FmXFormShell::CloseExternalFormViewer_Lock()
1670 if (impl_checkDisposed_Lock())
1671 return;
1673 if (!m_xExternalViewController.is())
1674 return;
1676 Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
1677 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
1678 if (!xCommLink.is())
1679 return;
1681 xExternalViewFrame->setComponent(nullptr,nullptr);
1682 ::comphelper::disposeComponent(xExternalViewFrame);
1683 m_xExternalViewController = nullptr;
1684 m_xExtViewTriggerController = nullptr;
1685 m_xExternalDisplayedForm = nullptr;
1689 Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _xForm) const
1691 if (impl_checkDisposed_Lock())
1692 return nullptr;
1694 Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1695 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1697 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1698 return m_xExternalDisplayedForm;
1700 return _xForm;
1704 Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
1706 if (impl_checkDisposed_Lock())
1707 return nullptr;
1709 Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1710 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1712 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1713 return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
1715 return _xForm;
1719 namespace
1721 bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
1723 return ( _nWhich == SID_FM_RECORD_FIRST )
1724 || ( _nWhich == SID_FM_RECORD_PREV )
1725 || ( _nWhich == SID_FM_RECORD_NEXT )
1726 || ( _nWhich == SID_FM_RECORD_LAST )
1727 || ( _nWhich == SID_FM_RECORD_NEW );
1732 bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState ) const
1734 const svx::ControllerFeatures& rController =
1735 lcl_isNavigationRelevant( _nSlot )
1736 ? getNavControllerFeatures_Lock()
1737 : getActiveControllerFeatures_Lock();
1739 if ( !_pCompleteState )
1740 return rController->isEnabled( _nSlot );
1742 rController->getState( _nSlot, *_pCompleteState );
1743 return _pCompleteState->Enabled;
1747 void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
1749 const svx::ControllerFeatures& rController =
1750 lcl_isNavigationRelevant( _nSlot )
1751 ? getNavControllerFeatures_Lock()
1752 : getActiveControllerFeatures_Lock();
1754 rController->execute( _nSlot );
1756 if ( _nSlot != SID_FM_RECORD_UNDO )
1757 return;
1759 // if we're doing an UNDO, *and* if the affected form is the form which we also display
1760 // as external view, then we need to reset the controls of the external form, too
1761 if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
1762 return;
1764 Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
1765 if ( !xContainer.is() )
1766 return;
1768 Reference< XReset > xReset;
1769 for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
1771 if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
1773 // no resets on sub forms
1774 Reference< XForm > xAsForm( xReset, UNO_QUERY );
1775 if ( !xAsForm.is() )
1776 xReset->reset();
1782 void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
1784 if ( !m_xActiveController.is() )
1785 return;
1787 if ( _bListen )
1788 m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
1789 else
1790 m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
1794 void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
1796 if (impl_checkDisposed_Lock())
1797 return;
1799 if (m_bChangingDesignMode)
1800 return;
1801 DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
1803 // if the routine has been called a second time,
1804 // the focus should no longer be transferred
1805 if (m_bInActivate)
1807 m_bSetFocus = xController != m_xActiveController;
1808 return;
1811 if (xController == m_xActiveController)
1812 return;
1814 // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
1815 Reference< XResultSet> xNavigationForm;
1816 if (m_xNavigationController.is())
1817 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1819 m_bInActivate = true;
1821 // check if the 2 controllers serve different forms
1822 Reference< XResultSet> xOldForm;
1823 if (m_xActiveController.is())
1824 xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
1825 Reference< XResultSet> xNewForm;
1826 if (xController.is())
1827 xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
1828 xOldForm = getInternalForm_Lock(xOldForm);
1829 xNewForm = getInternalForm_Lock(xNewForm);
1831 bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
1832 bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
1833 // we save the content of the old form if we move to a new form, and saving old content is allowed
1835 if ( m_xActiveController.is() && bNeedSave )
1837 // save content on change of the controller; a commit has already been executed
1838 if ( m_aActiveControllerFeatures->commitCurrentControl() )
1840 m_bSetFocus = true;
1841 if ( m_aActiveControllerFeatures->isModifiedRow() )
1843 bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
1844 bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
1845 if ( !bResult && m_bSetFocus )
1847 // if we couldn't save the current record, set the focus back to the
1848 // current control
1849 Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
1850 if ( xWindow.is() )
1851 xWindow->setFocus();
1852 m_bInActivate = false;
1853 return;
1855 else if ( bResult && bIsNew )
1857 Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
1858 if ( xCursor.is() )
1860 DO_SAFE( xCursor->last(); );
1867 stopListening_Lock();
1869 impl_switchActiveControllerListening_Lock(false);
1871 m_aActiveControllerFeatures.dispose();
1872 m_xActiveController = xController;
1873 if ( m_xActiveController.is() )
1874 m_aActiveControllerFeatures.assign( m_xActiveController );
1876 impl_switchActiveControllerListening_Lock(true);
1878 if ( m_xActiveController.is() )
1879 m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
1880 else
1881 m_xActiveForm = nullptr;
1883 startListening_Lock();
1885 // activate all dispatchers belonging to form of the new navigation controller
1886 xNavigationForm = nullptr;
1887 if (m_xNavigationController.is())
1888 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1890 m_bInActivate = false;
1892 m_pShell->UIFeatureChanged();
1893 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
1895 InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
1899 void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
1901 _rSelection = m_aCurrentSelection;
1905 bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
1907 m_aLastKnownMarkedControls.clear();
1909 if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
1910 collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
1912 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1916 bool FmXFormShell::selectLastMarkedControls_Lock()
1918 return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
1922 bool FmXFormShell::setCurrentSelection_Lock( InterfaceBag&& _rSelection )
1924 if (impl_checkDisposed_Lock())
1925 return false;
1927 DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
1929 if ( _rSelection.empty() && m_aCurrentSelection.empty() )
1930 // nothing to do
1931 return false;
1933 if ( _rSelection.size() == m_aCurrentSelection.size() )
1935 InterfaceBag::const_iterator aNew = _rSelection.begin();
1936 InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
1937 for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
1939 OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
1940 OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
1942 if ( aNew->get() != aOld->get() )
1943 break;
1946 if ( aNew == _rSelection.end() )
1947 // both bags equal
1948 return false;
1951 // the following is some strange code to ensure that when you have two grid controls in a document,
1952 // only one of them can have a selected column.
1953 // TODO: this should happen elsewhere, but not here - shouldn't it?
1954 if ( !m_aCurrentSelection.empty() )
1956 Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
1957 Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
1959 // is there nothing to be selected, or the parents differ, and the parent of the current object
1960 // is a selection supplier, then deselect
1961 if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
1963 Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
1964 if ( xSel.is() )
1965 xSel->select( Any() );
1969 m_aCurrentSelection = _rSelection;
1971 // determine the form which all the selected objects belong to, if any
1972 Reference< XForm > xNewCurrentForm;
1973 for (const auto& rpSelection : m_aCurrentSelection)
1975 Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
1976 OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
1978 if ( !xNewCurrentForm.is() )
1979 { // the first form we encountered
1980 xNewCurrentForm = xThisRoundsForm;
1982 else if ( xNewCurrentForm != xThisRoundsForm )
1983 { // different forms -> no "current form" at all
1984 xNewCurrentForm.clear();
1985 break;
1989 if ( !m_aCurrentSelection.empty() )
1990 impl_updateCurrentForm_Lock(xNewCurrentForm);
1992 // ensure some slots are updated
1993 for (sal_Int16 i : SelObjectSlotMap)
1994 InvalidateSlot_Lock(i, false);
1996 return true;
2000 bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
2002 return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
2006 void FmXFormShell::forgetCurrentForm_Lock()
2008 if ( !m_xCurrentForm.is() )
2009 return;
2011 // reset ...
2012 impl_updateCurrentForm_Lock(nullptr);
2014 // ... and try finding a new current form
2015 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
2016 impl_defaultCurrentForm_nothrow_Lock();
2020 void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
2022 if (impl_checkDisposed_Lock())
2023 return;
2025 m_xCurrentForm = _rxNewCurForm;
2027 // propagate to the FormPage(Impl)
2028 FmFormPage* pPage = m_pShell->GetCurPage();
2029 if ( pPage )
2030 pPage->GetImpl().setCurForm( m_xCurrentForm );
2032 // ensure the UI which depends on the current form is up-to-date
2033 for (sal_Int16 i : DlgSlotMap)
2034 InvalidateSlot_Lock(i, false);
2038 void FmXFormShell::startListening_Lock()
2040 if (impl_checkDisposed_Lock())
2041 return;
2043 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2044 if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
2046 Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
2047 if (xActiveFormSet.is())
2049 // if there is a data source, then build the listener
2050 // TODO: this is strange - shouldn't this depend on a isLoaded instead of
2051 // a "has command value"? Finally, the command value only means that it was
2052 // intended to be loaded, not that it actually *is* loaded
2053 OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
2054 if (!aSource.isEmpty())
2056 m_bDatabaseBar = true;
2058 xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2060 switch (m_eNavigate)
2062 case NavigationBarMode_PARENT:
2064 // search for the controller via which navigation is possible
2065 Reference< XChild> xChild = m_xActiveController;
2066 Reference< runtime::XFormController > xParent;
2067 while (xChild.is())
2069 xChild.set(xChild->getParent(), UNO_QUERY);
2070 xParent.set(xChild, UNO_QUERY);
2071 Reference< XPropertySet> xParentSet;
2072 if (xParent.is())
2073 xParentSet.set(xParent->getModel(), UNO_QUERY);
2074 if (xParentSet.is())
2076 xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2077 if (m_eNavigate == NavigationBarMode_CURRENT)
2078 break;
2081 m_xNavigationController = xParent;
2083 break;
2085 case NavigationBarMode_CURRENT:
2086 m_xNavigationController = m_xActiveController;
2087 break;
2089 default:
2090 m_xNavigationController = nullptr;
2091 m_bDatabaseBar = false;
2094 m_aNavControllerFeatures.dispose();
2095 if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
2096 m_aNavControllerFeatures.assign( m_xNavigationController );
2098 // because of RecordCount, listen at the controller which controls the navigation
2099 Reference< XPropertySet> xNavigationSet;
2100 if (m_xNavigationController.is())
2102 xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
2103 if (xNavigationSet.is())
2104 xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
2106 return;
2111 m_eNavigate = NavigationBarMode_NONE;
2112 m_bDatabaseBar = false;
2113 m_xNavigationController = nullptr;
2117 void FmXFormShell::stopListening_Lock()
2119 if (impl_checkDisposed_Lock())
2120 return;
2122 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2123 if ( xDatabaseForm.is() )
2125 if (m_xNavigationController.is())
2127 Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
2128 if (xSet.is())
2129 xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2134 m_bDatabaseBar = false;
2135 m_eNavigate = NavigationBarMode_NONE;
2136 m_xNavigationController = nullptr;
2140 void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
2142 if (impl_checkDisposed_Lock())
2143 return;
2145 // if the window is already visible, only update the state
2146 bool bHasChild = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_PROPERTIES );
2147 if ( bHasChild && bShow )
2148 UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
2150 // else toggle state
2151 else
2152 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2154 InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
2155 InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
2159 IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
2161 if (impl_checkDisposed_Lock())
2162 return;
2164 DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
2165 "FmXFormShell::OnFoundData : invalid context!");
2166 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2167 DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
2169 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2170 if (!xCursor.is())
2171 return; // what should I do there?
2173 // to the record
2176 xCursor->moveToBookmark(rfriWhere.aPosition);
2178 catch(const SQLException&)
2180 OSL_FAIL("Can position on bookmark!");
2183 LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
2185 // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
2186 SAL_WARN_IF(o3tl::make_unsigned(rfriWhere.nFieldPos) >=
2187 m_arrSearchedControls.size(),
2188 "svx.form", "FmXFormShell::OnFoundData : invalid index!");
2189 SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
2191 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2192 m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
2194 FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
2195 Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
2196 DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
2197 if ( !xControlModel.is() )
2198 return;
2200 // disable the permanent cursor for the last grid we found a record
2201 if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
2203 Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
2204 xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any( false ) );
2205 Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
2206 if (xOldSetState.is())
2207 xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
2208 else
2209 xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
2212 // if the field is in a GridControl, I have to additionally go into the corresponding column there
2213 sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
2214 if (nGridColumn != -1)
2215 { // unfortunately, I have to first get the control again
2216 Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
2217 Reference< XGrid> xGrid(xControl, UNO_QUERY);
2218 DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
2219 // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
2221 // enable a permanent cursor for the grid so we can see the found text
2222 Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
2223 DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
2224 xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, Any( true ) );
2225 xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, Any( COL_LIGHTRED ) );
2226 m_xLastGridFound = xControlModel;
2228 if ( xGrid.is() )
2229 xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
2232 // As the cursor has been repositioned, I have (in positioned) invalidated
2233 // my form bar slots. But that does not take effect here unfortunately, as
2234 // generally the (modal) search dialog is of course at the top ... So, force ...
2235 sal_uInt16 nPos = 0;
2236 while (DatabaseSlotMap[nPos])
2237 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(DatabaseSlotMap[nPos++]);
2238 // unfortunately the update goes against the invalidate with only individual slots
2242 IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
2244 if (impl_checkDisposed_Lock())
2245 return;
2247 DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
2248 "FmXFormShell::OnCanceledNotFound : invalid context!");
2249 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2250 DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
2252 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2253 if (!xCursor.is())
2254 return; // what should I do there?
2256 // to the record
2259 xCursor->moveToBookmark(rfriWhere.aPosition);
2261 catch(const SQLException&)
2263 OSL_FAIL("Can position on bookmark!");
2267 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2271 IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
2273 if (impl_checkDisposed_Lock())
2274 return 0;
2276 DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
2277 Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
2278 DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
2280 Reference< XResultSet> xIter(xForm, UNO_QUERY);
2281 DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
2284 // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
2285 OUString strFieldList, sFieldDisplayNames;
2286 m_arrSearchedControls.clear();
2287 m_arrRelativeGridColumn.clear();
2289 // small problem: To mark found fields, I need SdrObjects. To determine which controls
2290 // to include in the search, I need Controls (that is, XControl interfaces). So I have
2291 // to iterate over one of them and get the other in some way. Unfortunately, there is
2292 // no direct connection between the two worlds (except from a GetUnoControl to a
2293 // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
2294 // However I can get to the Model from the Control and also from the SdrObject, and in
2295 // this way the assignment SdrObject<->Control is possible with a double loop.
2296 // The alternative to this (ugly but certainly not entirely fixable) solution would be
2297 // to renounce the caching of the SdrObjects, which would lead to significant extra
2298 // work in OnFoundData (since there I'd have to get the SdrObject first thing every
2299 // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
2300 // do that here.
2302 Reference< XNameAccess> xValidFormFields;
2303 Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
2304 DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
2305 if (xSupplyCols.is())
2306 xValidFormFields = xSupplyCols->getColumns();
2307 DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
2309 // current Page/Controller
2310 FmFormPage* pCurrentPage = m_pShell->GetCurPage();
2311 assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
2312 // Search all SdrControls of this page...
2313 OUString sControlSource, aName;
2315 SdrObjListIter aPageIter( pCurrentPage );
2316 while ( aPageIter.IsMore() )
2318 SdrObject* pCurrent = aPageIter.Next();
2319 FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
2320 // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
2322 if ( !pFormObject )
2323 continue;
2325 // the current object's model, in different tastes
2326 Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
2327 Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
2328 DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
2329 if ( !xCurrentFormComponent.is() )
2330 continue;
2332 // does the component belong to the form which we're interested in?
2333 if ( xCurrentFormComponent->getParent() != xForm )
2334 continue;
2336 // ... ask for the ControlSource property
2337 SearchableControlIterator iter( xCurrentFormComponent );
2338 Reference< XControl> xControl;
2339 // the control that has model xControlModel
2340 // (the following while can be passed through several times, without the Control
2341 // being modified, so I don't have to search every time from scratch)
2343 Reference< XInterface > xSearchable( iter.Next() );
2344 while ( xSearchable.is() )
2346 sControlSource = iter.getCurrentValue();
2347 if ( sControlSource.isEmpty() )
2349 // the current element has no ControlSource, so it is a GridControl (that
2350 // is the only thing that still permits the SearchableControlIteratore)
2351 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2352 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2354 Reference< XGridPeer> xGridPeer;
2355 if ( xControl.is() )
2356 xGridPeer.set( xControl->getPeer(), UNO_QUERY );
2359 if (!xGridPeer.is())
2360 break;
2362 Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
2363 if (!xPeerContainer.is())
2364 break;
2366 Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
2367 DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
2368 // the case 'no columns' should be indicated with an empty container, I think ...
2369 DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
2371 Reference< XInterface> xCurrentColumn;
2372 for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
2374 xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
2375 if (!xCurrentColumn.is())
2376 continue;
2378 // can we use this column control for searching ?
2379 if (!IsSearchableControl(xCurrentColumn))
2380 continue;
2382 sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
2383 Reference< XPropertySet> xCurrentColModel;
2384 xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
2385 aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
2386 // the cursor has a field matching the control source ?
2387 if (xValidFormFields->hasByName(aName))
2389 strFieldList += aName + ";";
2391 sFieldDisplayNames +=
2392 ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
2393 ";";
2395 rfmscContextInfo.arrFields.push_back(xCurrentColumn);
2397 // and the SdrOject to the Field
2398 m_arrSearchedControls.push_back(pCurrent);
2399 // the number of the column
2400 m_arrRelativeGridColumn.push_back(nViewPos);
2403 } while (false);
2405 else
2407 if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
2409 // now I need the Control to SdrObject
2410 if (!xControl.is())
2412 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2413 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2416 if (IsSearchableControl(xControl))
2418 // all tests passed -> take along in the list
2419 strFieldList += sControlSource + ";";
2421 // the label which should appear for the control :
2422 sFieldDisplayNames +=
2423 getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
2424 ";";
2426 // mark the SdrObject (accelerates the treatment in OnFoundData)
2427 m_arrSearchedControls.push_back(pCurrent);
2429 // the number of the column (here a dummy, since it is only interesting for GridControls)
2430 m_arrRelativeGridColumn.push_back(-1);
2432 // and for the formatted search...
2433 rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
2438 xSearchable = iter.Next();
2442 strFieldList = comphelper::string::stripEnd(strFieldList, ';');
2443 sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
2445 if (rfmscContextInfo.arrFields.empty())
2447 rfmscContextInfo.arrFields.clear();
2448 rfmscContextInfo.xCursor = nullptr;
2449 rfmscContextInfo.strUsedFields.clear();
2450 return 0;
2453 rfmscContextInfo.xCursor = xIter;
2454 rfmscContextInfo.strUsedFields = strFieldList;
2455 rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
2457 // 66463 - 31.05.99 - FS
2458 // when the cursor is a non-STANDARD RecordMode, set it back
2459 Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
2460 Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
2461 if (xUpdateCursor.is() && xCursorSet.is())
2463 if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
2464 xUpdateCursor->moveToCurrentRow();
2465 else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
2466 xUpdateCursor->cancelRowUpdates();
2469 return rfmscContextInfo.arrFields.size();
2472 // XContainerListener
2474 void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
2476 SolarMutexGuard g;
2478 if (impl_checkDisposed_Lock())
2479 return;
2481 // new object to listen to
2482 Reference< XInterface> xTemp;
2483 evt.Element >>= xTemp;
2484 AddElement_Lock(xTemp);
2486 m_pShell->DetermineForms(true);
2490 void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
2492 SolarMutexGuard g;
2494 if (impl_checkDisposed_Lock() )
2495 return;
2497 Reference< XInterface> xTemp;
2498 evt.ReplacedElement >>= xTemp;
2499 RemoveElement_Lock(xTemp);
2500 evt.Element >>= xTemp;
2501 AddElement_Lock(xTemp);
2505 void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
2507 SolarMutexGuard g;
2509 if (impl_checkDisposed_Lock())
2510 return;
2512 Reference< XInterface> xTemp;
2513 evt.Element >>= xTemp;
2514 RemoveElement_Lock(xTemp);
2516 m_pShell->DetermineForms(true);
2520 void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
2522 if (impl_checkDisposed_Lock())
2523 return;
2525 Reference< XIndexAccess > xForms;
2527 FmFormPage* pPage = m_pShell->GetCurPage();
2528 if ( pPage && m_pShell->m_bDesignMode )
2529 xForms = pPage->GetForms( false );
2531 if ( m_xForms != xForms )
2533 RemoveElement_Lock( m_xForms );
2534 m_xForms = xForms;
2535 AddElement_Lock(m_xForms);
2538 SolarMutexGuard g;
2539 m_pShell->DetermineForms( _bInvalidate );
2543 void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
2545 if (impl_checkDisposed_Lock())
2546 return;
2547 impl_AddElement_nothrow(_xElement);
2550 void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
2552 // listen at the container
2553 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2554 if (xContainer.is())
2556 const sal_uInt32 nCount = xContainer->getCount();
2557 Reference< XInterface> xElement;
2558 for (sal_uInt32 i = 0; i < nCount; ++i)
2560 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2561 impl_AddElement_nothrow(xElement);
2564 const Reference< XContainer> xCont(Element, UNO_QUERY);
2565 if (xCont.is())
2566 xCont->addContainerListener(this);
2569 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2570 if (xSelSupplier.is())
2571 xSelSupplier->addSelectionChangeListener(this);
2575 void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
2577 if (impl_checkDisposed_Lock())
2578 return;
2579 impl_RemoveElement_nothrow_Lock(Element);
2582 void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
2584 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2585 if (xSelSupplier.is())
2586 xSelSupplier->removeSelectionChangeListener(this);
2588 // remove connection to children
2589 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2590 if (xContainer.is())
2592 const Reference< XContainer> xCont(Element, UNO_QUERY);
2593 if (xCont.is())
2594 xCont->removeContainerListener(this);
2596 const sal_uInt32 nCount = xContainer->getCount();
2597 Reference< XInterface> xElement;
2598 for (sal_uInt32 i = 0; i < nCount; i++)
2600 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2601 impl_RemoveElement_nothrow_Lock(xElement);
2605 auto wasSelectedPos = m_aCurrentSelection.find( Element );
2606 if ( wasSelectedPos != m_aCurrentSelection.end() )
2607 m_aCurrentSelection.erase( wasSelectedPos );
2611 void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
2613 SolarMutexGuard g;
2615 if (impl_checkDisposed_Lock())
2616 return;
2618 Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
2619 Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
2620 // a selection was removed, this can only be done by the shell
2621 if ( !xSelObj.is() )
2622 return;
2624 EnableTrackProperties_Lock(false);
2626 bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
2628 InterfaceBag aNewSelection;
2629 aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
2631 if (setCurrentSelection_Lock(std::move(aNewSelection)) && IsPropBrwOpen_Lock())
2632 ShowSelectionProperties_Lock(true);
2634 EnableTrackProperties_Lock(true);
2636 if ( bMarkChanged )
2637 m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
2641 IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
2643 if (impl_checkDisposed_Lock())
2644 return;
2646 if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
2647 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
2651 void FmXFormShell::SetSelectionDelayed_Lock()
2653 if (impl_checkDisposed_Lock())
2654 return;
2656 if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
2657 m_aMarkTimer.Start();
2661 void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
2663 if (impl_checkDisposed_Lock())
2664 return;
2666 DetermineSelection_Lock(rMarkList);
2667 m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
2671 void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
2673 if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
2674 ShowSelectionProperties_Lock(true);
2678 bool FmXFormShell::IsPropBrwOpen_Lock() const
2680 if (impl_checkDisposed_Lock())
2681 return false;
2683 return m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()
2684 && m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
2688 class FmXFormShell::SuspendPropertyTracking
2690 private:
2691 FmXFormShell& m_rShell;
2692 bool m_bEnabled;
2694 public:
2695 explicit SuspendPropertyTracking( FmXFormShell& _rShell )
2696 :m_rShell( _rShell )
2697 ,m_bEnabled( false )
2699 if (m_rShell.IsTrackPropertiesEnabled_Lock())
2701 m_rShell.EnableTrackProperties_Lock(false);
2702 m_bEnabled = true;
2706 ~SuspendPropertyTracking( )
2708 if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
2709 m_rShell.EnableTrackProperties_Lock(true);
2714 void FmXFormShell::SetDesignMode_Lock(bool bDesign)
2716 if (impl_checkDisposed_Lock())
2717 return;
2719 DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
2720 m_bChangingDesignMode = true;
2722 // 67506 - 15.07.99 - FS
2723 // if we're switching off the design mode we have to force the property browser to be closed
2724 // so it can commit it's changes _before_ we load the forms
2725 if (!bDesign)
2727 m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
2728 if (m_bHadPropertyBrowserInDesignMode)
2729 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2732 FmFormView* pFormView = m_pShell->GetFormView();
2733 if (bDesign)
2735 // we are currently filtering, so stop filtering
2736 if (m_bFilterMode)
2737 stopFiltering_Lock(false);
2739 // unsubscribe from the objects of my MarkList
2740 pFormView->GetImpl()->stopMarkListWatching();
2742 else
2744 m_aMarkTimer.Stop();
2746 SuspendPropertyTracking aSuspend( *this );
2747 pFormView->GetImpl()->saveMarkList();
2750 if (bDesign && m_xExternalViewController.is())
2751 CloseExternalFormViewer_Lock();
2753 pFormView->ChangeDesignMode(bDesign);
2755 // notify listeners
2756 FmDesignModeChangedHint aChangedHint( bDesign );
2757 m_pShell->Broadcast(aChangedHint);
2759 m_pShell->m_bDesignMode = bDesign;
2760 UpdateForms_Lock(false);
2762 m_pTextShell->designModeChanged();
2764 if (bDesign)
2766 SdrMarkList aList;
2768 // during changing the mark list, don't track the selected objects in the property browser
2769 SuspendPropertyTracking aSuspend( *this );
2770 // restore the marks
2771 pFormView->GetImpl()->restoreMarkList( aList );
2774 // synchronize with the restored mark list
2775 if ( aList.GetMarkCount() )
2776 SetSelection_Lock(aList);
2778 else
2780 // subscribe to the model of the view (so that I'm informed when someone deletes
2781 // during the alive mode controls that I had saved in the saveMarklist (60343)
2782 pFormView->GetImpl()->startMarkListWatching();
2785 m_pShell->UIFeatureChanged();
2787 // 67506 - 15.07.99 - FS
2788 if (bDesign && m_bHadPropertyBrowserInDesignMode)
2790 // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
2791 // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
2792 // That's why we use an asynchron execution on the dispatcher.
2793 // (And that's why this has to be done AFTER the UIFeatureChanged.)
2794 m_pShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
2796 m_bChangingDesignMode = false;
2800 Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
2802 if (impl_checkDisposed_Lock())
2803 return nullptr;
2805 Reference< XControl > xControl;
2808 Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
2810 const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
2811 // ... that I can then search
2812 for (Reference< XControl > const & control : seqControls)
2814 xControl.set( control, UNO_SET_THROW );
2815 Reference< XControlModel > xCurrentModel( xControl->getModel() );
2816 if ( xCurrentModel == i_rxModel )
2817 break;
2818 xControl.clear();
2821 if ( !xControl.is() )
2823 // fallback (some controls might not have been created, yet, since they were never visible so far)
2824 Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
2825 const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
2826 ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
2828 const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
2829 ENSURE_OR_THROW( pSdrView, "no current view" );
2831 xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow->GetOutDev() ), UNO_SET_THROW );
2834 catch( const Exception& )
2836 DBG_UNHANDLED_EXCEPTION("svx");
2839 OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
2840 return xControl;
2843 // note: _out_rForms is a member so needs lock
2844 void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
2845 std::u16string_view _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
2849 Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
2850 if ( !xContainer.is() )
2851 return;
2853 sal_Int32 nCount( xContainer->getCount() );
2854 if ( nCount == 0 )
2855 return;
2857 OUString sCurrentFormName;
2858 OUStringBuffer aNextLevelPrefix;
2859 for ( sal_Int32 i=0; i<nCount; ++i )
2861 // is the current child a form?
2862 Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
2863 if ( !xCurrentAsForm.is() )
2864 continue;
2866 Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
2867 sCurrentFormName = xNamed->getName();
2869 // the name of the current form
2870 OUString sCompleteCurrentName( sCurrentFormName );
2871 if ( !_rCurrentLevelPrefix.empty() )
2873 sCompleteCurrentName += OUString::Concat(" (") + _rCurrentLevelPrefix + ")";
2876 // the prefix for the next level
2877 aNextLevelPrefix = _rCurrentLevelPrefix;
2878 if ( !_rCurrentLevelPrefix.empty() )
2879 aNextLevelPrefix.append( '/' );
2880 aNextLevelPrefix.append( sCurrentFormName );
2882 // remember both the form and its "display name"
2883 _out_rForms.push_back( xCurrentAsForm );
2884 _out_rNames.push_back( sCompleteCurrentName );
2886 // and descend
2887 impl_collectFormSearchContexts_nothrow_Lock(
2888 xCurrentAsForm, aNextLevelPrefix,
2889 _out_rForms, _out_rNames);
2890 aNextLevelPrefix.setLength(0);
2893 catch( const Exception& )
2895 DBG_UNHANDLED_EXCEPTION("svx");
2900 void FmXFormShell::startFiltering_Lock()
2902 if (impl_checkDisposed_Lock())
2903 return;
2905 // setting all forms in filter mode
2906 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2908 // if the active controller is our external one we have to use the trigger controller
2909 Reference< XControlContainer> xContainer;
2910 if (getActiveController_Lock() == m_xExternalViewController)
2912 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
2913 xContainer = m_xExtViewTriggerController->getContainer();
2915 else
2916 xContainer = getActiveController_Lock()->getContainer();
2918 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow( xContainer );
2919 if ( pAdapter.is() )
2921 const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
2922 for (const auto& rpController : rControllerList)
2924 Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
2925 if (xModeSelector.is())
2926 xModeSelector->setMode( "FilterMode" );
2930 m_bFilterMode = true;
2932 m_pShell->UIFeatureChanged();
2933 SfxViewFrame* pViewFrame = m_pShell->GetViewShell()->GetViewFrame();
2934 pViewFrame->GetBindings().InvalidateShell( *m_pShell );
2936 if ( pViewFrame->KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
2937 && !pViewFrame->HasChildWindow( SID_FM_FILTER_NAVIGATOR )
2940 pViewFrame->ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
2945 static void saveFilter(const Reference< runtime::XFormController >& _rxController)
2947 Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
2948 Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
2950 // call the subcontroller
2951 Reference< runtime::XFormController > xController;
2952 for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
2954 _rxController->getByIndex(i) >>= xController;
2955 saveFilter(xController);
2961 xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
2962 xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, Any( true ) );
2964 catch (const Exception& )
2966 DBG_UNHANDLED_EXCEPTION("svx");
2972 void FmXFormShell::stopFiltering_Lock(bool bSave)
2974 if (impl_checkDisposed_Lock())
2975 return;
2977 m_bFilterMode = false;
2979 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2981 // if the active controller is our external one we have to use the trigger controller
2982 Reference< XControlContainer> xContainer;
2983 if (getActiveController_Lock() == m_xExternalViewController)
2985 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
2986 xContainer = m_xExtViewTriggerController->getContainer();
2988 else
2989 xContainer = getActiveController_Lock()->getContainer();
2991 rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow(xContainer);
2992 if ( pAdapter.is() )
2994 const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
2995 ::std::vector < OUString > aOriginalFilters;
2996 ::std::vector < bool > aOriginalApplyFlags;
2998 if (bSave)
3000 for (const auto& rpController : rControllerList)
3002 // remember the current filter settings in case we're going to reload the forms below (which may fail)
3005 Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
3006 aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
3007 aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
3009 catch(Exception&)
3011 OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
3012 // put dummies into the arrays so the they have the right size
3014 if (aOriginalFilters.size() == aOriginalApplyFlags.size())
3015 // the first getPropertyValue failed -> use two dummies
3016 aOriginalFilters.emplace_back( );
3017 aOriginalApplyFlags.push_back( false );
3019 saveFilter(rpController);
3022 for (const auto& rController : rControllerList)
3025 Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
3026 if (xModeSelector.is())
3027 xModeSelector->setMode( "DataMode" );
3029 if (bSave) // execute the filter
3031 const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
3032 for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
3033 j != rControllers.end(); ++j)
3035 Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
3036 if (!xReload.is())
3037 continue;
3038 Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
3042 xReload->reload();
3044 catch(Exception&)
3046 TOOLS_WARN_EXCEPTION("svx.form", "");
3049 if (!isRowSetAlive(xFormSet))
3050 { // something went wrong -> restore the original state
3051 OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
3052 bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
3055 xFormSet->setPropertyValue(FM_PROP_FILTER, Any(sOriginalFilter));
3056 xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, Any(bOriginalApplyFlag));
3057 xReload->reload();
3059 catch(const Exception&)
3061 DBG_UNHANDLED_EXCEPTION("svx");
3068 m_pShell->UIFeatureChanged();
3069 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
3073 void FmXFormShell::CreateExternalView_Lock()
3075 if (impl_checkDisposed_Lock())
3076 return;
3078 DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
3080 // the frame the external view is displayed in
3081 bool bAlreadyExistent = m_xExternalViewController.is();
3082 Reference< css::frame::XFrame> xExternalViewFrame;
3084 Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
3085 // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
3087 // _first_ check if we have any valid fields we can use for the grid view
3088 // FS - 21.10.99 - 69219
3090 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3091 bool bHaveUsableControls = false;
3092 for (;;)
3094 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3095 if (!xCurrentModelSet.is())
3096 break;
3097 // the FmXBoundFormFieldIterator only supplies controls with a valid control source
3098 // so we just have to check the field type
3099 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3100 switch (nClassId)
3102 case FormComponentType::IMAGECONTROL:
3103 case FormComponentType::CONTROL:
3104 continue;
3106 bHaveUsableControls = true;
3107 break;
3110 if (!bHaveUsableControls)
3112 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
3113 VclMessageType::Warning, VclButtonsType::Ok,
3114 SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
3115 xBox->run();
3116 return;
3120 // load the component for external form views
3121 if (!bAlreadyExistent)
3123 OUString sFrameName("_beamer");
3124 URL aWantToDispatch;
3125 aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
3127 Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
3128 Reference< css::frame::XDispatch> xDisp;
3129 if (xProv.is())
3130 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
3131 css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
3132 if (xDisp.is())
3134 xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
3137 // with this the component should be loaded, now search the frame where it resides in
3138 xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
3139 if (xExternalViewFrame.is())
3141 m_xExternalViewController = xExternalViewFrame->getController();
3142 if (m_xExternalViewController.is())
3143 m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
3146 else
3148 xExternalViewFrame = m_xExternalViewController->getFrame();
3149 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3151 // if we display the active form we interpret the slot as "remove it"
3152 Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
3153 if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
3155 if (m_xExternalViewController == getActiveController_Lock())
3157 Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
3158 ControllerFeatures aHelper( xAsFormController );
3159 (void)aHelper->commitCurrentControl();
3162 Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
3163 CloseExternalFormViewer_Lock();
3164 setActiveController_Lock(xNewController);
3165 return;
3168 URL aClearURL;
3169 aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
3171 Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
3172 if (xClear.is())
3173 xClear->dispatch(aClearURL, Sequence< PropertyValue>());
3176 // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
3177 // instance for which this "external view" was triggered
3179 // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
3180 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3182 if (m_xExternalViewController.is())
3184 DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
3185 // collect the dispatchers we will need
3186 URL aAddColumnURL;
3187 aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
3188 Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
3189 URL aAttachURL;
3190 aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
3191 Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
3193 if (xAddColumnDispatch.is() && xAttachDispatch.is())
3195 DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
3196 // first : dispatch the descriptions for the columns to add
3197 sal_Int16 nAddedColumns = 0;
3199 // for radio buttons we need some special structures
3200 typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
3201 typedef std::map< OUString, OUString > FmMapUString2UString;
3202 typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
3204 MapUString2UstringSeq aRadioValueLists;
3205 MapUString2UstringSeq aRadioListSources;
3206 FmMapUString2UString aRadioControlSources;
3207 FmMapUString2Int16 aRadioPositions;
3209 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3210 OUString sColumnType,aGroupName,sControlSource;
3211 Sequence< Property> aProps;
3212 for (;;)
3214 Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
3215 if (!xCurrentModelSet.is())
3216 break;
3217 OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
3218 // create a description of the column to be created
3219 // first : determine it's type
3221 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3222 switch (nClassId)
3224 case FormComponentType::RADIOBUTTON:
3226 // get the label of the button (this is the access key for our structures)
3227 aGroupName = getLabelName(xCurrentModelSet);
3229 // add the reference value of the radio button to the list source sequence
3230 Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
3231 sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
3232 aThisGroupLabels.realloc(nNewSizeL);
3233 aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
3235 // add the label to the value list sequence
3236 Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
3237 sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
3238 aThisGroupControlSources.realloc(nNewSizeC);
3239 aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
3241 // remember the controls source of the radio group
3242 sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
3243 if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
3244 aRadioControlSources[aGroupName] = sControlSource;
3245 #ifdef DBG_UTIL
3246 else
3247 DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
3248 "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
3249 // (radio buttons with the same name should have the same control source)
3250 #endif
3251 // remember the position within the columns
3252 if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
3253 aRadioPositions[aGroupName] = nAddedColumns;
3255 // any further handling is done below
3257 continue;
3259 case FormComponentType::IMAGECONTROL:
3260 case FormComponentType::CONTROL:
3261 // no grid columns for these types (though they have a control source)
3262 continue;
3263 case FormComponentType::CHECKBOX:
3264 sColumnType = FM_COL_CHECKBOX; break;
3265 case FormComponentType::LISTBOX:
3266 sColumnType = FM_COL_LISTBOX; break;
3267 case FormComponentType::COMBOBOX:
3268 sColumnType = FM_COL_COMBOBOX; break;
3269 case FormComponentType::DATEFIELD:
3270 sColumnType = FM_COL_DATEFIELD; break;
3271 case FormComponentType::TIMEFIELD:
3272 sColumnType = FM_COL_TIMEFIELD; break;
3273 case FormComponentType::NUMERICFIELD:
3274 sColumnType = FM_COL_NUMERICFIELD; break;
3275 case FormComponentType::CURRENCYFIELD:
3276 sColumnType = FM_COL_CURRENCYFIELD; break;
3277 case FormComponentType::PATTERNFIELD:
3278 sColumnType = FM_COL_PATTERNFIELD; break;
3280 case FormComponentType::TEXTFIELD:
3282 sColumnType = FM_COL_TEXTFIELD;
3283 // we know at least two different controls which are TextFields : the basic edit field and the formatted
3284 // field. we distinguish them by their service name
3285 Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
3286 if (xInfo.is())
3288 SdrObjKind nObjectType = getControlTypeByObject(xInfo);
3289 if (SdrObjKind::FormFormattedField == nObjectType)
3290 sColumnType = FM_COL_FORMATTEDFIELD;
3293 break;
3294 default:
3295 sColumnType = FM_COL_TEXTFIELD; break;
3298 const sal_Int16 nDispatchArgs = 3;
3299 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3300 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3302 // properties describing "meta data" about the column
3303 // the type
3304 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3305 pDispatchArgs->Value <<= sColumnType;
3306 ++pDispatchArgs;
3308 // the pos : append the col
3309 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3310 pDispatchArgs->Value <<= nAddedColumns;
3311 ++pDispatchArgs;
3313 // the properties to forward to the new column
3314 Sequence< PropertyValue> aColumnProps(1);
3315 PropertyValue* pColumnProps = aColumnProps.getArray();
3317 // the label
3318 pColumnProps->Name = FM_PROP_LABEL;
3319 pColumnProps->Value <<= getLabelName(xCurrentModelSet);
3320 ++pColumnProps;
3322 // for all other props : transfer them
3323 Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
3324 DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
3325 aProps = xControlModelInfo->getProperties();
3327 // realloc the control description sequence
3328 sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
3329 aColumnProps.realloc(nExistentDescs + aProps.getLength());
3330 pColumnProps = aColumnProps.getArray() + nExistentDescs;
3332 for (const Property& rProp : std::as_const(aProps))
3334 if (rProp.Name == FM_PROP_LABEL)
3335 // already set
3336 continue;
3337 if (rProp.Name == FM_PROP_DEFAULTCONTROL)
3338 // allow the column's own "default control"
3339 continue;
3340 if (rProp.Attributes & PropertyAttribute::READONLY)
3341 // assume that properties which are readonly for the control are ro for the column to be created, too
3342 continue;
3344 pColumnProps->Name = rProp.Name;
3345 pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
3346 ++pColumnProps;
3348 aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
3350 // columns props are a dispatch argument
3351 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3352 pDispatchArgs->Value <<= aColumnProps;
3353 ++pDispatchArgs;
3354 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3355 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3357 // dispatch the "add column"
3358 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3359 ++nAddedColumns;
3362 // now for the radio button handling
3363 sal_Int16 nOffset(0);
3364 // properties describing the "direct" column properties
3365 const sal_Int16 nListBoxDescription = 6;
3366 Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
3367 for (const auto& rCtrlSource : aRadioControlSources)
3369 PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
3370 // label
3371 pListBoxDescription->Name = FM_PROP_LABEL;
3372 pListBoxDescription->Value <<= rCtrlSource.first;
3373 ++pListBoxDescription;
3375 // control source
3376 pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
3377 pListBoxDescription->Value <<= rCtrlSource.second;
3378 ++pListBoxDescription;
3380 // bound column
3381 pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
3382 pListBoxDescription->Value <<= sal_Int16(1);
3383 ++pListBoxDescription;
3385 // content type
3386 pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
3387 pListBoxDescription->Value <<= ListSourceType_VALUELIST;
3388 ++pListBoxDescription;
3390 // list source
3391 MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
3392 DBG_ASSERT(aCurrentListSource != aRadioListSources.end(),
3393 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3394 pListBoxDescription->Name = FM_PROP_LISTSOURCE;
3395 pListBoxDescription->Value <<= (*aCurrentListSource).second;
3396 ++pListBoxDescription;
3398 // value list
3399 MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
3400 DBG_ASSERT(aCurrentValueList != aRadioValueLists.end(),
3401 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3402 pListBoxDescription->Name = FM_PROP_STRINGITEMLIST;
3403 pListBoxDescription->Value <<= (*aCurrentValueList).second;
3404 ++pListBoxDescription;
3406 DBG_ASSERT(nListBoxDescription == (pListBoxDescription - aListBoxDescription.getConstArray()),
3407 "FmXFormShell::CreateExternalView : forgot to adjust nListBoxDescription ?");
3409 // properties describing the column "meta data"
3410 const sal_Int16 nDispatchArgs = 3;
3411 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3412 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3414 // column type : listbox
3415 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3416 pDispatchArgs->Value <<= OUString(FM_COL_LISTBOX);
3417 // pDispatchArgs->Value <<= (OUString)FM_COL_LISTBOX;
3418 ++pDispatchArgs;
3420 // column position
3421 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3422 FmMapUString2Int16::const_iterator aOffset = aRadioPositions.find(rCtrlSource.first);
3423 DBG_ASSERT(aOffset != aRadioPositions.end(),
3424 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3425 sal_Int16 nPosition = (*aOffset).second;
3426 nPosition = nPosition + nOffset;
3427 // we already inserted nOffset additional columns...
3428 pDispatchArgs->Value <<= nPosition;
3429 ++pDispatchArgs;
3431 // the
3432 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3433 pDispatchArgs->Value <<= aListBoxDescription;
3434 ++pDispatchArgs;
3435 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3436 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3438 // dispatch the "add column"
3439 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3440 ++nAddedColumns;
3441 ++nOffset;
3445 DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
3446 // we should have checked if we have any usable controls (see above).
3448 // "load" the "form" of the external view
3449 PropertyValue aArg;
3450 aArg.Name = FMARG_ATTACHTO_MASTERFORM;
3451 Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
3452 aArg.Value <<= xForm;
3454 m_xExternalDisplayedForm = xForm;
3455 // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
3456 // which needs the m_xExternalDisplayedForm
3458 xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
3460 m_xExtViewTriggerController = xCurrentNavController;
3462 // we want to know modifications done in the external view
3463 // if the external controller is a XFormController we can use all our default handlings for it
3464 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
3465 OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
3466 if (xFormController.is())
3467 xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
3470 #ifdef DBG_UTIL
3471 else
3473 OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
3475 #endif
3476 InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
3480 void FmXFormShell::implAdjustConfigCache_Lock()
3482 // get (cache) the wizard usage flag
3483 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3484 Sequence< Any > aFlags = GetProperties(aNames);
3485 if (1 == aFlags.getLength())
3486 m_bUseWizards = ::cppu::any2bool(aFlags[0]);
3490 void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
3492 DBG_TESTSOLARMUTEX();
3493 if (impl_checkDisposed_Lock())
3494 return;
3496 for (const OUString& rName : _rPropertyNames)
3497 if (rName == "FormControlPilotsEnabled")
3499 implAdjustConfigCache_Lock();
3500 InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
3504 void FmXFormShell::ImplCommit()
3509 void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
3511 m_bUseWizards = _bUseThem;
3513 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3514 Sequence< Any > aValues{ Any(m_bUseWizards) };
3515 PutProperties(aNames, aValues);
3519 void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
3522 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3524 _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
3527 // if we have an async load operation pending for the 0-th page for this view,
3528 // we need to cancel this
3529 if (FmFormPage* pPage = _rCurrentView.GetCurPage())
3531 // move all events from our queue to a new one, omit the events for the deactivated
3532 // page
3533 ::std::queue< FmLoadAction > aNewEvents;
3534 while ( !m_aLoadingPages.empty() )
3536 FmLoadAction aAction = m_aLoadingPages.front();
3537 m_aLoadingPages.pop();
3538 if ( pPage != aAction.pPage )
3540 aNewEvents.push( aAction );
3542 else
3544 Application::RemoveUserEvent( aAction.nEventId );
3547 m_aLoadingPages = aNewEvents;
3549 // remove callbacks at the page
3550 pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
3552 UpdateForms_Lock(true);
3556 IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
3558 if (impl_checkDisposed_Lock())
3559 return;
3561 m_nActivationEvent = nullptr;
3562 SfxObjectShell* pDocument = m_pShell->GetObjectShell();
3564 if ( pDocument && !pDocument->HasName() )
3566 if (isEnhancedForm_Lock())
3568 // show the data navigator
3569 if ( !m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
3570 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
3576 IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
3578 UpdateForms_Lock(true);
3582 void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
3584 FmFormPage* pPage = _rCurrentView.GetCurPage();
3586 // activate our view if we are activated ourself
3587 // FS - 30.06.99 - 67308
3588 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3590 // load forms for the page the current view belongs to
3591 if ( pPage )
3593 if ( !pPage->GetImpl().hasEverBeenActivated() )
3594 loadForms_Lock(pPage, LoadFormsFlags::Load
3595 | (_bSyncAction ? LoadFormsFlags::Sync
3596 : LoadFormsFlags::Async));
3597 pPage->GetImpl().setHasBeenActivated( );
3600 // first-time initializations for the views
3601 if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
3603 _rCurrentView.GetImpl()->onFirstViewActivation( dynamic_cast<FmFormModel*>( _rCurrentView.GetModel() ) );
3604 _rCurrentView.GetImpl()->setHasBeenActivated( );
3607 // activate the current view
3608 _rCurrentView.GetImpl()->Activate( _bSyncAction );
3611 // set callbacks at the page
3612 if ( pPage )
3614 pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
3617 UpdateForms_Lock(true);
3619 if ( m_bFirstActivation )
3621 m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
3622 m_bFirstActivation = false;
3625 // find a default "current form", if there is none, yet
3626 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
3627 impl_defaultCurrentForm_nothrow_Lock();
3631 void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
3633 if (impl_checkDisposed_Lock())
3634 return;
3636 if ( m_xCurrentForm.is() )
3637 // no action required
3638 return;
3640 FmFormView* pFormView = m_pShell->GetFormView();
3641 FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
3642 if ( !pPage )
3643 return;
3647 Reference< XIndexAccess > xForms = pPage->GetForms( false );
3648 if ( !xForms.is() || !xForms->hasElements() )
3649 return;
3651 Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
3652 impl_updateCurrentForm_Lock(xNewCurrentForm);
3654 catch( const Exception& )
3656 DBG_UNHANDLED_EXCEPTION("svx");
3661 void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
3663 if (!_rxModels.is())
3665 OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
3666 return;
3669 sal_Int32 nCount = _rxModels->getCount();
3670 Reference< XPropertySet > xCurrent;
3671 Reference< XPropertySetInfo > xCurrentInfo;
3672 Reference< XPropertySet > xBoundField;
3674 for (sal_Int32 i=0; i<nCount; ++i)
3676 _rxModels->getByIndex(i) >>= xCurrent;
3677 if (xCurrent.is())
3678 xCurrentInfo = xCurrent->getPropertySetInfo();
3679 else
3680 xCurrentInfo.clear();
3681 if (!xCurrentInfo.is())
3682 continue;
3684 if (xCurrentInfo->hasPropertyByName(FM_PROP_CLASSID))
3685 { // it's a control model
3687 // check if this control is bound to a living database field
3688 if (xCurrentInfo->hasPropertyByName(FM_PROP_BOUNDFIELD))
3689 xCurrent->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xBoundField;
3690 else
3691 xBoundField.clear();
3693 // reset only if it's *not* bound
3694 bool bReset = !xBoundField.is();
3696 // and additionally, check if it has an external value binding
3697 Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
3698 if ( xBindable.is() && xBindable->getValueBinding().is() )
3699 bReset = false;
3701 if ( bReset )
3703 Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
3704 if ( xControlReset.is() )
3705 xControlReset->reset();
3708 else
3710 Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
3711 if (xContainer.is())
3712 smartControlReset(xContainer);
3718 IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
3720 FmLoadAction aAction = m_aLoadingPages.front();
3721 m_aLoadingPages.pop();
3723 loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
3727 namespace
3729 bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
3731 // determines whether a form should be loaded or not
3732 // if there is no datasource or connection there is no reason to load a form
3733 Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
3734 if ( !xSet.is() )
3735 return false;
3738 Reference< XConnection > xConn;
3739 if ( isEmbeddedInDatabase( _rxLoadable, xConn ) )
3740 return true;
3742 // is there already an active connection
3743 xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
3744 if ( xConn.is() )
3745 return true;
3747 OUString sPropertyValue;
3748 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
3749 if ( !sPropertyValue.isEmpty() )
3750 return true;
3752 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
3753 if ( !sPropertyValue.isEmpty() )
3754 return true;
3756 catch(const Exception&)
3758 DBG_UNHANDLED_EXCEPTION("svx");
3760 return false;
3765 void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
3767 DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
3768 "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
3770 if ( _nBehaviour & LoadFormsFlags::Async )
3772 m_aLoadingPages.push( FmLoadAction(
3773 _pPage,
3774 _nBehaviour,
3775 Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
3776 ) );
3777 return;
3780 DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
3781 if ( !_pPage )
3782 return;
3784 // lock the undo env so the forms can change non-transient properties while loading
3785 // (without this my doc's modified flag would be set)
3786 FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
3787 rFmFormModel.GetUndoEnv().Lock();
3789 // load all forms
3790 Reference< XIndexAccess > xForms = _pPage->GetForms( false );
3792 if ( xForms.is() )
3794 Reference< XLoadable > xForm;
3795 for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
3797 xForms->getByIndex( j ) >>= xForm;
3798 bool bFormWasLoaded = false;
3799 // a database form must be loaded for
3802 if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
3804 if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
3805 xForm->load();
3807 else
3809 if ( xForm->isLoaded() )
3811 bFormWasLoaded = true;
3812 xForm->unload();
3816 catch( const Exception& )
3818 DBG_UNHANDLED_EXCEPTION("svx");
3821 // reset the form if it was loaded
3822 if ( bFormWasLoaded )
3824 Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
3825 DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
3826 if ( xContainer.is() )
3827 smartControlReset( xContainer );
3832 // unlock the environment
3833 rFmFormModel.GetUndoEnv().UnLock();
3837 void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
3839 DBG_TESTSOLARMUTEX();
3840 m_pTextShell->ExecuteTextAttribute( _rReq );
3844 void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
3846 DBG_TESTSOLARMUTEX();
3847 m_pTextShell->GetTextAttributeState( _rSet );
3851 bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
3853 DBG_TESTSOLARMUTEX();
3854 return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
3858 void FmXFormShell::ForgetActiveControl_Lock()
3860 DBG_TESTSOLARMUTEX();
3861 m_pTextShell->ForgetActiveControl();
3865 void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
3867 DBG_TESTSOLARMUTEX();
3868 m_pTextShell->SetControlActivationHandler( _rHdl );
3871 void FmXFormShell::handleShowPropertiesRequest_Lock()
3873 if (onlyControlsAreMarked_Lock())
3874 ShowSelectionProperties_Lock( true );
3878 void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
3880 // catch simple double clicks
3881 if (_rViewEvent.mnMouseClicks == 2 && _rViewEvent.mnMouseCode == MOUSE_LEFT)
3883 if ( _rViewEvent.meHit == SdrHitKind::MarkedObject )
3885 if (onlyControlsAreMarked_Lock())
3886 ShowSelectionProperties_Lock( true );
3892 bool FmXFormShell::HasControlFocus_Lock() const
3894 bool bHasControlFocus = false;
3898 Reference<runtime::XFormController> xController(getActiveController_Lock());
3899 Reference< XControl > xCurrentControl;
3900 if ( xController.is() )
3901 xCurrentControl.set( xController->getCurrentControl() );
3902 if ( xCurrentControl.is() )
3904 Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
3905 bHasControlFocus = xPeerWindow->hasFocus();
3908 catch( const Exception& )
3910 DBG_UNHANDLED_EXCEPTION("svx");
3913 return bHasControlFocus;
3917 SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
3918 :IndexAccessIterator(xStartingPoint)
3923 bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
3925 // if the thing has a ControlSource and a BoundField property
3926 Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
3927 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
3929 // and the BoundField is valid
3930 Reference< XPropertySet> xField;
3931 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3932 if (xField.is())
3934 // we take it
3935 m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
3936 return true;
3940 // if it is a grid control
3941 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
3943 Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
3944 if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
3946 m_sCurrentValue.clear();
3947 return true;
3951 return false;
3955 bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
3957 return true;
3960 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */