Bump version to 6.4-15
[LibreOffice.git] / svx / source / form / fmshimp.cxx
blobb17a794c79ffdb766e85dd9d594969a4c36300ba
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 .
21 #include <sal/macros.h>
22 #include <sal/log.hxx>
23 #include <fmobj.hxx>
24 #include <fmpgeimp.hxx>
25 #include <svx/fmtools.hxx>
26 #include <fmprop.hxx>
27 #include <fmservs.hxx>
28 #include <fmshimp.hxx>
29 #include <fmtextcontrolshell.hxx>
30 #include <fmundo.hxx>
31 #include <fmurl.hxx>
32 #include <fmvwimp.hxx>
33 #include <formtoolbars.hxx>
34 #include <gridcols.hxx>
35 #include <svx/svditer.hxx>
36 #include <svx/dialmgr.hxx>
37 #include <svx/strings.hrc>
38 #include <svx/fmglob.hxx>
39 #include <svx/fmmodel.hxx>
40 #include <svx/fmpage.hxx>
41 #include <svx/fmshell.hxx>
42 #include <svx/fmview.hxx>
43 #include <svx/obj3d.hxx>
44 #include <svx/sdrpagewindow.hxx>
45 #include <svx/svdpagv.hxx>
46 #include <svx/svxdlg.hxx>
47 #include <svx/svxids.hrc>
48 #include <bitmaps.hlst>
50 #include <com/sun/star/awt/XWindow2.hpp>
51 #include <com/sun/star/awt/XCheckBox.hpp>
52 #include <com/sun/star/awt/XListBox.hpp>
53 #include <com/sun/star/awt/XTextComponent.hpp>
54 #include <com/sun/star/beans/theIntrospection.hpp>
55 #include <com/sun/star/beans/NamedValue.hpp>
56 #include <com/sun/star/beans/PropertyAttribute.hpp>
57 #include <com/sun/star/beans/XPropertyState.hpp>
58 #include <com/sun/star/container/XContainer.hpp>
59 #include <com/sun/star/container/XEnumeration.hpp>
60 #include <com/sun/star/container/XEnumerationAccess.hpp>
61 #include <com/sun/star/container/XIndexAccess.hpp>
62 #include <com/sun/star/container/XNamed.hpp>
63 #include <com/sun/star/form/ListSourceType.hpp>
64 #include <com/sun/star/form/TabOrderDialog.hpp>
65 #include <com/sun/star/form/XBoundComponent.hpp>
66 #include <com/sun/star/form/XBoundControl.hpp>
67 #include <com/sun/star/form/XGrid.hpp>
68 #include <com/sun/star/form/XGridPeer.hpp>
69 #include <com/sun/star/form/XLoadable.hpp>
70 #include <com/sun/star/form/XReset.hpp>
71 #include <com/sun/star/form/binding/XBindableValue.hpp>
72 #include <com/sun/star/form/binding/XListEntrySink.hpp>
73 #include <com/sun/star/frame/FrameSearchFlag.hpp>
74 #include <com/sun/star/script/XEventAttacherManager.hpp>
75 #include <com/sun/star/sdbc/SQLException.hpp>
76 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
77 #include <com/sun/star/util/XCancellable.hpp>
78 #include <com/sun/star/util/XModeSelector.hpp>
79 #include <com/sun/star/util/XModifyBroadcaster.hpp>
80 #include <com/sun/star/util/XNumberFormatter.hpp>
81 #include <com/sun/star/view/XSelectionSupplier.hpp>
83 #include <comphelper/evtmethodhelper.hxx>
84 #include <comphelper/processfactory.hxx>
85 #include <comphelper/property.hxx>
86 #include <comphelper/sequence.hxx>
87 #include <comphelper/solarmutex.hxx>
88 #include <comphelper/string.hxx>
89 #include <comphelper/types.hxx>
90 #include <connectivity/dbtools.hxx>
91 #include <sfx2/dispatch.hxx>
92 #include <sfx2/docfile.hxx>
93 #include <sfx2/frame.hxx>
94 #include <sfx2/objsh.hxx>
95 #include <sfx2/viewfrm.hxx>
96 #include <sfx2/viewsh.hxx>
97 #include <toolkit/helper/vclunohelper.hxx>
98 #include <tools/debug.hxx>
99 #include <tools/diagnose_ex.h>
100 #include <vcl/image.hxx>
101 #include <vcl/weld.hxx>
102 #include <vcl/waitobj.hxx>
103 #include <vcl/settings.hxx>
104 #include <vcl/svapp.hxx>
106 #include <algorithm>
107 #include <functional>
108 #include <map>
109 #include <memory>
110 #include <vector>
112 // is used for Invalidate -> maintain it as well
113 static const sal_uInt16 DatabaseSlotMap[] =
115 SID_FM_RECORD_FIRST,
116 SID_FM_RECORD_NEXT,
117 SID_FM_RECORD_PREV,
118 SID_FM_RECORD_LAST,
119 SID_FM_RECORD_NEW,
120 SID_FM_RECORD_DELETE,
121 SID_FM_RECORD_ABSOLUTE,
122 SID_FM_RECORD_TOTAL,
123 SID_FM_RECORD_SAVE,
124 SID_FM_RECORD_UNDO,
125 SID_FM_REMOVE_FILTER_SORT,
126 SID_FM_SORTUP,
127 SID_FM_SORTDOWN,
128 SID_FM_ORDERCRIT,
129 SID_FM_AUTOFILTER,
130 SID_FM_FORM_FILTERED,
131 SID_FM_REFRESH,
132 SID_FM_REFRESH_FORM_CONTROL,
133 SID_FM_SEARCH,
134 SID_FM_FILTER_START,
135 SID_FM_VIEW_AS_GRID,
139 // is used for Invalidate -> maintain it as well
140 // sort ascending !!!!!!
141 static const sal_Int16 DlgSlotMap[] = // slots of the controller
143 SID_FM_CTL_PROPERTIES,
144 SID_FM_PROPERTIES,
145 SID_FM_TAB_DIALOG,
146 SID_FM_ADD_FIELD,
147 SID_FM_SHOW_FMEXPLORER,
148 SID_FM_FIELDS_CONTROL,
149 SID_FM_SHOW_PROPERTIES,
150 SID_FM_PROPERTY_CONTROL,
151 SID_FM_FMEXPLORER_CONTROL,
152 SID_FM_SHOW_DATANAVIGATOR,
153 SID_FM_DATANAVIGATOR_CONTROL,
157 static const sal_Int16 SelObjectSlotMap[] = // slots depending on the SelObject
159 SID_FM_CONVERTTO_EDIT,
160 SID_FM_CONVERTTO_BUTTON,
161 SID_FM_CONVERTTO_FIXEDTEXT,
162 SID_FM_CONVERTTO_LISTBOX,
163 SID_FM_CONVERTTO_CHECKBOX,
164 SID_FM_CONVERTTO_RADIOBUTTON,
165 SID_FM_CONVERTTO_GROUPBOX,
166 SID_FM_CONVERTTO_COMBOBOX,
167 SID_FM_CONVERTTO_IMAGEBUTTON,
168 SID_FM_CONVERTTO_FILECONTROL,
169 SID_FM_CONVERTTO_DATE,
170 SID_FM_CONVERTTO_TIME,
171 SID_FM_CONVERTTO_NUMERIC,
172 SID_FM_CONVERTTO_CURRENCY,
173 SID_FM_CONVERTTO_PATTERN,
174 SID_FM_CONVERTTO_IMAGECONTROL,
175 SID_FM_CONVERTTO_FORMATTED,
176 SID_FM_CONVERTTO_SCROLLBAR,
177 SID_FM_CONVERTTO_SPINBUTTON,
178 SID_FM_CONVERTTO_NAVIGATIONBAR,
180 SID_FM_FMEXPLORER_CONTROL,
181 SID_FM_DATANAVIGATOR_CONTROL,
186 // the following arrays must be consistent, i.e., corresponding entries should
187 // be at the same relative position within their respective arrays
188 static const char* aConvertSlots[] =
190 "ConvertToEdit",
191 "ConvertToButton",
192 "ConvertToFixed",
193 "ConvertToList",
194 "ConvertToCheckBox",
195 "ConvertToRadio",
196 "ConvertToGroup",
197 "ConvertToCombo",
198 "ConvertToImageBtn",
199 "ConvertToFileControl",
200 "ConvertToDate",
201 "ConvertToTime",
202 "ConvertToNumeric",
203 "ConvertToCurrency",
204 "ConvertToPattern",
205 "ConvertToImageControl",
206 "ConvertToFormatted",
207 "ConvertToScrollBar",
208 "ConvertToSpinButton",
209 "ConvertToNavigationBar"
212 static const OUStringLiteral aImgIds[] =
214 RID_SVXBMP_EDITBOX,
215 RID_SVXBMP_BUTTON,
216 RID_SVXBMP_FIXEDTEXT,
217 RID_SVXBMP_LISTBOX,
218 RID_SVXBMP_CHECKBOX,
219 RID_SVXBMP_RADIOBUTTON,
220 RID_SVXBMP_GROUPBOX,
221 RID_SVXBMP_COMBOBOX,
222 RID_SVXBMP_IMAGEBUTTON,
223 RID_SVXBMP_FILECONTROL,
224 RID_SVXBMP_DATEFIELD,
225 RID_SVXBMP_TIMEFIELD,
226 RID_SVXBMP_NUMERICFIELD,
227 RID_SVXBMP_CURRENCYFIELD,
228 RID_SVXBMP_PATTERNFIELD,
229 RID_SVXBMP_IMAGECONTROL,
230 RID_SVXBMP_FORMATTEDFIELD,
231 RID_SVXBMP_SCROLLBAR,
232 RID_SVXBMP_SPINBUTTON,
233 RID_SVXBMP_NAVIGATIONBAR
236 static const sal_Int16 nObjectTypes[] =
238 OBJ_FM_EDIT,
239 OBJ_FM_BUTTON,
240 OBJ_FM_FIXEDTEXT,
241 OBJ_FM_LISTBOX,
242 OBJ_FM_CHECKBOX,
243 OBJ_FM_RADIOBUTTON,
244 OBJ_FM_GROUPBOX,
245 OBJ_FM_COMBOBOX,
246 OBJ_FM_IMAGEBUTTON,
247 OBJ_FM_FILECONTROL,
248 OBJ_FM_DATEFIELD,
249 OBJ_FM_TIMEFIELD,
250 OBJ_FM_NUMERICFIELD,
251 OBJ_FM_CURRENCYFIELD,
252 OBJ_FM_PATTERNFIELD,
253 OBJ_FM_IMAGECONTROL,
254 OBJ_FM_FORMATTEDFIELD,
255 OBJ_FM_SCROLLBAR,
256 OBJ_FM_SPINBUTTON,
257 OBJ_FM_NAVIGATIONBAR
260 using namespace ::com::sun::star;
261 using namespace ::com::sun::star::ui;
262 using namespace ::com::sun::star::uno;
263 using namespace ::com::sun::star::sdb;
264 using namespace ::com::sun::star::sdbc;
265 using namespace ::com::sun::star::sdbcx;
266 using namespace ::com::sun::star::beans;
267 using namespace ::com::sun::star::container;
268 using namespace ::com::sun::star::form;
269 using namespace ::com::sun::star::form::binding;
270 using namespace ::com::sun::star::form::runtime;
271 using namespace ::com::sun::star::awt;
272 using namespace ::com::sun::star::view;
273 using namespace ::com::sun::star::util;
274 using namespace ::com::sun::star::script;
275 using namespace ::svxform;
276 using namespace ::svx;
277 using namespace ::dbtools;
280 //= helper
282 namespace
285 void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag& /* [out] */ _rInterfaces )
287 _rInterfaces.clear();
289 const size_t nMarkCount = _rMarkList.GetMarkCount();
290 for ( size_t i = 0; i < nMarkCount; ++i)
292 SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
294 std::unique_ptr<SdrObjListIter> pGroupIterator;
295 if ( pCurrent->IsGroupObject() )
297 pGroupIterator.reset(new SdrObjListIter( pCurrent->GetSubList() ));
298 pCurrent = pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
301 while ( pCurrent )
303 FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
304 // note this will de-reference virtual objects, if necessary/possible
305 if ( pAsFormObject )
307 Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
308 // the UNO_QUERY is important for normalization
309 if ( xControlModel.is() )
310 _rInterfaces.insert( xControlModel );
313 // next element
314 pCurrent = pGroupIterator && pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
320 sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
324 if (rColumns.is())
326 // loop through all columns
327 sal_Int32 i;
328 Reference< XPropertySet> xCur;
329 for (i=0; i<rColumns->getCount(); ++i)
331 rColumns->getByIndex(i) >>= xCur;
332 if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
334 // for every visible col : if nViewPos is greater zero, decrement it, else we
335 // have found the model position
336 if (!nViewPos)
337 break;
338 else
339 --nViewPos;
342 if (i<rColumns->getCount())
343 return i;
346 catch(const Exception&)
348 DBG_UNHANDLED_EXCEPTION("svx");
350 return -1;
354 void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
355 const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
357 // first check if we have a XEventAttacherManager for the model
358 Reference< XChild> xModelChild(xModel, UNO_QUERY);
359 if (!xModelChild.is())
360 return; // nothing to do
362 Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
363 if (!xEventManager.is())
364 return; // nothing to do
366 if (!rTransferIfAvailable.hasElements())
367 return; // nothing to do
369 // check for the index of the model within its parent
370 Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
371 if (!xParentIndex.is())
372 return; // nothing to do
373 sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
374 if (nIndex<0 || nIndex>=xParentIndex->getCount())
375 return; // nothing to do
377 // then we need information about the listeners supported by the control and the model
378 Sequence< Type> aModelListeners;
379 Sequence< Type> aControlListeners;
381 Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
383 if (xModel.is())
385 Any aModel(makeAny(xModel));
386 aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
389 if (xControl.is())
391 Any aControl(makeAny(xControl));
392 aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
395 sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
396 if (!nMaxNewLen)
397 return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
399 Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
400 ScriptEventDescriptor* pTransferable = aTransferable.getArray();
402 for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
404 // search the model/control idl classes for the event described by pCurrent
405 for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
407 for (const Type& rCurrentListener : *pCurrentArray)
409 OUString aListener = rCurrentListener.getTypeName();
410 if (!aListener.isEmpty())
411 aListener = aListener.copy(aListener.lastIndexOf('.')+1);
413 if (aListener == rCurrent.ListenerType)
414 // the current ScriptEventDescriptor doesn't match the current listeners class
415 continue;
417 // now check the methods
418 Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
420 if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
422 // we can transfer the script event : the model (control) supports it
423 *pTransferable = rCurrent;
424 ++pTransferable;
425 break;
431 sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
432 aTransferable.realloc(nRealNewLen);
434 xEventManager->registerScriptEvents(nIndex, aTransferable);
438 OUString getServiceNameByControlType(sal_Int16 nType)
440 switch (nType)
442 case OBJ_FM_EDIT : return FM_COMPONENT_TEXTFIELD;
443 case OBJ_FM_BUTTON : return FM_COMPONENT_COMMANDBUTTON;
444 case OBJ_FM_FIXEDTEXT : return FM_COMPONENT_FIXEDTEXT;
445 case OBJ_FM_LISTBOX : return FM_COMPONENT_LISTBOX;
446 case OBJ_FM_CHECKBOX : return FM_COMPONENT_CHECKBOX;
447 case OBJ_FM_RADIOBUTTON : return FM_COMPONENT_RADIOBUTTON;
448 case OBJ_FM_GROUPBOX : return FM_COMPONENT_GROUPBOX;
449 case OBJ_FM_COMBOBOX : return FM_COMPONENT_COMBOBOX;
450 case OBJ_FM_GRID : return FM_COMPONENT_GRIDCONTROL;
451 case OBJ_FM_IMAGEBUTTON : return FM_COMPONENT_IMAGEBUTTON;
452 case OBJ_FM_FILECONTROL : return FM_COMPONENT_FILECONTROL;
453 case OBJ_FM_DATEFIELD : return FM_COMPONENT_DATEFIELD;
454 case OBJ_FM_TIMEFIELD : return FM_COMPONENT_TIMEFIELD;
455 case OBJ_FM_NUMERICFIELD : return FM_COMPONENT_NUMERICFIELD;
456 case OBJ_FM_CURRENCYFIELD : return FM_COMPONENT_CURRENCYFIELD;
457 case OBJ_FM_PATTERNFIELD : return FM_COMPONENT_PATTERNFIELD;
458 case OBJ_FM_HIDDEN : return FM_COMPONENT_HIDDENCONTROL;
459 case OBJ_FM_IMAGECONTROL : return FM_COMPONENT_IMAGECONTROL;
460 case OBJ_FM_FORMATTEDFIELD : return FM_COMPONENT_FORMATTEDFIELD;
461 case OBJ_FM_SCROLLBAR : return FM_SUN_COMPONENT_SCROLLBAR;
462 case OBJ_FM_SPINBUTTON : return FM_SUN_COMPONENT_SPINBUTTON;
463 case OBJ_FM_NAVIGATIONBAR : return FM_SUN_COMPONENT_NAVIGATIONBAR;
465 return OUString();
471 // check if the control has one of the interfaces we can use for searching
472 // *_pCurrentText will be filled with the current text of the control (as used when searching this control)
473 bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
474 OUString* _pCurrentText )
476 if ( !_rxControl.is() )
477 return false;
479 Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
480 if ( xAsText.is() )
482 if ( _pCurrentText )
483 *_pCurrentText = xAsText->getText();
484 return true;
487 Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
488 if ( xListBox.is() )
490 if ( _pCurrentText )
491 *_pCurrentText = xListBox->getSelectedItem();
492 return true;
495 Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
496 if ( xCheckBox.is() )
498 if ( _pCurrentText )
500 switch ( static_cast<::TriState>(xCheckBox->getState()) )
502 case TRISTATE_FALSE: *_pCurrentText = "0"; break;
503 case TRISTATE_TRUE: *_pCurrentText = "1"; break;
504 default: _pCurrentText->clear(); break;
507 return true;
510 return false;
514 bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
516 if (_rContainer == m_xStartingPoint)
517 // would be quite stupid to step over the root...
518 return true;
520 return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
524 bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
526 if (!_rElement.is())
527 // NULL element
528 return false;
530 if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
531 // a forms or a grid
532 return false;
534 Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
535 if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
536 // no "BoundField" property
537 return false;
539 Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
540 if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
541 // void or invalid property value
542 return false;
544 return aVal.hasValue();
548 static bool isControlList(const SdrMarkList& rMarkList)
550 // the list contains only controls and at least one control
551 const size_t nMarkCount = rMarkList.GetMarkCount();
552 bool bControlList = nMarkCount != 0;
554 bool bHadAnyLeafs = false;
556 for (size_t i = 0; i < nMarkCount && bControlList; ++i)
558 SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
559 E3dObject* pAs3DObject = dynamic_cast< E3dObject* >( pObj);
560 // E3dObject's do not contain any 2D-objects (by definition)
561 // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
562 // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
563 // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
564 // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
565 // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
566 // And this would be wrong :)
567 // 03.02.00 - 72529 - FS
568 if (!pAs3DObject)
570 if (pObj->IsGroupObject())
572 SdrObjListIter aIter(pObj->GetSubList());
573 while (aIter.IsMore() && bControlList)
575 bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
576 bHadAnyLeafs = true;
579 else
581 bHadAnyLeafs = true;
582 bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
587 return bControlList && bHadAnyLeafs;
591 static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
593 Reference< XForm > xForm( _rxElement, UNO_QUERY );
594 if ( xForm.is() )
595 return xForm;
597 Reference< XChild > xChild( _rxElement, UNO_QUERY );
598 if ( xChild.is() )
599 return GetForm( xChild->getParent() );
601 return Reference< XForm >();
604 FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
605 :FmXFormShell_BD_BASE( _rMutex )
609 FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
610 :FmXFormShell_BASE(m_aMutex)
611 ,FmXFormShell_CFGBASE("Office.Common/Misc", ConfigItemMode::NONE)
612 ,m_eNavigate( NavigationBarMode_NONE )
613 ,m_nInvalidationEvent( nullptr )
614 ,m_nActivationEvent( nullptr )
615 ,m_pShell( &_rShell )
616 ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
617 ,m_aActiveControllerFeatures( this )
618 ,m_aNavControllerFeatures( this )
619 ,m_eDocumentType( eUnknownDocumentType )
620 ,m_nLockSlotInvalidation( 0 )
621 ,m_bHadPropertyBrowserInDesignMode( false )
622 ,m_bTrackProperties( true )
623 ,m_bUseWizards( true )
624 ,m_bDatabaseBar( false )
625 ,m_bInActivate( false )
626 ,m_bSetFocus( false )
627 ,m_bFilterMode( false )
628 ,m_bChangingDesignMode( false )
629 ,m_bPreparedClose( false )
630 ,m_bFirstActivation( true )
632 m_aMarkTimer.SetTimeout(100);
633 m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
634 m_aMarkTimer.SetDebugName("svx::FmXFormShell m_aMarkTimer");
636 m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
638 // to prevent deletion of this we acquire our refcounter once
639 osl_atomic_increment(&m_refCount);
641 // correct the refcounter
642 osl_atomic_decrement(&m_refCount);
644 // cache the current configuration settings we're interested in
645 implAdjustConfigCache_Lock();
646 // and register for changes on this settings
647 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
648 EnableNotification(aNames);
652 FmXFormShell::~FmXFormShell()
657 Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
659 Reference< css::frame::XModel > xModel;
661 // determine the type of document we live in
664 Reference< css::frame::XController > xController;
665 if ( m_xAttachedFrame.is() )
666 xController = m_xAttachedFrame->getController();
667 if ( xController.is() )
668 xModel = xController->getModel();
670 catch( const Exception& )
672 DBG_UNHANDLED_EXCEPTION("svx");
674 return xModel;
678 bool FmXFormShell::isEnhancedForm_Lock() const
680 return getDocumentType_Lock() == eEnhancedForm;
684 bool FmXFormShell::impl_checkDisposed_Lock() const
686 DBG_TESTSOLARMUTEX();
687 if ( !m_pShell )
689 OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
690 return true;
692 return false;
696 ::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
698 if ( m_eDocumentType != eUnknownDocumentType )
699 return m_eDocumentType;
701 // determine the type of document we live in
702 Reference<css::frame::XModel> xModel = getContextDocument_Lock();
703 if ( xModel.is() )
704 m_eDocumentType = DocumentClassification::classifyDocument( xModel );
705 else
707 OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
708 m_eDocumentType = eTextDocument;
709 // fallback, just to have a defined state
712 return m_eDocumentType;
716 bool FmXFormShell::IsReadonlyDoc_Lock() const
718 if (impl_checkDisposed_Lock())
719 return true;
721 FmFormModel* pModel = m_pShell->GetFormModel();
722 if ( pModel && pModel->GetObjectShell() )
723 return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
724 return true;
727 // EventListener
729 void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
731 SolarMutexGuard g;
733 if (m_xActiveController == e.Source)
735 // the controller will release, then release everything
736 stopListening_Lock();
737 m_xActiveForm = nullptr;
738 m_xActiveController = nullptr;
739 m_xNavigationController = nullptr;
741 m_aActiveControllerFeatures.dispose();
742 m_aNavControllerFeatures.dispose();
744 if ( m_pShell )
745 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
748 if (e.Source == m_xExternalViewController)
750 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
751 OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
752 if (xFormController.is())
753 xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
755 if (m_xExternalViewController.is())
756 m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
758 m_xExternalViewController = nullptr;
759 m_xExternalDisplayedForm = nullptr;
760 m_xExtViewTriggerController = nullptr;
762 InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
767 void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
769 SolarMutexGuard g;
771 if (impl_checkDisposed_Lock())
772 return;
774 if (evt.PropertyName == FM_PROP_ROWCOUNT)
776 // The update following this forces a re-painting of the corresponding
777 // slots. But if I am not in the MainThread of the application (because,
778 // for example, a cursor is counting data sets at the moment and always
779 // gives me this PropertyChanges), this can clash with normal paints in
780 // the MainThread of the application. (Such paints happen, for example,
781 // if one simply places another application over the office and switches
782 // back again).
783 // Therefore the use of the SolarMutex, which safeguards that.
784 comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
785 if (rSolarSafety.tryToAcquire())
787 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
788 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(SID_FM_RECORD_TOTAL);
789 rSolarSafety.release();
791 else
793 // with the following the slot is invalidated asynchron
794 LockSlotInvalidation_Lock(true);
795 InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
796 LockSlotInvalidation_Lock(false);
800 // this may be called from a non-main-thread so invalidate the shell asynchronously
801 LockSlotInvalidation_Lock(true);
802 InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
803 LockSlotInvalidation_Lock(false);
807 void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
809 SolarMutexGuard g;
811 if (impl_checkDisposed_Lock())
812 return;
814 OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
816 if ( m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame() )
818 // unfortunately, SFX requires sal_uInt16
819 ::std::vector< sal_uInt16 > aSlotIds;
820 aSlotIds.reserve( _rFeatures.size() );
821 ::std::copy( _rFeatures.begin(),
822 _rFeatures.end(),
823 ::std::insert_iterator< ::std::vector< sal_uInt16 > >( aSlotIds, aSlotIds.begin() )
826 // furthermore, SFX wants a terminating 0
827 aSlotIds.push_back( 0 );
829 // and, last but not least, SFX wants the ids to be sorted
830 ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
832 sal_uInt16 *pSlotIds = aSlotIds.data();
833 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( pSlotIds );
838 void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
840 SolarMutexGuard g;
842 if (impl_checkDisposed_Lock())
843 return;
845 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
846 m_pTextShell->formActivated( xController );
847 setActiveController_Lock(xController);
851 void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
853 SolarMutexGuard g;
855 if (impl_checkDisposed_Lock())
856 return;
858 Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
859 m_pTextShell->formDeactivated( xController );
863 void FmXFormShell::disposing()
865 SolarMutexGuard g;
867 FmXFormShell_BASE::disposing();
869 if ( m_pShell && !m_pShell->IsDesignMode() )
870 setActiveController_Lock(nullptr, true);
871 // do NOT save the content of the old form (the second parameter tells this)
872 // if we're here, then we expect that PrepareClose has been called, and thus the user
873 // got a chance to commit or reject any changes. So in case we're here and there
874 // are still uncommitted changes, the user explicitly wanted this.
876 m_pTextShell->dispose();
878 m_xAttachedFrame = nullptr;
880 CloseExternalFormViewer_Lock();
882 while ( !m_aLoadingPages.empty() )
884 Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
885 m_aLoadingPages.pop();
889 if (m_nInvalidationEvent)
891 Application::RemoveUserEvent(m_nInvalidationEvent);
892 m_nInvalidationEvent = nullptr;
894 if ( m_nActivationEvent )
896 Application::RemoveUserEvent( m_nActivationEvent );
897 m_nActivationEvent = nullptr;
902 DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
903 // should have been deleted while being disposed
905 m_aMarkTimer.Stop();
908 DisableNotification();
910 RemoveElement_Lock(m_xForms);
911 m_xForms.clear();
913 impl_switchActiveControllerListening_Lock(false);
914 m_xActiveController = nullptr;
915 m_xActiveForm = nullptr;
917 m_pShell = nullptr;
918 m_xNavigationController = nullptr;
919 m_xCurrentForm = nullptr;
920 m_xLastGridFound = nullptr;
921 m_xAttachedFrame = nullptr;
922 m_xExternalViewController = nullptr;
923 m_xExtViewTriggerController = nullptr;
924 m_xExternalDisplayedForm = nullptr;
926 InterfaceBag aEmpty;
927 m_aCurrentSelection.swap( aEmpty );
929 m_aActiveControllerFeatures.dispose();
930 m_aNavControllerFeatures.dispose();
934 void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
936 if (impl_checkDisposed_Lock())
937 return;
939 if ( m_nLockSlotInvalidation )
941 OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
942 InvalidateSlot_Lock(_nId, false);
944 else
946 OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
947 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( _nId, true, true );
948 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update( _nId );
953 void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
955 if (impl_checkDisposed_Lock())
956 return;
958 if (m_nLockSlotInvalidation)
960 sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
961 m_arrInvalidSlots.emplace_back(nId, nFlags );
963 else
964 if (nId)
965 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(nId, true, bWithId);
966 else
967 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
971 void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
973 if (impl_checkDisposed_Lock())
974 return;
976 DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
978 if (bLock)
979 ++m_nLockSlotInvalidation;
980 else if (!--m_nLockSlotInvalidation)
982 // (asynchronously) invalidate everything accumulated during the locked phase
983 if (!m_nInvalidationEvent)
984 m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
989 IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
991 if (impl_checkDisposed_Lock())
992 return;
994 m_nInvalidationEvent = nullptr;
996 for (const auto& rInvalidSlot : m_arrInvalidSlots)
998 if (rInvalidSlot.id)
999 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
1000 else
1001 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
1003 m_arrInvalidSlots.clear();
1007 void FmXFormShell::ForceUpdateSelection_Lock()
1009 if (impl_checkDisposed_Lock())
1010 return;
1012 if (IsSelectionUpdatePending_Lock())
1014 m_aMarkTimer.Stop();
1016 // optionally turn off the invalidation of slots which is implicitly done by SetSelection
1017 LockSlotInvalidation_Lock(true);
1019 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
1021 LockSlotInvalidation_Lock(false);
1025 std::unique_ptr<VclBuilder> FmXFormShell::GetConversionMenu_Lock()
1027 std::unique_ptr<VclBuilder> pBuilder(new VclBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/convertmenu.ui", ""));
1028 VclPtr<PopupMenu> pNewMenu(pBuilder->get_menu("menu"));
1029 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1031 // the corresponding image at it
1032 pNewMenu->SetItemImage(pNewMenu->GetItemId(aConvertSlots[i]), Image(StockImage::Yes, aImgIds[i]));
1034 return pBuilder;
1037 OString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
1039 assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
1041 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1043 if (nSlot == SelObjectSlotMap[i])
1044 return aConvertSlots[i];
1047 return OString();
1050 bool FmXFormShell::isControlConversionSlot(const OString& rIdent)
1052 for (const auto& rConvertSlot : aConvertSlots)
1053 if (rIdent == rConvertSlot)
1054 return true;
1055 return false;
1058 void FmXFormShell::executeControlConversionSlot_Lock(const OString &rIdent)
1060 OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
1061 InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
1062 if ( aSelectedElement == m_aCurrentSelection.end() )
1063 return;
1065 executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
1068 bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, const OString& rIdent)
1070 if (impl_checkDisposed_Lock())
1071 return false;
1073 OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
1074 if ( !_rxObject.is() )
1075 return false;
1077 SdrPage* pPage = m_pShell->GetCurPage();
1078 FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1079 OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
1080 if ( !pFormPage )
1081 return false;
1083 OSL_ENSURE( isSolelySelected_Lock(_rxObject),
1084 "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
1086 for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
1088 if (rIdent == aConvertSlots[lookupSlot])
1090 Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
1092 FmFormObj* pFormObject = nullptr;
1093 SdrObjListIter aPageIter( pFormPage );
1094 while ( aPageIter.IsMore() )
1096 SdrObject* pCurrent = aPageIter.Next();
1097 pFormObject = FmFormObj::GetFormObject( pCurrent );
1098 if ( !pFormObject )
1099 continue;
1101 Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
1102 if ( xCurrentNormalized.get() == xNormalizedObject.get() )
1103 break;
1105 pFormObject = nullptr;
1108 if ( !pFormObject )
1109 return false;
1111 OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
1112 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1113 Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
1114 if (!xNewModel.is())
1115 return false;
1117 Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
1119 // transfer properties
1120 Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
1121 Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
1124 lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
1125 TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
1127 Sequence< css::script::ScriptEventDescriptor> aOldScripts;
1128 Reference< XChild> xChild(xOldModel, UNO_QUERY);
1129 if (xChild.is())
1131 Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
1133 // remember old script events
1134 Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
1135 if (xParent.is() && xEvManager.is())
1137 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1138 if (nIndex>=0 && nIndex<xParent->getCount())
1139 aOldScripts = xEvManager->getScriptEvents(nIndex);
1142 // replace the model within the parent container
1143 Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
1144 if (xIndexParent.is())
1146 // the form container works with FormComponents
1147 Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
1148 DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
1149 Any aNewModel(makeAny(xComponent));
1153 sal_Int32 nIndex = getElementPos(xParent, xOldModel);
1154 if (nIndex>=0 && nIndex<xParent->getCount())
1155 xIndexParent->replaceByIndex(nIndex, aNewModel);
1156 else
1158 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1159 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1160 if (xNewComponent.is())
1161 xNewComponent->dispose();
1162 return false;
1165 catch(Exception&)
1167 OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
1168 Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
1169 if (xNewComponent.is())
1170 xNewComponent->dispose();
1171 return false;
1177 // special handling for the LabelControl-property : can only be set when the model is placed
1178 // within the forms hierarchy
1179 if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
1183 xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
1185 catch(Exception&)
1191 // set new model
1192 pFormObject->SetChanged();
1193 pFormObject->SetUnoControlModel(xNewModel);
1195 // transfer script events
1196 // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
1197 if (aOldScripts.hasElements())
1199 // find the control for the model
1200 Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
1202 Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
1204 Reference< XControl> xControl;
1205 auto pControl = std::find_if(aControls.begin(), aControls.end(),
1206 [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
1207 if (pControl != aControls.end())
1208 xControl = *pControl;
1209 TransferEventScripts(xNewModel, xControl, aOldScripts);
1212 // transfer value bindings, if possible
1214 Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
1215 Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
1216 if ( xOldBindable.is() )
1220 if ( xNewBindable.is() )
1221 xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
1222 xOldBindable->setValueBinding( nullptr );
1224 catch(const Exception&)
1226 DBG_UNHANDLED_EXCEPTION("svx");
1230 // same for list entry sources
1232 Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
1233 Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
1234 if ( xOldSink.is() )
1238 if ( xNewSink.is() )
1239 xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
1240 xOldSink->setListEntrySource( nullptr );
1242 catch(const Exception&)
1244 DBG_UNHANDLED_EXCEPTION("svx");
1249 // create an undo action
1250 FmFormModel* pModel = m_pShell->GetFormModel();
1251 DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
1252 if (pModel && pModel->IsUndoEnabled() )
1254 pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
1256 else
1258 FmUndoModelReplaceAction::DisposeElement( xOldModel );
1261 return true;
1264 return false;
1267 bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(const OString& rIdent)
1269 if ( m_aCurrentSelection.empty() )
1270 return false;
1272 InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
1273 Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
1274 if ( !xElementInfo.is() )
1275 // no service info -> cannot determine this
1276 return false;
1278 if ( ++aCheck != m_aCurrentSelection.end() )
1279 // more than one element
1280 return false;
1282 if ( Reference< XForm >::query( xElementInfo ).is() )
1283 // it's a form
1284 return false;
1286 sal_Int16 nObjectType = getControlTypeByObject( xElementInfo );
1288 if ( ( OBJ_FM_HIDDEN == nObjectType )
1289 || ( OBJ_FM_CONTROL == nObjectType )
1290 || ( OBJ_FM_GRID == nObjectType )
1292 return false; // those types cannot be converted
1294 DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
1295 "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
1297 for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
1298 if (rIdent == aConvertSlots[i])
1299 return nObjectTypes[i] != nObjectType;
1301 return true; // all other slots: assume "yes"
1304 void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(Menu& rMenu)
1306 for (sal_uInt16 i = 0; i < rMenu.GetItemCount(); ++i)
1308 // the context is already of a type that corresponds to the entry -> disable
1309 const sal_uInt16 nId = rMenu.GetItemId(i);
1310 rMenu.EnableItem(nId, canConvertCurrentSelectionToControl_Lock(rMenu.GetItemIdent(nId)));
1314 void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
1316 if (impl_checkDisposed_Lock())
1317 return;
1319 Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
1320 if (xControlModels.is())
1322 for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
1324 Reference< XPropertySet> xModelSet;
1325 xControlModels->getByIndex(i) >>= xModelSet;
1326 if (!xModelSet.is())
1327 continue;
1329 if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
1330 continue;
1331 sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
1332 if (FormComponentType::GRIDCONTROL != nClassId)
1333 continue;
1335 if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
1336 continue;
1338 switch (nSync)
1340 case LoopGridsSync::DISABLE_SYNC:
1342 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
1344 break;
1345 case LoopGridsSync::FORCE_SYNC:
1347 Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
1348 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1349 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
1351 break;
1352 case LoopGridsSync::ENABLE_SYNC:
1354 xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
1356 break;
1359 if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
1361 xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
1362 Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
1363 if (xModelPropState.is())
1364 xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
1365 else
1366 xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
1373 Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
1375 if (impl_checkDisposed_Lock())
1376 return nullptr;
1378 SdrPageView* pPageView = nullptr;
1379 if ( m_pShell && m_pShell->GetFormView() )
1380 pPageView = m_pShell->GetFormView()->GetSdrPageView();
1382 Reference< XControlContainer> xControlContainer;
1383 if ( pPageView )
1384 xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
1386 return xControlContainer;
1390 void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
1392 if (impl_checkDisposed_Lock())
1393 return;
1395 OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
1396 if ( !_rxForForm.is() )
1397 return;
1401 Reference< XWindow > xParentWindow;
1402 if ( m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame() )
1403 xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame()->GetWindow() );
1405 Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
1406 comphelper::getProcessComponentContext(),
1407 _rxForForm, getControlContainerForView_Lock(), xParentWindow
1410 xDialog->execute();
1412 catch( const Exception& )
1414 OSL_FAIL( "FmXFormShell::ExecuteTabOrderDialog: caught an exception!" );
1419 void FmXFormShell::ExecuteSearch_Lock()
1421 if (impl_checkDisposed_Lock())
1422 return;
1424 // a collection of all (logical) forms
1425 FmFormArray aEmpty;
1426 m_aSearchForms.swap( aEmpty );
1427 ::std::vector< OUString > aContextNames;
1428 impl_collectFormSearchContexts_nothrow_Lock(
1429 m_pShell->GetCurPage()->GetForms(), OUString(),
1430 m_aSearchForms, aContextNames);
1432 if ( m_aSearchForms.size() != aContextNames.size() )
1434 SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
1435 return;
1438 // filter out the forms which do not contain valid controls at all
1440 FmFormArray aValidForms;
1441 ::std::vector< OUString > aValidContexts;
1442 FmFormArray::const_iterator form = m_aSearchForms.begin();
1443 ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
1444 for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
1446 FmSearchContext aTestContext;
1447 aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
1448 sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
1449 if ( nValidControls > 0 )
1451 aValidForms.push_back( *form );
1452 aValidContexts.push_back( *contextName );
1456 m_aSearchForms.swap( aValidForms );
1457 aContextNames.swap( aValidContexts );
1460 if (m_aSearchForms.empty() )
1462 // there are no controls that meet all the conditions for a search
1463 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
1464 VclMessageType::Warning, VclButtonsType::Ok,
1465 SvxResId(RID_STR_NODATACONTROLS)));
1466 xBox->run();
1467 return;
1470 // now I need another 'initial context'
1471 sal_Int16 nInitialContext = 0;
1472 Reference<XForm> xActiveForm(getActiveForm_Lock());
1473 for ( size_t i=0; i<m_aSearchForms.size(); ++i )
1475 if (m_aSearchForms.at(i) == xActiveForm)
1477 nInitialContext = static_cast<sal_Int16>(i);
1478 break;
1482 // If the dialog should initially offer the text of the active control,
1483 // this must have an XTextComponent interface. An addition, this makes
1484 // sense only if the current field is also bound to a table (or whatever) field.
1485 OUString strActiveField;
1486 OUString strInitialText;
1487 // ... this I get from my FormController
1488 DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
1489 Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
1490 if (xActiveControl.is())
1492 // the control can tell me its model ...
1493 Reference< XControlModel> xActiveModel( xActiveControl->getModel());
1494 DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
1496 // I ask the model for the ControlSource property ...
1497 Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
1498 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
1500 Reference< XPropertySet> xField;
1501 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
1502 if (xField.is()) // (only when the thing is really bound)
1504 // and the control itself for a TextComponent interface (so that I can pick up the text there)
1505 Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
1506 if (xText.is())
1508 strActiveField = getLabelName(xProperties);
1509 strInitialText = xText->getText();
1513 else
1515 // the control itself has no ControlSource, but maybe it is a GridControl
1516 Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
1517 if (xGrid.is())
1519 // for strActiveField I need the ControlSource of the column,
1520 // for that the columns container, for that the GridPeer
1521 Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
1522 Reference< XIndexAccess> xColumns;
1523 if (xGridPeer.is())
1524 xColumns = xGridPeer->getColumns();
1526 sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
1527 sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
1528 Reference< XPropertySet> xCurrentCol;
1529 if(xColumns.is())
1530 xColumns->getByIndex(nModelCol) >>= xCurrentCol;
1531 if (xCurrentCol.is())
1532 strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
1534 // the text of the current column
1535 Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
1536 Reference< XInterface> xCurControl;
1537 xColControls->getByIndex(nViewCol) >>= xCurControl;
1538 OUString sInitialText;
1539 if (IsSearchableControl(xCurControl, &sInitialText))
1540 strInitialText = sInitialText;
1545 // taking care of possible GridControls that I know
1546 LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
1548 // Now I am ready for the dialogue.
1549 // When the potential deadlocks caused by the use of the solar mutex in
1550 // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
1551 // placed here, because the search in a separate thread is nevertheless
1552 // somewhat more fluid. Should be, however, somehow made dependent of the
1553 // underlying cursor. DAO for example is not thread-safe.
1554 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1555 ScopedVclPtr<AbstractFmSearchDialog> pDialog(
1556 pFact->CreateFmSearchDialog(
1557 m_pShell->GetViewShell()->GetViewFrame()->GetWindow().GetFrameWeld(),
1558 strInitialText, aContextNames, nInitialContext,
1559 LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
1560 pDialog->SetActiveField( strActiveField );
1561 pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
1562 pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
1563 pDialog->Execute();
1564 pDialog.disposeAndClear();
1566 // restore GridControls again
1567 LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
1569 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
1570 // because I marked controls in OnFoundData (if I was there)
1574 bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
1576 if (impl_checkDisposed_Lock())
1577 return false;
1579 if (m_pShell->IsDesignMode())
1580 // in the design mode (without active controls) the main document is to take care of it
1581 return false;
1583 Reference<XForm> xForm(getActiveForm_Lock());
1584 if (!xForm.is())
1585 // no current form (in particular no current control) -> the main document is to take care
1586 return false;
1588 Reference< XRowSet> xDB(xForm, UNO_QUERY);
1589 DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
1591 Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
1592 if (xSupplier.is())
1594 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1595 if (xSet.is())
1599 Any aVal( xSet->getPropertyValue("TwoDigitDateStart") );
1600 aVal >>= n;
1601 return true;
1603 catch(Exception&)
1609 return false;
1613 void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
1615 if (impl_checkDisposed_Lock())
1616 return;
1618 Reference<XForm> xActiveForm(getActiveForm_Lock());
1619 Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
1620 if ( xActiveRowSet.is() )
1622 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
1623 if (xSupplier.is())
1625 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1626 if (xSet.is())
1630 xSet->setPropertyValue("TwoDigitDateStart", makeAny<sal_uInt16>(n));
1632 catch(Exception&)
1634 OSL_FAIL("FmXFormShell::SetY2KState: Exception occurred!");
1638 return;
1642 // no active form found -> iterate through all current forms
1643 Reference< XIndexAccess> xCurrentForms( m_xForms);
1644 if (!xCurrentForms.is())
1645 { // in the alive mode, my forms are not set, but the ones on the page are
1646 if (m_pShell->GetCurPage())
1647 xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
1649 if (!xCurrentForms.is())
1650 return;
1652 ::comphelper::IndexAccessIterator aIter(xCurrentForms);
1653 Reference< XInterface> xCurrentElement( aIter.Next());
1654 while (xCurrentElement.is())
1656 // is the current element a DatabaseForm?
1657 Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
1658 if ( xElementAsRowSet.is() )
1660 Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
1661 if (!xSupplier.is())
1662 continue;
1664 Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
1665 if (xSet.is())
1669 xSet->setPropertyValue("TwoDigitDateStart", makeAny<sal_uInt16>(n));
1671 catch(Exception&)
1673 OSL_FAIL("FmXFormShell::SetY2KState: Exception occurred!");
1678 xCurrentElement = aIter.Next();
1683 void FmXFormShell::CloseExternalFormViewer_Lock()
1685 if (impl_checkDisposed_Lock())
1686 return;
1688 if (!m_xExternalViewController.is())
1689 return;
1691 Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
1692 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
1693 if (!xCommLink.is())
1694 return;
1696 xExternalViewFrame->setComponent(nullptr,nullptr);
1697 ::comphelper::disposeComponent(xExternalViewFrame);
1698 m_xExternalViewController = nullptr;
1699 m_xExtViewTriggerController = nullptr;
1700 m_xExternalDisplayedForm = nullptr;
1704 Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _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 m_xExternalDisplayedForm;
1715 return _xForm;
1719 Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
1721 if (impl_checkDisposed_Lock())
1722 return nullptr;
1724 Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
1725 if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
1727 DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
1728 return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
1730 return _xForm;
1734 namespace
1736 bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
1738 return ( _nWhich == SID_FM_RECORD_FIRST )
1739 || ( _nWhich == SID_FM_RECORD_PREV )
1740 || ( _nWhich == SID_FM_RECORD_NEXT )
1741 || ( _nWhich == SID_FM_RECORD_LAST )
1742 || ( _nWhich == SID_FM_RECORD_NEW );
1747 bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState )
1749 const svx::ControllerFeatures& rController =
1750 lcl_isNavigationRelevant( _nSlot )
1751 ? getNavControllerFeatures_Lock()
1752 : getActiveControllerFeatures_Lock();
1754 if ( !_pCompleteState )
1755 return rController->isEnabled( _nSlot );
1757 rController->getState( _nSlot, *_pCompleteState );
1758 return _pCompleteState->Enabled;
1762 void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
1764 const svx::ControllerFeatures& rController =
1765 lcl_isNavigationRelevant( _nSlot )
1766 ? getNavControllerFeatures_Lock()
1767 : getActiveControllerFeatures_Lock();
1769 rController->execute( _nSlot );
1771 if ( _nSlot == SID_FM_RECORD_UNDO )
1773 // if we're doing an UNDO, *and* if the affected form is the form which we also display
1774 // as external view, then we need to reset the controls of the external form, too
1775 if (getInternalForm_Lock(getActiveForm_Lock()) == m_xExternalDisplayedForm)
1777 Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
1778 if ( xContainer.is() )
1780 Reference< XReset > xReset;
1781 for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
1783 if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
1785 // no resets on sub forms
1786 Reference< XForm > xAsForm( xReset, UNO_QUERY );
1787 if ( !xAsForm.is() )
1788 xReset->reset();
1797 void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
1799 if ( !m_xActiveController.is() )
1800 return;
1802 if ( _bListen )
1803 m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
1804 else
1805 m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
1809 void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
1811 if (impl_checkDisposed_Lock())
1812 return;
1814 if (m_bChangingDesignMode)
1815 return;
1816 DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
1818 // if the routine has been called a second time,
1819 // the focus should no longer be transferred
1820 if (m_bInActivate)
1822 m_bSetFocus = xController != m_xActiveController;
1823 return;
1826 if (xController == m_xActiveController)
1827 return;
1829 // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
1830 Reference< XResultSet> xNavigationForm;
1831 if (m_xNavigationController.is())
1832 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1834 m_bInActivate = true;
1836 // check if the 2 controllers serve different forms
1837 Reference< XResultSet> xOldForm;
1838 if (m_xActiveController.is())
1839 xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
1840 Reference< XResultSet> xNewForm;
1841 if (xController.is())
1842 xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
1843 xOldForm = getInternalForm_Lock(xOldForm);
1844 xNewForm = getInternalForm_Lock(xNewForm);
1846 bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
1847 bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
1848 // we save the content of the old form if we move to a new form, and saving old content is allowed
1850 if ( m_xActiveController.is() && bNeedSave )
1852 // save content on change of the controller; a commit has already been executed
1853 if ( m_aActiveControllerFeatures->commitCurrentControl() )
1855 m_bSetFocus = true;
1856 if ( m_aActiveControllerFeatures->isModifiedRow() )
1858 bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
1859 bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
1860 if ( !bResult && m_bSetFocus )
1862 // if we couldn't save the current record, set the focus back to the
1863 // current control
1864 Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
1865 if ( xWindow.is() )
1866 xWindow->setFocus();
1867 m_bInActivate = false;
1868 return;
1870 else if ( bResult && bIsNew )
1872 Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor().get() );
1873 if ( xCursor.is() )
1875 DO_SAFE( xCursor->last(); );
1882 stopListening_Lock();
1884 impl_switchActiveControllerListening_Lock(false);
1886 m_aActiveControllerFeatures.dispose();
1887 m_xActiveController = xController;
1888 if ( m_xActiveController.is() )
1889 m_aActiveControllerFeatures.assign( m_xActiveController );
1891 impl_switchActiveControllerListening_Lock(true);
1893 if ( m_xActiveController.is() )
1894 m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
1895 else
1896 m_xActiveForm = nullptr;
1898 startListening_Lock();
1900 // activate all dispatchers belonging to form of the new navigation controller
1901 xNavigationForm = nullptr;
1902 if (m_xNavigationController.is())
1903 xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
1905 m_bInActivate = false;
1907 m_pShell->UIFeatureChanged();
1908 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
1910 InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
1914 void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
1916 _rSelection = m_aCurrentSelection;
1920 bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
1922 m_aLastKnownMarkedControls.clear();
1924 if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
1925 collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
1927 return setCurrentSelection_Lock(m_aLastKnownMarkedControls);
1931 bool FmXFormShell::selectLastMarkedControls_Lock()
1933 return setCurrentSelection_Lock(m_aLastKnownMarkedControls);
1937 bool FmXFormShell::setCurrentSelection_Lock( const InterfaceBag& _rSelection )
1939 if (impl_checkDisposed_Lock())
1940 return false;
1942 DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
1944 if ( _rSelection.empty() && m_aCurrentSelection.empty() )
1945 // nothing to do
1946 return false;
1948 if ( _rSelection.size() == m_aCurrentSelection.size() )
1950 InterfaceBag::const_iterator aNew = _rSelection.begin();
1951 InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
1952 for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
1954 OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
1955 OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
1957 if ( aNew->get() != aOld->get() )
1958 break;
1961 if ( aNew == _rSelection.end() )
1962 // both bags equal
1963 return false;
1966 // the following is some strange code to ensure that when you have two grid controls in a document,
1967 // only one of them can have a selected column.
1968 // TODO: this should happen elsewhere, but not here - shouldn't it?
1969 if ( !m_aCurrentSelection.empty() )
1971 Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
1972 Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
1974 // is there nothing to be selected, or the parents differ, and the parent of the current object
1975 // is a selection supplier, then deselect
1976 if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
1978 Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
1979 if ( xSel.is() )
1980 xSel->select( Any() );
1984 m_aCurrentSelection = _rSelection;
1986 // determine the form which all the selected objects belong to, if any
1987 Reference< XForm > xNewCurrentForm;
1988 for (const auto& rpSelection : m_aCurrentSelection)
1990 Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
1991 OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
1993 if ( !xNewCurrentForm.is() )
1994 { // the first form we encountered
1995 xNewCurrentForm = xThisRoundsForm;
1997 else if ( xNewCurrentForm != xThisRoundsForm )
1998 { // different forms -> no "current form" at all
1999 xNewCurrentForm.clear();
2000 break;
2004 if ( !m_aCurrentSelection.empty() )
2005 impl_updateCurrentForm_Lock(xNewCurrentForm);
2007 // ensure some slots are updated
2008 for (sal_Int16 i : SelObjectSlotMap)
2009 InvalidateSlot_Lock(i, false);
2011 return true;
2015 bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
2017 return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
2021 void FmXFormShell::forgetCurrentForm_Lock()
2023 if ( !m_xCurrentForm.is() )
2024 return;
2026 // reset ...
2027 impl_updateCurrentForm_Lock(nullptr);
2029 // ... and try finding a new current form
2030 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
2031 impl_defaultCurrentForm_nothrow_Lock();
2035 void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
2037 if (impl_checkDisposed_Lock())
2038 return;
2040 m_xCurrentForm = _rxNewCurForm;
2042 // propagate to the FormPage(Impl)
2043 FmFormPage* pPage = m_pShell->GetCurPage();
2044 if ( pPage )
2045 pPage->GetImpl().setCurForm( m_xCurrentForm );
2047 // ensure the UI which depends on the current form is up-to-date
2048 for (sal_Int16 i : DlgSlotMap)
2049 InvalidateSlot_Lock(i, false);
2053 void FmXFormShell::startListening_Lock()
2055 if (impl_checkDisposed_Lock())
2056 return;
2058 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2059 if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
2061 Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
2062 if (xActiveFormSet.is())
2064 // if there is a data source, then build the listener
2065 // TODO: this is strange - shouldn't this depend on a isLoaded instead of
2066 // a "has command value"? Finally, the command value only means that it was
2067 // intended to be loaded, not that it actually *is* loaded
2068 OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
2069 if (!aSource.isEmpty())
2071 m_bDatabaseBar = true;
2073 xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2075 switch (m_eNavigate)
2077 case NavigationBarMode_PARENT:
2079 // search for the controller via which navigation is possible
2080 Reference< XChild> xChild = m_xActiveController;
2081 Reference< runtime::XFormController > xParent;
2082 while (xChild.is())
2084 xChild.set(xChild->getParent(), UNO_QUERY);
2085 xParent.set(xChild, UNO_QUERY);
2086 Reference< XPropertySet> xParentSet;
2087 if (xParent.is())
2088 xParentSet.set(xParent->getModel(), UNO_QUERY);
2089 if (xParentSet.is())
2091 xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
2092 if (m_eNavigate == NavigationBarMode_CURRENT)
2093 break;
2096 m_xNavigationController = xParent;
2098 break;
2100 case NavigationBarMode_CURRENT:
2101 m_xNavigationController = m_xActiveController;
2102 break;
2104 default:
2105 m_xNavigationController = nullptr;
2106 m_bDatabaseBar = false;
2109 m_aNavControllerFeatures.dispose();
2110 if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
2111 m_aNavControllerFeatures.assign( m_xNavigationController );
2113 // because of RecordCount, listen at the controller which controls the navigation
2114 Reference< XPropertySet> xNavigationSet;
2115 if (m_xNavigationController.is())
2117 xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
2118 if (xNavigationSet.is())
2119 xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
2121 return;
2126 m_eNavigate = NavigationBarMode_NONE;
2127 m_bDatabaseBar = false;
2128 m_xNavigationController = nullptr;
2132 void FmXFormShell::stopListening_Lock()
2134 if (impl_checkDisposed_Lock())
2135 return;
2137 Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
2138 if ( xDatabaseForm.is() )
2140 if (m_xNavigationController.is())
2142 Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
2143 if (xSet.is())
2144 xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2149 m_bDatabaseBar = false;
2150 m_eNavigate = NavigationBarMode_NONE;
2151 m_xNavigationController = nullptr;
2155 void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
2157 if (impl_checkDisposed_Lock())
2158 return;
2160 // if the window is already visible, only update the state
2161 bool bHasChild = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_PROPERTIES );
2162 if ( bHasChild && bShow )
2163 UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
2165 // else toggle state
2166 else
2167 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2169 InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
2170 InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
2174 IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
2176 if (impl_checkDisposed_Lock())
2177 return;
2179 DBG_ASSERT((rfriWhere.nContext >= 0) && (rfriWhere.nContext < static_cast<sal_Int16>(m_aSearchForms.size())),
2180 "FmXFormShell::OnFoundData : invalid context!");
2181 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2182 DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
2184 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2185 if (!xCursor.is())
2186 return; // what should I do there?
2188 // to the record
2191 xCursor->moveToBookmark(rfriWhere.aPosition);
2193 catch(const SQLException&)
2195 OSL_FAIL("Can position on bookmark!");
2198 LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
2200 // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
2201 SAL_WARN_IF(static_cast<size_t>(rfriWhere.nFieldPos) >=
2202 m_arrSearchedControls.size(),
2203 "svx.form", "FmXFormShell::OnFoundData : invalid index!");
2204 SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
2206 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2207 m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
2209 FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
2210 Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
2211 DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
2212 if ( !xControlModel.is() )
2213 return;
2215 // disable the permanent cursor for the last grid we found a record
2216 if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
2218 Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
2219 xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, makeAny( false ) );
2220 Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
2221 if (xOldSetState.is())
2222 xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
2223 else
2224 xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
2227 // if the field is in a GridControl, I have to additionally go into the corresponding column there
2228 sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
2229 if (nGridColumn != -1)
2230 { // unfortunately, I have to first get the control again
2231 Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
2232 Reference< XGrid> xGrid(xControl, UNO_QUERY);
2233 DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
2234 // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
2236 // enable a permanent cursor for the grid so we can see the found text
2237 Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
2238 DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
2239 xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, makeAny( true ) );
2240 xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, makeAny( COL_LIGHTRED ) );
2241 m_xLastGridFound = xControlModel;
2243 if ( xGrid.is() )
2244 xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
2247 // As the cursor has been repositioned, I have (in positioned) invalidated
2248 // my form bar slots. But that does not take effect here unfortunately, as
2249 // generally the (modal) search dialog is of course at the top ... So, force ...
2250 sal_uInt16 nPos = 0;
2251 while (DatabaseSlotMap[nPos])
2252 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(DatabaseSlotMap[nPos++]);
2253 // unfortunately the update goes against the invalidate with only individual slots
2257 IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
2259 if (impl_checkDisposed_Lock())
2260 return;
2262 DBG_ASSERT((rfriWhere.nContext >= 0) && (rfriWhere.nContext < static_cast<sal_Int16>(m_aSearchForms.size())),
2263 "FmXFormShell::OnCanceledNotFound : invalid context!");
2264 Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
2265 DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
2267 Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
2268 if (!xCursor.is())
2269 return; // what should I do there?
2271 // to the record
2274 xCursor->moveToBookmark(rfriWhere.aPosition);
2276 catch(const SQLException&)
2278 OSL_FAIL("Can position on bookmark!");
2282 m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
2286 IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
2288 if (impl_checkDisposed_Lock())
2289 return 0;
2291 DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
2292 Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
2293 DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
2295 Reference< XResultSet> xIter(xForm, UNO_QUERY);
2296 DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
2299 // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
2300 OUString strFieldList, sFieldDisplayNames;
2301 m_arrSearchedControls.clear();
2302 m_arrRelativeGridColumn.clear();
2304 // small problem: To mark found fields, I need SdrObjects. To determine which controls
2305 // to include in the search, I need Controls (that is, XControl interfaces). So I have
2306 // to iterate over one of them and get the other in some way. Unfortunately, there is
2307 // no direct connexion between the two worlds (except from a GetUnoControl to a
2308 // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
2309 // However I can get to the Model from the Control and also from the SdrObject, and in
2310 // this way the assignment SdrObject<->Control is possible with a double loop.
2311 // The alternative to this (ugly but certainly not entirely fixable) solution would be
2312 // to renounce the caching of the SdrObjects, which would lead to significant extra
2313 // work in OnFoundData (since there I'd have to get the SdrObject first thing every
2314 // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
2315 // do that here.
2317 Reference< XNameAccess> xValidFormFields;
2318 Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
2319 DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
2320 if (xSupplyCols.is())
2321 xValidFormFields = xSupplyCols->getColumns();
2322 DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
2324 // current Page/Controller
2325 FmFormPage* pCurrentPage = m_pShell->GetCurPage();
2326 assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
2327 // Search all SdrControls of this page...
2328 OUString sControlSource, aName;
2330 SdrObjListIter aPageIter( pCurrentPage );
2331 while ( aPageIter.IsMore() )
2333 SdrObject* pCurrent = aPageIter.Next();
2334 FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
2335 // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
2337 if ( !pFormObject )
2338 continue;
2340 // the current object's model, in different tastes
2341 Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
2342 Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
2343 DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
2344 if ( !xCurrentFormComponent.is() )
2345 continue;
2347 // does the component belong to the form which we're interested in?
2348 if ( xCurrentFormComponent->getParent() != xForm )
2349 continue;
2351 // ... ask for the ControlSource property
2352 SearchableControlIterator iter( xCurrentFormComponent );
2353 Reference< XControl> xControl;
2354 // the control that has model xControlModel
2355 // (the following while can be passed through several times, without the Control
2356 // being modified, so I don't have to search every time from scratch)
2358 Reference< XInterface > xSearchable( iter.Next() );
2359 while ( xSearchable.is() )
2361 sControlSource = iter.getCurrentValue();
2362 if ( sControlSource.isEmpty() )
2364 // the current element has no ControlSource, so it is a GridControl (that
2365 // is the only thing that still permits the SearchableControlIteratore)
2366 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2367 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2369 Reference< XGridPeer> xGridPeer;
2370 if ( xControl.is() )
2371 xGridPeer.set( xControl->getPeer(), UNO_QUERY );
2374 if (!xGridPeer.is())
2375 break;
2377 Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
2378 if (!xPeerContainer.is())
2379 break;
2381 Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
2382 DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
2383 // the case 'no columns' should be indicated with an empty container, I think ...
2384 DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
2386 Reference< XInterface> xCurrentColumn;
2387 for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
2389 xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
2390 if (!xCurrentColumn.is())
2391 continue;
2393 // can we use this column control for searching ?
2394 if (!IsSearchableControl(xCurrentColumn))
2395 continue;
2397 sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
2398 Reference< XPropertySet> xCurrentColModel;
2399 xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
2400 aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
2401 // the cursor has a field matching the control source ?
2402 if (xValidFormFields->hasByName(aName))
2404 strFieldList += aName + ";";
2406 sFieldDisplayNames +=
2407 ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
2408 ";";
2410 rfmscContextInfo.arrFields.push_back(xCurrentColumn);
2412 // and the SdrOject to the Field
2413 m_arrSearchedControls.push_back(pCurrent);
2414 // the number of the column
2415 m_arrRelativeGridColumn.push_back(nViewPos);
2418 } while (false);
2420 else
2422 if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
2424 // now I need the Control to SdrObject
2425 if (!xControl.is())
2427 xControl = impl_getControl_Lock(xControlModel, *pFormObject);
2428 DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
2431 if (IsSearchableControl(xControl))
2433 // all tests passed -> take along in the list
2434 strFieldList += sControlSource + ";";
2436 // the label which should appear for the control :
2437 sFieldDisplayNames +=
2438 getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
2439 ";";
2441 // mark the SdrObject (accelerates the treatment in OnFoundData)
2442 m_arrSearchedControls.push_back(pCurrent);
2444 // the number of the column (here a dummy, since it is only interesting for GridControls)
2445 m_arrRelativeGridColumn.push_back(-1);
2447 // and for the formatted search...
2448 rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
2453 xSearchable = iter.Next();
2457 strFieldList = comphelper::string::stripEnd(strFieldList, ';');
2458 sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
2460 if (rfmscContextInfo.arrFields.empty())
2462 rfmscContextInfo.arrFields.clear();
2463 rfmscContextInfo.xCursor = nullptr;
2464 rfmscContextInfo.strUsedFields.clear();
2465 return 0L;
2468 rfmscContextInfo.xCursor = xIter;
2469 rfmscContextInfo.strUsedFields = strFieldList;
2470 rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
2472 // 66463 - 31.05.99 - FS
2473 // when the cursor is a non-STANDARD RecordMode, set it back
2474 Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
2475 Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
2476 if (xUpdateCursor.is() && xCursorSet.is())
2478 if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
2479 xUpdateCursor->moveToCurrentRow();
2480 else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
2481 xUpdateCursor->cancelRowUpdates();
2484 return rfmscContextInfo.arrFields.size();
2487 // XContainerListener
2489 void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
2491 SolarMutexGuard g;
2493 if (impl_checkDisposed_Lock())
2494 return;
2496 // new object to listen to
2497 Reference< XInterface> xTemp;
2498 evt.Element >>= xTemp;
2499 AddElement_Lock(xTemp);
2501 m_pShell->DetermineForms(true);
2505 void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
2507 SolarMutexGuard g;
2509 if (impl_checkDisposed_Lock() )
2510 return;
2512 Reference< XInterface> xTemp;
2513 evt.ReplacedElement >>= xTemp;
2514 RemoveElement_Lock(xTemp);
2515 evt.Element >>= xTemp;
2516 AddElement_Lock(xTemp);
2520 void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
2522 SolarMutexGuard g;
2524 if (impl_checkDisposed_Lock())
2525 return;
2527 Reference< XInterface> xTemp;
2528 evt.Element >>= xTemp;
2529 RemoveElement_Lock(xTemp);
2531 m_pShell->DetermineForms(true);
2535 void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
2537 if (impl_checkDisposed_Lock())
2538 return;
2540 Reference< XIndexAccess > xForms;
2542 FmFormPage* pPage = m_pShell->GetCurPage();
2543 if ( pPage && m_pShell->m_bDesignMode )
2544 xForms = pPage->GetForms( false );
2546 if ( m_xForms != xForms )
2548 RemoveElement_Lock( m_xForms );
2549 m_xForms = xForms;
2550 AddElement_Lock(m_xForms);
2553 SolarMutexGuard g;
2554 m_pShell->DetermineForms( _bInvalidate );
2558 void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
2560 if (impl_checkDisposed_Lock())
2561 return;
2562 impl_AddElement_nothrow(_xElement);
2565 void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
2567 // listen at the container
2568 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2569 if (xContainer.is())
2571 const sal_uInt32 nCount = xContainer->getCount();
2572 Reference< XInterface> xElement;
2573 for (sal_uInt32 i = 0; i < nCount; ++i)
2575 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2576 impl_AddElement_nothrow(xElement);
2579 const Reference< XContainer> xCont(Element, UNO_QUERY);
2580 if (xCont.is())
2581 xCont->addContainerListener(this);
2584 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2585 if (xSelSupplier.is())
2586 xSelSupplier->addSelectionChangeListener(this);
2590 void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
2592 if (impl_checkDisposed_Lock())
2593 return;
2594 impl_RemoveElement_nothrow_Lock(Element);
2597 void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
2599 const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
2600 if (xSelSupplier.is())
2601 xSelSupplier->removeSelectionChangeListener(this);
2603 // remove connection to children
2604 const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
2605 if (xContainer.is())
2607 const Reference< XContainer> xCont(Element, UNO_QUERY);
2608 if (xCont.is())
2609 xCont->removeContainerListener(this);
2611 const sal_uInt32 nCount = xContainer->getCount();
2612 Reference< XInterface> xElement;
2613 for (sal_uInt32 i = 0; i < nCount; i++)
2615 xElement.set(xContainer->getByIndex(i),UNO_QUERY);
2616 impl_RemoveElement_nothrow_Lock(xElement);
2620 InterfaceBag::iterator wasSelectedPos = m_aCurrentSelection.find( Element );
2621 if ( wasSelectedPos != m_aCurrentSelection.end() )
2622 m_aCurrentSelection.erase( wasSelectedPos );
2626 void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
2628 SolarMutexGuard g;
2630 if (impl_checkDisposed_Lock())
2631 return;
2633 Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
2634 Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
2635 // a selection was removed, this can only be done by the shell
2636 if ( !xSelObj.is() )
2637 return;
2639 EnableTrackProperties_Lock(false);
2641 bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
2642 Reference< XForm > xNewForm( GetForm( rEvent.Source ) );
2644 InterfaceBag aNewSelection;
2645 aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
2647 if (setCurrentSelection_Lock(aNewSelection) && IsPropBrwOpen_Lock())
2648 ShowSelectionProperties_Lock(true);
2650 EnableTrackProperties_Lock(true);
2652 if ( bMarkChanged )
2653 m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
2657 IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
2659 if (impl_checkDisposed_Lock())
2660 return;
2662 if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
2663 SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
2667 void FmXFormShell::SetSelectionDelayed_Lock()
2669 if (impl_checkDisposed_Lock())
2670 return;
2672 if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
2673 m_aMarkTimer.Start();
2677 void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
2679 if (impl_checkDisposed_Lock())
2680 return;
2682 DetermineSelection_Lock(rMarkList);
2683 m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
2687 void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
2689 if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
2690 ShowSelectionProperties_Lock(true);
2694 bool FmXFormShell::IsPropBrwOpen_Lock() const
2696 if (impl_checkDisposed_Lock())
2697 return false;
2699 return m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()
2700 && m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
2704 class FmXFormShell::SuspendPropertyTracking
2706 private:
2707 FmXFormShell& m_rShell;
2708 bool m_bEnabled;
2710 public:
2711 explicit SuspendPropertyTracking( FmXFormShell& _rShell )
2712 :m_rShell( _rShell )
2713 ,m_bEnabled( false )
2715 if (m_rShell.IsTrackPropertiesEnabled_Lock())
2717 m_rShell.EnableTrackProperties_Lock(false);
2718 m_bEnabled = true;
2722 ~SuspendPropertyTracking( )
2724 if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
2725 m_rShell.EnableTrackProperties_Lock(true);
2730 void FmXFormShell::SetDesignMode_Lock(bool bDesign)
2732 if (impl_checkDisposed_Lock())
2733 return;
2735 DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
2736 m_bChangingDesignMode = true;
2738 // 67506 - 15.07.99 - FS
2739 // if we're switching off the design mode we have to force the property browser to be closed
2740 // so it can commit it's changes _before_ we load the forms
2741 if (!bDesign)
2743 m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
2744 if (m_bHadPropertyBrowserInDesignMode)
2745 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
2748 FmFormView* pFormView = m_pShell->GetFormView();
2749 if (bDesign)
2751 // we are currently filtering, so stop filtering
2752 if (m_bFilterMode)
2753 stopFiltering_Lock(false);
2755 // unsubscribe from the objects of my MarkList
2756 pFormView->GetImpl()->stopMarkListWatching();
2758 else
2760 m_aMarkTimer.Stop();
2762 SuspendPropertyTracking aSuspend( *this );
2763 pFormView->GetImpl()->saveMarkList();
2766 if (bDesign && m_xExternalViewController.is())
2767 CloseExternalFormViewer_Lock();
2769 pFormView->ChangeDesignMode(bDesign);
2771 // notify listeners
2772 FmDesignModeChangedHint aChangedHint( bDesign );
2773 m_pShell->Broadcast(aChangedHint);
2775 m_pShell->m_bDesignMode = bDesign;
2776 UpdateForms_Lock(false);
2778 m_pTextShell->designModeChanged();
2780 if (bDesign)
2782 SdrMarkList aList;
2784 // during changing the mark list, don't track the selected objects in the property browser
2785 SuspendPropertyTracking aSuspend( *this );
2786 // restore the marks
2787 pFormView->GetImpl()->restoreMarkList( aList );
2790 // synchronize with the restored mark list
2791 if ( aList.GetMarkCount() )
2792 SetSelection_Lock(aList);
2794 else
2796 // subscribe to the model of the view (so that I'm informed when someone deletes
2797 // during the alive mode controls that I had saved in the saveMarklist (60343)
2798 pFormView->GetImpl()->startMarkListWatching();
2801 m_pShell->UIFeatureChanged();
2803 // 67506 - 15.07.99 - FS
2804 if (bDesign && m_bHadPropertyBrowserInDesignMode)
2806 // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
2807 // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
2808 // That's why we use an asynchron execution on the dispatcher.
2809 // (And that's why this has to be done AFTER the UIFeatureChanged.)
2810 m_pShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
2812 m_bChangingDesignMode = false;
2816 Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
2818 if (impl_checkDisposed_Lock())
2819 return nullptr;
2821 Reference< XControl > xControl;
2824 Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
2826 const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
2827 // ... that I can then search
2828 for (Reference< XControl > const & control : seqControls)
2830 xControl.set( control, UNO_SET_THROW );
2831 Reference< XControlModel > xCurrentModel( xControl->getModel() );
2832 if ( xCurrentModel == i_rxModel )
2833 break;
2834 xControl.clear();
2837 if ( !xControl.is() )
2839 // fallback (some controls might not have been created, yet, since they were never visible so far)
2840 Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
2841 const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
2842 ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
2844 const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
2845 ENSURE_OR_THROW( pSdrView, "no current view" );
2847 xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow ), UNO_SET_THROW );
2850 catch( const Exception& )
2852 DBG_UNHANDLED_EXCEPTION("svx");
2855 OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
2856 return xControl;
2859 // note: _out_rForms is a member so needs lock
2860 void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
2861 const OUString& _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
2865 Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
2866 if ( !xContainer.is() )
2867 return;
2869 sal_Int32 nCount( xContainer->getCount() );
2870 if ( nCount == 0 )
2871 return;
2873 OUString sCurrentFormName;
2874 OUStringBuffer aNextLevelPrefix;
2875 for ( sal_Int32 i=0; i<nCount; ++i )
2877 // is the current child a form?
2878 Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
2879 if ( !xCurrentAsForm.is() )
2880 continue;
2882 Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
2883 sCurrentFormName = xNamed->getName();
2885 // the name of the current form
2886 OUStringBuffer sCompleteCurrentName( sCurrentFormName );
2887 if ( !_rCurrentLevelPrefix.isEmpty() )
2889 sCompleteCurrentName.append( " (" );
2890 sCompleteCurrentName.append ( _rCurrentLevelPrefix );
2891 sCompleteCurrentName.append( ")" );
2894 // the prefix for the next level
2895 aNextLevelPrefix = _rCurrentLevelPrefix;
2896 if ( !_rCurrentLevelPrefix.isEmpty() )
2897 aNextLevelPrefix.append( '/' );
2898 aNextLevelPrefix.append( sCurrentFormName );
2900 // remember both the form and its "display name"
2901 _out_rForms.push_back( xCurrentAsForm );
2902 _out_rNames.push_back( sCompleteCurrentName.makeStringAndClear() );
2904 // and descend
2905 impl_collectFormSearchContexts_nothrow_Lock(
2906 xCurrentAsForm, aNextLevelPrefix.makeStringAndClear(),
2907 _out_rForms, _out_rNames);
2910 catch( const Exception& )
2912 DBG_UNHANDLED_EXCEPTION("svx");
2917 void FmXFormShell::startFiltering_Lock()
2919 if (impl_checkDisposed_Lock())
2920 return;
2922 // setting all forms in filter mode
2923 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2925 // if the active controller is our external one we have to use the trigger controller
2926 Reference< XControlContainer> xContainer;
2927 if (getActiveController_Lock() == m_xExternalViewController)
2929 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
2930 xContainer = m_xExtViewTriggerController->getContainer();
2932 else
2933 xContainer = getActiveController_Lock()->getContainer();
2935 PFormViewPageWindowAdapter pAdapter = pXView->findWindow( xContainer );
2936 if ( pAdapter.is() )
2938 const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
2939 for (const auto& rpController : rControllerList)
2941 Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
2942 if (xModeSelector.is())
2943 xModeSelector->setMode( "FilterMode" );
2947 m_bFilterMode = true;
2949 m_pShell->UIFeatureChanged();
2950 SfxViewFrame* pViewFrame = m_pShell->GetViewShell()->GetViewFrame();
2951 pViewFrame->GetBindings().InvalidateShell( *m_pShell );
2953 if ( pViewFrame->KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
2954 && !pViewFrame->HasChildWindow( SID_FM_FILTER_NAVIGATOR )
2957 pViewFrame->ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
2962 static void saveFilter(const Reference< runtime::XFormController >& _rxController)
2964 Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
2965 Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
2967 // call the subcontroller
2968 Reference< runtime::XFormController > xController;
2969 for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
2971 _rxController->getByIndex(i) >>= xController;
2972 saveFilter(xController);
2978 xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
2979 xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, makeAny( true ) );
2981 catch (const Exception& )
2983 DBG_UNHANDLED_EXCEPTION("svx");
2989 void FmXFormShell::stopFiltering_Lock(bool bSave)
2991 if (impl_checkDisposed_Lock())
2992 return;
2994 m_bFilterMode = false;
2996 FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
2998 // if the active controller is our external one we have to use the trigger controller
2999 Reference< XControlContainer> xContainer;
3000 if (getActiveController_Lock() == m_xExternalViewController)
3002 DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
3003 xContainer = m_xExtViewTriggerController->getContainer();
3005 else
3006 xContainer = getActiveController_Lock()->getContainer();
3008 PFormViewPageWindowAdapter pAdapter = pXView->findWindow(xContainer);
3009 if ( pAdapter.is() )
3011 const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
3012 ::std::vector < OUString > aOriginalFilters;
3013 ::std::vector < bool > aOriginalApplyFlags;
3015 if (bSave)
3017 for (const auto& rpController : rControllerList)
3019 // remember the current filter settings in case we're going to reload the forms below (which may fail)
3022 Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
3023 aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
3024 aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
3026 catch(Exception&)
3028 OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
3029 // put dummies into the arrays so the they have the right size
3031 if (aOriginalFilters.size() == aOriginalApplyFlags.size())
3032 // the first getPropertyValue failed -> use two dummies
3033 aOriginalFilters.emplace_back( );
3034 aOriginalApplyFlags.push_back( false );
3036 saveFilter(rpController);
3039 for (const auto& rController : rControllerList)
3042 Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
3043 if (xModeSelector.is())
3044 xModeSelector->setMode( "DataMode" );
3046 if (bSave) // execute the filter
3048 const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
3049 for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
3050 j != rControllers.end(); ++j)
3052 Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
3053 if (!xReload.is())
3054 continue;
3055 Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
3059 xReload->reload();
3061 catch(Exception&)
3063 OSL_FAIL("FmXFormShell::stopFiltering: Exception occurred!");
3066 if (!isRowSetAlive(xFormSet))
3067 { // something went wrong -> restore the original state
3068 OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
3069 bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
3072 xFormSet->setPropertyValue(FM_PROP_FILTER, makeAny(sOriginalFilter));
3073 xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, makeAny(bOriginalApplyFlag));
3074 xReload->reload();
3076 catch(const Exception&)
3078 DBG_UNHANDLED_EXCEPTION("svx");
3085 m_pShell->UIFeatureChanged();
3086 m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
3090 void FmXFormShell::CreateExternalView_Lock()
3092 if (impl_checkDisposed_Lock())
3093 return;
3095 DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
3097 // the frame the external view is displayed in
3098 bool bAlreadyExistent = m_xExternalViewController.is();
3099 Reference< css::frame::XFrame> xExternalViewFrame;
3100 OUString sFrameName("_beamer");
3102 Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
3103 // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
3105 // _first_ check if we have any valid fields we can use for the grid view
3106 // FS - 21.10.99 - 69219
3108 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3109 Reference< XPropertySet> xCurrentModelSet;
3110 bool bHaveUsableControls = false;
3111 while ((xCurrentModelSet = Reference< XPropertySet>(aModelIterator.Next(), UNO_QUERY)).is())
3113 // the FmXBoundFormFieldIterator only supplies controls with a valid control source
3114 // so we just have to check the field type
3115 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3116 switch (nClassId)
3118 case FormComponentType::IMAGECONTROL:
3119 case FormComponentType::CONTROL:
3120 continue;
3122 bHaveUsableControls = true;
3123 break;
3126 if (!bHaveUsableControls)
3128 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
3129 VclMessageType::Warning, VclButtonsType::Ok,
3130 SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
3131 xBox->run();
3132 return;
3136 // load the component for external form views
3137 if (!bAlreadyExistent)
3139 URL aWantToDispatch;
3140 aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
3142 Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
3143 Reference< css::frame::XDispatch> xDisp;
3144 if (xProv.is())
3145 xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
3146 css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
3147 if (xDisp.is())
3149 xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
3152 // with this the component should be loaded, now search the frame where it resides in
3153 xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
3154 if (xExternalViewFrame.is())
3156 m_xExternalViewController = xExternalViewFrame->getController();
3157 if (m_xExternalViewController.is())
3158 m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
3161 else
3163 xExternalViewFrame = m_xExternalViewController->getFrame();
3164 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3166 // if we display the active form we interpret the slot as "remove it"
3167 Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
3168 if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
3170 if (m_xExternalViewController == getActiveController_Lock())
3172 Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
3173 ControllerFeatures aHelper( xAsFormController );
3174 (void)aHelper->commitCurrentControl();
3177 Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
3178 CloseExternalFormViewer_Lock();
3179 setActiveController_Lock(xNewController);
3180 return;
3183 URL aClearURL;
3184 aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
3186 Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
3187 if (xClear.is())
3188 xClear->dispatch(aClearURL, Sequence< PropertyValue>());
3191 // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
3192 // instance for which this "external view" was triggered
3194 // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
3195 Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
3197 if (m_xExternalViewController.is())
3199 DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
3200 // collect the dispatchers we will need
3201 URL aAddColumnURL;
3202 aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
3203 Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
3204 URL aAttachURL;
3205 aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
3206 Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
3208 if (xAddColumnDispatch.is() && xAttachDispatch.is())
3210 DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
3211 // first : dispatch the descriptions for the columns to add
3212 sal_Int16 nAddedColumns = 0;
3214 // for radio buttons we need some special structures
3215 typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
3216 typedef std::map< OUString, OUString > FmMapUString2UString;
3217 typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
3219 MapUString2UstringSeq aRadioValueLists;
3220 MapUString2UstringSeq aRadioListSources;
3221 FmMapUString2UString aRadioControlSources;
3222 FmMapUString2Int16 aRadioPositions;
3224 FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
3225 Reference< XPropertySet> xCurrentModelSet;
3226 OUString sColumnType,aGroupName,sControlSource;
3227 Sequence< Property> aProps;
3228 while ((xCurrentModelSet = Reference< XPropertySet>(aModelIterator.Next(), UNO_QUERY)).is())
3230 OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
3231 // create a description of the column to be created
3232 // first : determine it's type
3234 sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
3235 switch (nClassId)
3237 case FormComponentType::RADIOBUTTON:
3239 // get the label of the button (this is the access key for our structures)
3240 aGroupName = getLabelName(xCurrentModelSet);
3242 // add the reference value of the radio button to the list source sequence
3243 Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
3244 sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
3245 aThisGroupLabels.realloc(nNewSizeL);
3246 aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
3248 // add the label to the value list sequence
3249 Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
3250 sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
3251 aThisGroupControlSources.realloc(nNewSizeC);
3252 aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
3254 // remember the controls source of the radio group
3255 sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
3256 if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
3257 aRadioControlSources[aGroupName] = sControlSource;
3258 #ifdef DBG_UTIL
3259 else
3260 DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
3261 "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
3262 // (radio buttons with the same name should have the same control source)
3263 #endif
3264 // remember the position within the columns
3265 if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
3266 aRadioPositions[aGroupName] = nAddedColumns;
3268 // any further handling is done below
3270 continue;
3272 case FormComponentType::IMAGECONTROL:
3273 case FormComponentType::CONTROL:
3274 // no grid columns for these types (though they have a control source)
3275 continue;
3276 case FormComponentType::CHECKBOX:
3277 sColumnType = FM_COL_CHECKBOX; break;
3278 case FormComponentType::LISTBOX:
3279 sColumnType = FM_COL_LISTBOX; break;
3280 case FormComponentType::COMBOBOX:
3281 sColumnType = FM_COL_COMBOBOX; break;
3282 case FormComponentType::DATEFIELD:
3283 sColumnType = FM_COL_DATEFIELD; break;
3284 case FormComponentType::TIMEFIELD:
3285 sColumnType = FM_COL_TIMEFIELD; break;
3286 case FormComponentType::NUMERICFIELD:
3287 sColumnType = FM_COL_NUMERICFIELD; break;
3288 case FormComponentType::CURRENCYFIELD:
3289 sColumnType = FM_COL_CURRENCYFIELD; break;
3290 case FormComponentType::PATTERNFIELD:
3291 sColumnType = FM_COL_PATTERNFIELD; break;
3293 case FormComponentType::TEXTFIELD:
3295 sColumnType = FM_COL_TEXTFIELD;
3296 // we know at least two different controls which are TextFields : the basic edit field and the formatted
3297 // field. we distinguish them by their service name
3298 Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
3299 if (xInfo.is())
3301 sal_Int16 nObjectType = getControlTypeByObject(xInfo);
3302 if (OBJ_FM_FORMATTEDFIELD == nObjectType)
3303 sColumnType = FM_COL_FORMATTEDFIELD;
3306 break;
3307 default:
3308 sColumnType = FM_COL_TEXTFIELD; break;
3311 const sal_Int16 nDispatchArgs = 3;
3312 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3313 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3315 // properties describing "meta data" about the column
3316 // the type
3317 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3318 pDispatchArgs->Value <<= sColumnType;
3319 ++pDispatchArgs;
3321 // the pos : append the col
3322 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3323 pDispatchArgs->Value <<= nAddedColumns;
3324 ++pDispatchArgs;
3326 // the properties to forward to the new column
3327 Sequence< PropertyValue> aColumnProps(1);
3328 PropertyValue* pColumnProps = aColumnProps.getArray();
3330 // the label
3331 pColumnProps->Name = FM_PROP_LABEL;
3332 pColumnProps->Value <<= getLabelName(xCurrentModelSet);
3333 ++pColumnProps;
3335 // for all other props : transfer them
3336 Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
3337 DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
3338 aProps = xControlModelInfo->getProperties();
3340 // realloc the control description sequence
3341 sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
3342 aColumnProps.realloc(nExistentDescs + aProps.getLength());
3343 pColumnProps = aColumnProps.getArray() + nExistentDescs;
3345 for (const Property& rProp : std::as_const(aProps))
3347 if (rProp.Name == FM_PROP_LABEL)
3348 // already set
3349 continue;
3350 if (rProp.Name == FM_PROP_DEFAULTCONTROL)
3351 // allow the column's own "default control"
3352 continue;
3353 if (rProp.Attributes & PropertyAttribute::READONLY)
3354 // assume that properties which are readonly for the control are ro for the column to be created, too
3355 continue;
3357 pColumnProps->Name = rProp.Name;
3358 pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
3359 ++pColumnProps;
3361 aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
3363 // columns props are a dispatch argument
3364 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3365 pDispatchArgs->Value <<= aColumnProps;
3366 ++pDispatchArgs;
3367 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3368 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3370 // dispatch the "add column"
3371 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3372 ++nAddedColumns;
3375 // now for the radio button handling
3376 sal_Int16 nOffset(0);
3377 // properties describing the "direct" column properties
3378 const sal_Int16 nListBoxDescription = 6;
3379 Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
3380 for (const auto& rCtrlSource : aRadioControlSources)
3382 PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
3383 // label
3384 pListBoxDescription->Name = FM_PROP_LABEL;
3385 pListBoxDescription->Value <<= rCtrlSource.first;
3386 ++pListBoxDescription;
3388 // control source
3389 pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
3390 pListBoxDescription->Value <<= rCtrlSource.second;
3391 ++pListBoxDescription;
3393 // bound column
3394 pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
3395 pListBoxDescription->Value <<= sal_Int16(1);
3396 ++pListBoxDescription;
3398 // content type
3399 pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
3400 pListBoxDescription->Value <<= ListSourceType_VALUELIST;
3401 ++pListBoxDescription;
3403 // list source
3404 MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
3405 DBG_ASSERT(aCurrentListSource != aRadioListSources.end(),
3406 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3407 pListBoxDescription->Name = FM_PROP_LISTSOURCE;
3408 pListBoxDescription->Value <<= (*aCurrentListSource).second;
3409 ++pListBoxDescription;
3411 // value list
3412 MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
3413 DBG_ASSERT(aCurrentValueList != aRadioValueLists.end(),
3414 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3415 pListBoxDescription->Name = FM_PROP_STRINGITEMLIST;
3416 pListBoxDescription->Value <<= (*aCurrentValueList).second;
3417 ++pListBoxDescription;
3419 DBG_ASSERT(nListBoxDescription == (pListBoxDescription - aListBoxDescription.getConstArray()),
3420 "FmXFormShell::CreateExternalView : forgot to adjust nListBoxDescription ?");
3422 // properties describing the column "meta data"
3423 const sal_Int16 nDispatchArgs = 3;
3424 Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
3425 PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
3427 // column type : listbox
3428 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
3429 OUString fColName = FM_COL_LISTBOX;
3430 pDispatchArgs->Value <<= fColName;
3431 // pDispatchArgs->Value <<= (OUString)FM_COL_LISTBOX;
3432 ++pDispatchArgs;
3434 // column position
3435 pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
3436 FmMapUString2Int16::const_iterator aOffset = aRadioPositions.find(rCtrlSource.first);
3437 DBG_ASSERT(aOffset != aRadioPositions.end(),
3438 "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
3439 sal_Int16 nPosition = (*aOffset).second;
3440 nPosition = nPosition + nOffset;
3441 // we already inserted nOffset additional columns...
3442 pDispatchArgs->Value <<= nPosition;
3443 ++pDispatchArgs;
3445 // the
3446 pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
3447 pDispatchArgs->Value <<= aListBoxDescription;
3448 ++pDispatchArgs;
3449 DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
3450 "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
3452 // dispatch the "add column"
3453 xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
3454 ++nAddedColumns;
3455 ++nOffset;
3459 DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
3460 // we should have checked if we have any usable controls (see above).
3462 // "load" the "form" of the external view
3463 PropertyValue aArg;
3464 aArg.Name = FMARG_ATTACHTO_MASTERFORM;
3465 Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
3466 aArg.Value <<= xForm;
3468 m_xExternalDisplayedForm = xForm;
3469 // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
3470 // which needs the m_xExternalDisplayedForm
3472 xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
3474 m_xExtViewTriggerController = xCurrentNavController;
3476 // we want to know modifications done in the external view
3477 // if the external controller is a XFormController we can use all our default handlings for it
3478 Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
3479 OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
3480 if (xFormController.is())
3481 xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
3484 #ifdef DBG_UTIL
3485 else
3487 OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
3489 #endif
3490 InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
3494 void FmXFormShell::implAdjustConfigCache_Lock()
3496 // get (cache) the wizard usage flag
3497 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3498 Sequence< Any > aFlags = GetProperties(aNames);
3499 if (1 == aFlags.getLength())
3500 m_bUseWizards = ::cppu::any2bool(aFlags[0]);
3504 void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
3506 DBG_TESTSOLARMUTEX();
3507 if (impl_checkDisposed_Lock())
3508 return;
3510 for (const OUString& rName : _rPropertyNames)
3511 if (rName == "FormControlPilotsEnabled")
3513 implAdjustConfigCache_Lock();
3514 InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
3518 void FmXFormShell::ImplCommit()
3523 void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
3525 m_bUseWizards = _bUseThem;
3527 Sequence< OUString > aNames { "FormControlPilotsEnabled" };
3528 Sequence< Any > aValues(1);
3529 aValues[0] <<= m_bUseWizards;
3530 PutProperties(aNames, aValues);
3534 void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
3537 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3539 _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
3542 // if we have an async load operation pending for the 0-th page for this view,
3543 // we need to cancel this
3544 if (FmFormPage* pPage = _rCurrentView.GetCurPage())
3546 // move all events from our queue to a new one, omit the events for the deactivated
3547 // page
3548 ::std::queue< FmLoadAction > aNewEvents;
3549 while ( !m_aLoadingPages.empty() )
3551 FmLoadAction aAction = m_aLoadingPages.front();
3552 m_aLoadingPages.pop();
3553 if ( pPage != aAction.pPage )
3555 aNewEvents.push( aAction );
3557 else
3559 Application::RemoveUserEvent( aAction.nEventId );
3562 m_aLoadingPages = aNewEvents;
3564 // remove callbacks at the page
3565 pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
3567 UpdateForms_Lock(true);
3571 IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
3573 if (impl_checkDisposed_Lock())
3574 return;
3576 m_nActivationEvent = nullptr;
3577 SfxObjectShell* pDocument = m_pShell->GetObjectShell();
3579 if ( pDocument && !pDocument->HasName() )
3581 if (isEnhancedForm_Lock())
3583 // show the data navigator
3584 if ( !m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
3585 m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
3591 IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
3593 UpdateForms_Lock(true);
3597 void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
3599 FmFormPage* pPage = _rCurrentView.GetCurPage();
3601 // activate our view if we are activated ourself
3602 // FS - 30.06.99 - 67308
3603 if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
3605 // load forms for the page the current view belongs to
3606 if ( pPage )
3608 if ( !pPage->GetImpl().hasEverBeenActivated() )
3609 loadForms_Lock(pPage, LoadFormsFlags::Load
3610 | (_bSyncAction ? LoadFormsFlags::Sync
3611 : LoadFormsFlags::Async));
3612 pPage->GetImpl().setHasBeenActivated( );
3615 // first-time initializations for the views
3616 if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
3618 _rCurrentView.GetImpl()->onFirstViewActivation( dynamic_cast<FmFormModel*>( _rCurrentView.GetModel() ) );
3619 _rCurrentView.GetImpl()->setHasBeenActivated( );
3622 // activate the current view
3623 _rCurrentView.GetImpl()->Activate( _bSyncAction );
3626 // set callbacks at the page
3627 if ( pPage )
3629 pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
3632 UpdateForms_Lock(true);
3634 if ( m_bFirstActivation )
3636 m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
3637 m_bFirstActivation = false;
3640 // find a default "current form", if there is none, yet
3641 // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
3642 impl_defaultCurrentForm_nothrow_Lock();
3646 void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
3648 if (impl_checkDisposed_Lock())
3649 return;
3651 if ( m_xCurrentForm.is() )
3652 // no action required
3653 return;
3655 FmFormView* pFormView = m_pShell->GetFormView();
3656 FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
3657 if ( !pPage )
3658 return;
3662 Reference< XIndexAccess > xForms = pPage->GetForms( false );
3663 if ( !xForms.is() || !xForms->hasElements() )
3664 return;
3666 Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
3667 impl_updateCurrentForm_Lock(xNewCurrentForm);
3669 catch( const Exception& )
3671 DBG_UNHANDLED_EXCEPTION("svx");
3676 void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
3678 if (!_rxModels.is())
3680 OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
3681 return;
3684 static const OUString sClassIdPropertyName = FM_PROP_CLASSID;
3685 static const OUString sBoundFieldPropertyName = FM_PROP_BOUNDFIELD;
3686 sal_Int32 nCount = _rxModels->getCount();
3687 Reference< XPropertySet > xCurrent;
3688 Reference< XPropertySetInfo > xCurrentInfo;
3689 Reference< XPropertySet > xBoundField;
3691 for (sal_Int32 i=0; i<nCount; ++i)
3693 _rxModels->getByIndex(i) >>= xCurrent;
3694 if (xCurrent.is())
3695 xCurrentInfo = xCurrent->getPropertySetInfo();
3696 else
3697 xCurrentInfo.clear();
3698 if (!xCurrentInfo.is())
3699 continue;
3701 if (xCurrentInfo->hasPropertyByName(sClassIdPropertyName))
3702 { // it's a control model
3704 // check if this control is bound to a living database field
3705 if (xCurrentInfo->hasPropertyByName(sBoundFieldPropertyName))
3706 xCurrent->getPropertyValue(sBoundFieldPropertyName) >>= xBoundField;
3707 else
3708 xBoundField.clear();
3710 // reset only if it's *not* bound
3711 bool bReset = !xBoundField.is();
3713 // and additionally, check if it has an external value binding
3714 Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
3715 if ( xBindable.is() && xBindable->getValueBinding().is() )
3716 bReset = false;
3718 if ( bReset )
3720 Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
3721 if ( xControlReset.is() )
3722 xControlReset->reset();
3725 else
3727 Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
3728 if (xContainer.is())
3729 smartControlReset(xContainer);
3735 IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
3737 FmLoadAction aAction = m_aLoadingPages.front();
3738 m_aLoadingPages.pop();
3740 loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
3744 namespace
3746 bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
3748 // determines whether a form should be loaded or not
3749 // if there is no datasource or connection there is no reason to load a form
3750 Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
3751 if ( !xSet.is() )
3752 return false;
3755 Reference< XConnection > xConn;
3756 if ( isEmbeddedInDatabase( _rxLoadable.get(), xConn ) )
3757 return true;
3759 // is there already an active connection
3760 xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
3761 if ( xConn.is() )
3762 return true;
3764 OUString sPropertyValue;
3765 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
3766 if ( !sPropertyValue.isEmpty() )
3767 return true;
3769 OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
3770 if ( !sPropertyValue.isEmpty() )
3771 return true;
3773 catch(const Exception&)
3775 DBG_UNHANDLED_EXCEPTION("svx");
3777 return false;
3782 void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
3784 DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
3785 "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
3787 if ( _nBehaviour & LoadFormsFlags::Async )
3789 m_aLoadingPages.push( FmLoadAction(
3790 _pPage,
3791 _nBehaviour,
3792 Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
3793 ) );
3794 return;
3797 DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
3798 if ( _pPage )
3800 // lock the undo env so the forms can change non-transient properties while loading
3801 // (without this my doc's modified flag would be set)
3802 FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
3803 rFmFormModel.GetUndoEnv().Lock();
3805 // load all forms
3806 Reference< XIndexAccess > xForms = _pPage->GetForms( false );
3808 if ( xForms.is() )
3810 Reference< XLoadable > xForm;
3811 for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
3813 xForms->getByIndex( j ) >>= xForm;
3814 bool bFormWasLoaded = false;
3815 // a database form must be loaded for
3818 if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
3820 if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
3821 xForm->load();
3823 else
3825 if ( xForm->isLoaded() )
3827 bFormWasLoaded = true;
3828 xForm->unload();
3832 catch( const Exception& )
3834 DBG_UNHANDLED_EXCEPTION("svx");
3837 // reset the form if it was loaded
3838 if ( bFormWasLoaded )
3840 Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
3841 DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
3842 if ( xContainer.is() )
3843 smartControlReset( xContainer );
3848 // unlock the environment
3849 rFmFormModel.GetUndoEnv().UnLock();
3854 void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
3856 DBG_TESTSOLARMUTEX();
3857 m_pTextShell->ExecuteTextAttribute( _rReq );
3861 void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
3863 DBG_TESTSOLARMUTEX();
3864 m_pTextShell->GetTextAttributeState( _rSet );
3868 bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
3870 DBG_TESTSOLARMUTEX();
3871 return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
3875 void FmXFormShell::ForgetActiveControl_Lock()
3877 DBG_TESTSOLARMUTEX();
3878 m_pTextShell->ForgetActiveControl();
3882 void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
3884 DBG_TESTSOLARMUTEX();
3885 m_pTextShell->SetControlActivationHandler( _rHdl );
3888 void FmXFormShell::handleShowPropertiesRequest_Lock()
3890 if (onlyControlsAreMarked_Lock())
3891 ShowSelectionProperties_Lock( true );
3895 void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
3897 // catch simple double clicks
3898 if ( ( _rViewEvent.nMouseClicks == 2 ) && ( _rViewEvent.nMouseCode == MOUSE_LEFT ) )
3900 if ( _rViewEvent.eHit == SdrHitKind::MarkedObject )
3902 if (onlyControlsAreMarked_Lock())
3903 ShowSelectionProperties_Lock( true );
3909 bool FmXFormShell::HasControlFocus_Lock() const
3911 bool bHasControlFocus = false;
3915 Reference<runtime::XFormController> xController(getActiveController_Lock());
3916 Reference< XControl > xCurrentControl;
3917 if ( xController.is() )
3918 xCurrentControl.set( xController->getCurrentControl() );
3919 if ( xCurrentControl.is() )
3921 Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
3922 bHasControlFocus = xPeerWindow->hasFocus();
3925 catch( const Exception& )
3927 DBG_UNHANDLED_EXCEPTION("svx");
3930 return bHasControlFocus;
3934 SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
3935 :IndexAccessIterator(xStartingPoint)
3940 bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
3942 // if the thing has a ControlSource and a BoundField property
3943 Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
3944 if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
3946 // and the BoundField is valid
3947 Reference< XPropertySet> xField;
3948 xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
3949 if (xField.is())
3951 // we take it
3952 m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
3953 return true;
3957 // if it is a grid control
3958 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
3960 Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
3961 if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
3963 m_sCurrentValue.clear();
3964 return true;
3968 return false;
3972 bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
3974 return true;
3977 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */