1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <svx/fmgridif.hxx>
25 #include <fmprop.hxx>
26 #include <fmservs.hxx>
27 #include <svx/fmtools.hxx>
28 #include <fmurl.hxx>
29 #include <formcontrolfactory.hxx>
30 #include <gridcell.hxx>
31 #include <sdbdatacolumn.hxx>
32 #include <svx/fmgridcl.hxx>
33 #include <tools/urlobj.hxx>
34 
35 #include <com/sun/star/awt/PosSize.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
37 #include <com/sun/star/form/FormComponentType.hpp>
38 #include <com/sun/star/form/XFormComponent.hpp>
39 #include <com/sun/star/form/XLoadable.hpp>
40 #include <com/sun/star/form/XReset.hpp>
41 #include <com/sun/star/lang/DisposedException.hpp>
42 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
43 #include <com/sun/star/lang/NoSupportException.hpp>
44 #include <com/sun/star/sdbc/ResultSetType.hpp>
45 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
46 #include <com/sun/star/util/URLTransformer.hpp>
47 #include <com/sun/star/util/XURLTransformer.hpp>
48 #include <com/sun/star/view/XSelectionSupplier.hpp>
49 #include <com/sun/star/sdbcx/XRowLocate.hpp>
50 
51 #include <comphelper/enumhelper.hxx>
52 #include <comphelper/processfactory.hxx>
53 #include <comphelper/property.hxx>
54 #include <comphelper/sequence.hxx>
55 #include <comphelper/types.hxx>
56 #include <cppuhelper/supportsservice.hxx>
57 #include <cppuhelper/queryinterface.hxx>
58 #include <vcl/unohelp.hxx>
59 #include <vcl/svapp.hxx>
60 #include <tools/debug.hxx>
61 #include <tools/diagnose_ex.h>
62 #include <sal/macros.h>
63 
64 using namespace ::svxform;
65 using namespace ::com::sun::star::container;
66 using namespace ::com::sun::star::sdb;
67 using namespace ::com::sun::star::sdbc;
68 using namespace ::com::sun::star::uno;
69 using namespace ::com::sun::star::view;
70 using namespace ::com::sun::star::beans;
71 using namespace ::com::sun::star::lang;
72 using namespace ::com::sun::star::form;
73 using namespace ::com::sun::star::util;
74 using namespace ::com::sun::star;
75 
76 using ::com::sun::star::sdbcx::XColumnsSupplier;
77 using ::com::sun::star::frame::XDispatchProviderInterceptor;
78 using ::com::sun::star::frame::XDispatchProvider;
79 using ::com::sun::star::accessibility::XAccessible;
80 using ::com::sun::star::accessibility::XAccessibleContext;
81 using ::com::sun::star::sdb::XRowSetSupplier;
82 using ::com::sun::star::awt::XVclWindowPeer;
83 
84 
ImplCreateFontDescriptor(const vcl::Font & rFont)85 static css::awt::FontDescriptor ImplCreateFontDescriptor( const vcl::Font& rFont )
86 {
87     css::awt::FontDescriptor aFD;
88     aFD.Name = rFont.GetFamilyName();
89     aFD.StyleName = rFont.GetStyleName();
90     aFD.Height = static_cast<sal_Int16>(rFont.GetFontSize().Height());
91     aFD.Width = static_cast<sal_Int16>(rFont.GetFontSize().Width());
92     aFD.Family = static_cast<sal_Int16>(rFont.GetFamilyType());
93     aFD.CharSet = rFont.GetCharSet();
94     aFD.Pitch = static_cast<sal_Int16>(rFont.GetPitch());
95     aFD.CharacterWidth = vcl::unohelper::ConvertFontWidth( rFont.GetWidthType() );
96     aFD.Weight= vcl::unohelper::ConvertFontWeight( rFont.GetWeight() );
97     aFD.Slant = vcl::unohelper::ConvertFontSlant( rFont.GetItalic() );
98     aFD.Underline = static_cast<sal_Int16>(rFont.GetUnderline());
99     aFD.Strikeout = static_cast<sal_Int16>(rFont.GetStrikeout());
100     aFD.Orientation = rFont.GetOrientation().get() / 10.0;
101     aFD.Kerning = rFont.IsKerning();
102     aFD.WordLineMode = rFont.IsWordLineMode();
103     aFD.Type = 0;   // ??? => only to metric...
104     return aFD;
105 }
106 
107 
ImplCreateFont(const css::awt::FontDescriptor & rDescr)108 static vcl::Font ImplCreateFont( const css::awt::FontDescriptor& rDescr )
109 {
110     vcl::Font aFont;
111     aFont.SetFamilyName( rDescr.Name );
112     aFont.SetStyleName( rDescr.StyleName );
113     aFont.SetFontSize( ::Size( rDescr.Width, rDescr.Height ) );
114     aFont.SetFamily( static_cast<FontFamily>(rDescr.Family) );
115     aFont.SetCharSet( static_cast<rtl_TextEncoding>(rDescr.CharSet) );
116     aFont.SetPitch( static_cast<FontPitch>(rDescr.Pitch) );
117     aFont.SetWidthType( vcl::unohelper::ConvertFontWidth( rDescr.CharacterWidth ) );
118     aFont.SetWeight( vcl::unohelper::ConvertFontWeight( rDescr.Weight ) );
119     aFont.SetItalic( static_cast<FontItalic>(rDescr.Slant) );
120     aFont.SetUnderline( static_cast<::FontLineStyle>(rDescr.Underline) );
121     aFont.SetStrikeout( static_cast<::FontStrikeout>(rDescr.Strikeout) );
122     aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rDescr.Orientation * 10)) );
123     aFont.SetKerning( static_cast<FontKerning>(rDescr.Kerning) );
124     aFont.SetWordLineMode( rDescr.WordLineMode );
125     return aFont;
126 }
127 
FmXModifyMultiplexer(::cppu::OWeakObject & rSource,::osl::Mutex & _rMutex)128 FmXModifyMultiplexer::FmXModifyMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
129                     :OWeakSubObject( rSource )
130                     ,OInterfaceContainerHelper2( _rMutex )
131 {
132 }
133 
134 
queryInterface(const Type & _rType)135 Any SAL_CALL FmXModifyMultiplexer::queryInterface(const Type& _rType)
136 {
137     Any aReturn = ::cppu::queryInterface(_rType,
138         static_cast< css::util::XModifyListener*>(this),
139         static_cast< XEventListener*>(this)
140     );
141 
142     if (!aReturn.hasValue())
143         aReturn = OWeakSubObject::queryInterface( _rType );
144 
145     return aReturn;
146 }
147 
148 
disposing(const EventObject &)149 void FmXModifyMultiplexer::disposing(const EventObject& )
150 {
151 }
152 
153 
modified(const EventObject & e)154 void FmXModifyMultiplexer::modified(const EventObject& e)
155 {
156     EventObject aMulti( e);
157     aMulti.Source = &m_rParent;
158     notifyEach( &XModifyListener::modified, aMulti );
159 }
160 
FmXUpdateMultiplexer(::cppu::OWeakObject & rSource,::osl::Mutex & _rMutex)161 FmXUpdateMultiplexer::FmXUpdateMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
162                     :OWeakSubObject( rSource )
163                     ,OInterfaceContainerHelper2( _rMutex )
164 {
165 }
166 
167 
queryInterface(const Type & _rType)168 Any SAL_CALL FmXUpdateMultiplexer::queryInterface(const Type& _rType)
169 {
170     Any aReturn = ::cppu::queryInterface(_rType,
171         static_cast< XUpdateListener*>(this),
172         static_cast< XEventListener*>(this)
173     );
174 
175     if (!aReturn.hasValue())
176         aReturn = OWeakSubObject::queryInterface( _rType );
177 
178     return aReturn;
179 }
180 
181 
disposing(const EventObject &)182 void FmXUpdateMultiplexer::disposing(const EventObject& )
183 {
184 }
185 
186 
approveUpdate(const EventObject & e)187 sal_Bool FmXUpdateMultiplexer::approveUpdate(const EventObject &e)
188 {
189     EventObject aMulti( e );
190     aMulti.Source = &m_rParent;
191 
192     bool bResult = true;
193     if (getLength())
194     {
195         ::comphelper::OInterfaceIteratorHelper2 aIter(*this);
196         while ( bResult && aIter.hasMoreElements() )
197             bResult = static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aMulti );
198     }
199 
200     return bResult;
201 }
202 
203 
updated(const EventObject & e)204 void FmXUpdateMultiplexer::updated(const EventObject &e)
205 {
206     EventObject aMulti( e );
207     aMulti.Source = &m_rParent;
208     notifyEach( &XUpdateListener::updated, aMulti );
209 }
210 
FmXSelectionMultiplexer(::cppu::OWeakObject & rSource,::osl::Mutex & _rMutex)211 FmXSelectionMultiplexer::FmXSelectionMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
212     :OWeakSubObject( rSource )
213     ,OInterfaceContainerHelper2( _rMutex )
214 {
215 }
216 
217 
queryInterface(const Type & _rType)218 Any SAL_CALL FmXSelectionMultiplexer::queryInterface(const Type& _rType)
219 {
220     Any aReturn = ::cppu::queryInterface(_rType,
221         static_cast< XSelectionChangeListener*>(this),
222         static_cast< XEventListener*>(this)
223     );
224 
225     if (!aReturn.hasValue())
226         aReturn = OWeakSubObject::queryInterface( _rType );
227 
228     return aReturn;
229 }
230 
231 
disposing(const EventObject &)232 void FmXSelectionMultiplexer::disposing(const EventObject& )
233 {
234 }
235 
236 
selectionChanged(const EventObject & _rEvent)237 void SAL_CALL FmXSelectionMultiplexer::selectionChanged( const EventObject& _rEvent )
238 {
239     EventObject aMulti(_rEvent);
240     aMulti.Source = &m_rParent;
241     notifyEach( &XSelectionChangeListener::selectionChanged, aMulti );
242 }
243 
FmXContainerMultiplexer(::cppu::OWeakObject & rSource,::osl::Mutex & _rMutex)244 FmXContainerMultiplexer::FmXContainerMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
245                         :OWeakSubObject( rSource )
246                         ,OInterfaceContainerHelper2( _rMutex )
247 {
248 }
249 
250 
queryInterface(const Type & _rType)251 Any SAL_CALL FmXContainerMultiplexer::queryInterface(const Type& _rType)
252 {
253     Any aReturn = ::cppu::queryInterface(_rType,
254         static_cast< XContainerListener*>(this),
255         static_cast< XEventListener*>(this)
256     );
257 
258     if (!aReturn.hasValue())
259         aReturn = OWeakSubObject::queryInterface( _rType );
260 
261     return aReturn;
262 }
263 
264 
disposing(const EventObject &)265 void FmXContainerMultiplexer::disposing(const EventObject& )
266 {
267 }
268 
elementInserted(const ContainerEvent & e)269 void FmXContainerMultiplexer::elementInserted(const ContainerEvent& e)
270 {
271     ContainerEvent aMulti( e );
272     aMulti.Source = &m_rParent;
273     notifyEach( &XContainerListener::elementInserted, aMulti );
274 }
275 
276 
elementRemoved(const ContainerEvent & e)277 void FmXContainerMultiplexer::elementRemoved(const ContainerEvent& e)
278 {
279     ContainerEvent aMulti( e );
280     aMulti.Source = &m_rParent;
281     notifyEach( &XContainerListener::elementRemoved, aMulti );
282 }
283 
284 
elementReplaced(const ContainerEvent & e)285 void FmXContainerMultiplexer::elementReplaced(const ContainerEvent& e)
286 {
287     ContainerEvent aMulti( e );
288     aMulti.Source = &m_rParent;
289     notifyEach( &XContainerListener::elementReplaced, aMulti );
290 }
291 
FmXGridControlMultiplexer(::cppu::OWeakObject & rSource,::osl::Mutex & _rMutex)292 FmXGridControlMultiplexer::FmXGridControlMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
293     :OWeakSubObject( rSource )
294     ,OInterfaceContainerHelper2( _rMutex )
295 {
296 }
297 
298 
queryInterface(const Type & _rType)299 Any SAL_CALL FmXGridControlMultiplexer::queryInterface(const Type& _rType)
300 {
301     Any aReturn = ::cppu::queryInterface( _rType,
302         static_cast< XGridControlListener*>(this)
303     );
304 
305     if (!aReturn.hasValue())
306         aReturn = OWeakSubObject::queryInterface( _rType );
307 
308     return aReturn;
309 }
310 
311 
disposing(const EventObject &)312 void FmXGridControlMultiplexer::disposing( const EventObject& )
313 {
314 }
315 
316 
columnChanged(const EventObject & _event)317 void SAL_CALL FmXGridControlMultiplexer::columnChanged( const EventObject& _event )
318 {
319     EventObject aForwardedEvent( _event );
320     aForwardedEvent.Source = &m_rParent;
321     notifyEach( &XGridControlListener::columnChanged, aForwardedEvent );
322 }
323 
324 
325 //= FmXGridControl
326 
327 
FmXGridControl_NewInstance_Impl(const Reference<XMultiServiceFactory> & _rxFactory)328 Reference< XInterface > FmXGridControl_NewInstance_Impl(const Reference< XMultiServiceFactory>& _rxFactory)
329 {
330     return *(new FmXGridControl( comphelper::getComponentContext(_rxFactory) ));
331 }
332 
FmXGridControl(const Reference<XComponentContext> & _rxContext)333 FmXGridControl::FmXGridControl(const Reference< XComponentContext >& _rxContext)
334                :UnoControl()
335                ,m_aModifyListeners(*this, GetMutex())
336                ,m_aUpdateListeners(*this, GetMutex())
337                ,m_aContainerListeners(*this, GetMutex())
338                ,m_aSelectionListeners(*this, GetMutex())
339                ,m_aGridControlListeners(*this, GetMutex())
340                ,m_bInDraw(false)
341                ,m_xContext(_rxContext)
342 {
343 }
344 
345 
~FmXGridControl()346 FmXGridControl::~FmXGridControl()
347 {
348 }
349 
350 
queryAggregation(const Type & _rType)351 Any SAL_CALL FmXGridControl::queryAggregation(const Type& _rType)
352 {
353     Any aReturn = FmXGridControl_BASE::queryInterface(_rType);
354 
355     if (!aReturn.hasValue())
356         aReturn = UnoControl::queryAggregation( _rType );
357     return aReturn;
358 }
359 
360 
getTypes()361 Sequence< Type> SAL_CALL FmXGridControl::getTypes(  )
362 {
363     return comphelper::concatSequences(UnoControl::getTypes(),FmXGridControl_BASE::getTypes());
364 }
365 
366 
getImplementationId()367 Sequence<sal_Int8> SAL_CALL FmXGridControl::getImplementationId(  )
368 {
369     return css::uno::Sequence<sal_Int8>();
370 }
371 
372 // XServiceInfo
supportsService(const OUString & ServiceName)373 sal_Bool SAL_CALL FmXGridControl::supportsService(const OUString& ServiceName)
374 {
375     return cppu::supportsService(this, ServiceName);
376 }
377 
getImplementationName()378 OUString SAL_CALL FmXGridControl::getImplementationName()
379 {
380     return "com.sun.star.form.FmXGridControl";
381 }
382 
getSupportedServiceNames()383 css::uno::Sequence<OUString> SAL_CALL FmXGridControl::getSupportedServiceNames()
384 {
385     return { FM_SUN_CONTROL_GRIDCONTROL, "com.sun.star.awt.UnoControl" };
386 }
387 
388 
dispose()389 void SAL_CALL FmXGridControl::dispose()
390 {
391     SolarMutexGuard aGuard;
392 
393     EventObject aEvt;
394     aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
395     m_aModifyListeners.disposeAndClear(aEvt);
396     m_aUpdateListeners.disposeAndClear(aEvt);
397     m_aContainerListeners.disposeAndClear(aEvt);
398 
399     UnoControl::dispose();
400 }
401 
402 
GetComponentServiceName()403 OUString FmXGridControl::GetComponentServiceName()
404 {
405     return "DBGrid";
406 }
407 
408 
setModel(const Reference<css::awt::XControlModel> & rModel)409 sal_Bool SAL_CALL FmXGridControl::setModel(const Reference< css::awt::XControlModel >& rModel)
410 {
411     SolarMutexGuard aGuard;
412 
413     if (!UnoControl::setModel(rModel))
414         return false;
415 
416     Reference< XGridPeer > xGridPeer(getPeer(), UNO_QUERY);
417     if (xGridPeer.is())
418     {
419         Reference< XIndexContainer > xCols(mxModel, UNO_QUERY);
420         xGridPeer->setColumns(xCols);
421     }
422     return true;
423 }
424 
425 
imp_CreatePeer(vcl::Window * pParent)426 rtl::Reference<FmXGridPeer> FmXGridControl::imp_CreatePeer(vcl::Window* pParent)
427 {
428     rtl::Reference<FmXGridPeer> pReturn = new FmXGridPeer(m_xContext);
429 
430     // translate properties into WinBits
431     WinBits nStyle = WB_TABSTOP;
432     Reference< XPropertySet >  xModelSet(getModel(), UNO_QUERY);
433     if (xModelSet.is())
434     {
435         try
436         {
437             if (::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_BORDER)))
438                 nStyle |= WB_BORDER;
439         }
440         catch(const Exception&)
441         {
442             OSL_FAIL("Can not get style");
443         }
444     }
445 
446     pReturn->Create(pParent, nStyle);
447     return pReturn;
448 }
449 
450 
createPeer(const Reference<css::awt::XToolkit> &,const Reference<css::awt::XWindowPeer> & rParentPeer)451 void SAL_CALL FmXGridControl::createPeer(const Reference< css::awt::XToolkit >& /*rToolkit*/, const Reference< css::awt::XWindowPeer >& rParentPeer)
452 {
453     if ( !mxModel.is() )
454         throw DisposedException( OUString(), *this );
455 
456     DBG_ASSERT(/*(0 == m_nPeerCreationLevel) && */!mbCreatingPeer, "FmXGridControl::createPeer : recursion!");
457         // I think this should never assert, now that we're using the base class' mbCreatingPeer in addition to
458         // our own m_nPeerCreationLevel
459         // But I'm not sure as I don't _fully_ understand the underlying toolkit implementations...
460         // (if this asserts, we still need m_nPeerCreationLevel. If not, we could omit it...)
461         // 14.05.2001 - 86836 - frank.schoenheit@germany.sun.com
462 
463     // TODO: why the hell this whole class does not use any mutex?
464 
465     if (getPeer().is())
466         return;
467 
468     mbCreatingPeer = true;
469     // mbCreatingPeer is virtually the same as m_nPeerCreationLevel, but it's the base class' method
470     // to prevent recursion.
471 
472     vcl::Window* pParentWin = nullptr;
473     if (rParentPeer.is())
474     {
475         VCLXWindow* pParent = comphelper::getUnoTunnelImplementation<VCLXWindow>(rParentPeer);
476         if (pParent)
477             pParentWin = pParent->GetWindow();
478     }
479 
480     rtl::Reference<FmXGridPeer> pPeer = imp_CreatePeer(pParentWin);
481     DBG_ASSERT(pPeer != nullptr, "FmXGridControl::createPeer : imp_CreatePeer didn't return a peer !");
482     setPeer( pPeer );
483 
484     // reading the properties from the model
485 //      ++m_nPeerCreationLevel;
486     updateFromModel();
487 
488     // consider the following ugly scenario: updateFromModel leads to a propertiesChanges on the Control,
489     // which determines, dat a "critical" property has changed (e.g. "Border") and therefore starts a new
490     // Peer, which lands again here in createPeer we also start a second FmXGridPeer and initialise it.
491     // Then we exit from the first incarnation's updateFromModel and continue working with the pPeer,
492     // that is in fact now already obsolete (as another peer is being started in the second incarnation).
493     // Therefore the effort with the PeerCreationLevel, which ensures that we really use the Peer
494     // created at the deepest level, but first initialise it in the top-level.
495 //      if (--m_nPeerCreationLevel == 0)
496     {
497         DBG_ASSERT(getPeer().is(), "FmXGridControl::createPeer : something went wrong ... no top level peer !");
498         pPeer = comphelper::getUnoTunnelImplementation<FmXGridPeer>(getPeer());
499 
500         setPosSize( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight, css::awt::PosSize::POSSIZE );
501 
502         Reference< XIndexContainer >  xColumns(getModel(), UNO_QUERY);
503         if (xColumns.is())
504             pPeer->setColumns(xColumns);
505 
506         if (maComponentInfos.bVisible)
507             pPeer->setVisible(true);
508 
509         if (!maComponentInfos.bEnable)
510             pPeer->setEnable(false);
511 
512         if (maWindowListeners.getLength())
513             pPeer->addWindowListener( &maWindowListeners );
514 
515         if (maFocusListeners.getLength())
516             pPeer->addFocusListener( &maFocusListeners );
517 
518         if (maKeyListeners.getLength())
519             pPeer->addKeyListener( &maKeyListeners );
520 
521         if (maMouseListeners.getLength())
522             pPeer->addMouseListener( &maMouseListeners );
523 
524         if (maMouseMotionListeners.getLength())
525             pPeer->addMouseMotionListener( &maMouseMotionListeners );
526 
527         if (maPaintListeners.getLength())
528             pPeer->addPaintListener( &maPaintListeners );
529 
530         if (m_aModifyListeners.getLength())
531             pPeer->addModifyListener( &m_aModifyListeners );
532 
533         if (m_aUpdateListeners.getLength())
534             pPeer->addUpdateListener( &m_aUpdateListeners );
535 
536         if (m_aContainerListeners.getLength())
537             pPeer->addContainerListener( &m_aContainerListeners );
538 
539         // forward the design mode
540         bool bForceAlivePeer = m_bInDraw && !maComponentInfos.bVisible;
541         // (we force an alive-mode peer if we're in "draw", cause in this case the peer will be used for drawing in
542         // foreign devices. We ensure this with the visibility check as a living peer is assumed to be noncritical
543         // only if invisible)
544         Any aOldCursorBookmark;
545         if (!mbDesignMode || bForceAlivePeer)
546         {
547             Reference< XFormComponent >  xComp(getModel(), UNO_QUERY);
548             if (xComp.is())
549             {
550                 Reference< XRowSet >  xForm(xComp->getParent(), UNO_QUERY);
551                 // is the form alive?
552                 // we can see that if the form contains columns
553                 Reference< css::sdbcx::XColumnsSupplier >  xColumnsSupplier(xForm, UNO_QUERY);
554                 if (xColumnsSupplier.is())
555                 {
556                     if (Reference< XIndexAccess > (xColumnsSupplier->getColumns(),UNO_QUERY_THROW)->getCount())
557                     {
558                         // we get only a new bookmark if the resultset is not forwardonly
559                         if (::comphelper::getINT32(Reference< XPropertySet > (xForm, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_RESULTSET_TYPE)) != ResultSetType::FORWARD_ONLY)
560                         {
561                             // as the FmGridControl touches the data source it is connected to we have to remember the current
562                             // cursor position (and restore afterwards)
563                             // OJ: but only when we stand on a valid row
564                             if ( !xForm->isBeforeFirst() && !xForm->isAfterLast() )
565                             {
566                                 try
567                                 {
568                                     aOldCursorBookmark = Reference< css::sdbcx::XRowLocate > (xForm, UNO_QUERY_THROW)->getBookmark();
569                                 }
570                                 catch( const Exception& )
571                                 {
572                                     DBG_UNHANDLED_EXCEPTION("svx");
573                                 }
574                             }
575                         }
576                     }
577                 }
578                 pPeer->setRowSet(xForm);
579             }
580         }
581         pPeer->setDesignMode(mbDesignMode && !bForceAlivePeer);
582 
583         try
584         {
585             if (aOldCursorBookmark.hasValue())
586             {   // we have a valid bookmark, so we have to restore the cursor's position
587                 Reference< XFormComponent >  xComp(getModel(), UNO_QUERY);
588                 Reference< css::sdbcx::XRowLocate >  xLocate(xComp->getParent(), UNO_QUERY);
589                 xLocate->moveToBookmark(aOldCursorBookmark);
590             }
591         }
592         catch( const Exception& )
593         {
594             DBG_UNHANDLED_EXCEPTION("svx");
595         }
596 
597         Reference< css::awt::XView >  xPeerView(getPeer(), UNO_QUERY);
598         xPeerView->setZoom( maComponentInfos.nZoomX, maComponentInfos.nZoomY );
599         xPeerView->setGraphics( mxGraphics );
600     }
601     mbCreatingPeer = false;
602 }
603 
604 
addModifyListener(const Reference<css::util::XModifyListener> & l)605 void FmXGridControl::addModifyListener(const Reference< css::util::XModifyListener >& l)
606 {
607     m_aModifyListeners.addInterface( l );
608     if( getPeer().is() && m_aModifyListeners.getLength() == 1 )
609     {
610         Reference< css::util::XModifyBroadcaster >  xGrid(getPeer(), UNO_QUERY);
611         xGrid->addModifyListener( &m_aModifyListeners);
612     }
613 }
614 
615 
select(const Any & _rSelection)616 sal_Bool SAL_CALL FmXGridControl::select( const Any& _rSelection )
617 {
618     SolarMutexGuard aGuard;
619     Reference< XSelectionSupplier > xPeer(getPeer(), UNO_QUERY);
620     return xPeer->select(_rSelection);
621 }
622 
623 
getSelection()624 Any SAL_CALL FmXGridControl::getSelection(  )
625 {
626     SolarMutexGuard aGuard;
627     Reference< XSelectionSupplier > xPeer(getPeer(), UNO_QUERY);
628     return xPeer->getSelection();
629 }
630 
631 
addSelectionChangeListener(const Reference<XSelectionChangeListener> & _rxListener)632 void SAL_CALL FmXGridControl::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
633 {
634     m_aSelectionListeners.addInterface( _rxListener );
635     if( getPeer().is() && 1 == m_aSelectionListeners.getLength() )
636     {
637         Reference< XSelectionSupplier > xGrid(getPeer(), UNO_QUERY);
638         xGrid->addSelectionChangeListener( &m_aSelectionListeners);
639     }
640 }
641 
642 
removeSelectionChangeListener(const Reference<XSelectionChangeListener> & _rxListener)643 void SAL_CALL FmXGridControl::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
644 {
645     if( getPeer().is() && 1 == m_aSelectionListeners.getLength() )
646     {
647         Reference< XSelectionSupplier > xGrid(getPeer(), UNO_QUERY);
648         xGrid->removeSelectionChangeListener( &m_aSelectionListeners);
649     }
650     m_aSelectionListeners.removeInterface( _rxListener );
651 }
652 
653 
queryFieldDataType(const Type & xType)654 Sequence< sal_Bool > SAL_CALL FmXGridControl::queryFieldDataType( const Type& xType )
655 {
656     if (getPeer().is())
657     {
658         Reference< XGridFieldDataSupplier >  xPeerSupplier(getPeer(), UNO_QUERY);
659         if (xPeerSupplier.is())
660             return xPeerSupplier->queryFieldDataType(xType);
661     }
662 
663     return Sequence<sal_Bool>();
664 }
665 
666 
queryFieldData(sal_Int32 nRow,const Type & xType)667 Sequence< Any > SAL_CALL FmXGridControl::queryFieldData( sal_Int32 nRow, const Type& xType )
668 {
669     if (getPeer().is())
670     {
671         Reference< XGridFieldDataSupplier >  xPeerSupplier(getPeer(), UNO_QUERY);
672         if (xPeerSupplier.is())
673             return xPeerSupplier->queryFieldData(nRow, xType);
674     }
675 
676     return Sequence< Any>();
677 }
678 
679 
removeModifyListener(const Reference<css::util::XModifyListener> & l)680 void SAL_CALL FmXGridControl::removeModifyListener(const Reference< css::util::XModifyListener >& l)
681 {
682     if( getPeer().is() && m_aModifyListeners.getLength() == 1 )
683     {
684         Reference< css::util::XModifyBroadcaster >  xGrid(getPeer(), UNO_QUERY);
685         xGrid->removeModifyListener( &m_aModifyListeners);
686     }
687     m_aModifyListeners.removeInterface( l );
688 }
689 
690 
draw(sal_Int32 x,sal_Int32 y)691 void SAL_CALL FmXGridControl::draw( sal_Int32 x, sal_Int32 y )
692 {
693     SolarMutexGuard aGuard;
694     m_bInDraw = true;
695     UnoControl::draw(x, y);
696     m_bInDraw = false;
697 }
698 
699 
setDesignMode(sal_Bool bOn)700 void SAL_CALL FmXGridControl::setDesignMode(sal_Bool bOn)
701 {
702     css::util::ModeChangeEvent aModeChangeEvent;
703 
704     // --- <mutex_lock> ---
705     {
706         SolarMutexGuard aGuard;
707 
708         Reference< XRowSetSupplier >  xGrid(getPeer(), UNO_QUERY);
709 
710         if (xGrid.is() && (bool(bOn) != mbDesignMode || (!bOn && !xGrid->getRowSet().is())))
711         {
712             if (bOn)
713             {
714                 xGrid->setRowSet(Reference< XRowSet > ());
715             }
716             else
717             {
718                 Reference< XFormComponent >  xComp(getModel(), UNO_QUERY);
719                 if (xComp.is())
720                 {
721                     Reference< XRowSet >  xForm(xComp->getParent(), UNO_QUERY);
722                     xGrid->setRowSet(xForm);
723                 }
724             }
725 
726             // Avoid infinite recursion when calling XVclWindowPeer::setDesignMode below
727             mbDesignMode = bOn;
728 
729             Reference< XVclWindowPeer >  xVclWindowPeer( getPeer(), UNO_QUERY );
730             if (xVclWindowPeer.is())
731                 xVclWindowPeer->setDesignMode(bOn);
732         }
733         else
734         {
735             mbDesignMode = bOn;
736         }
737 
738         // dispose our current AccessibleContext, if we have one
739         // (changing the design mode implies having a new implementation for this context,
740         // so the old one must be declared DEFUNC)
741         DisposeAccessibleContext(
742                 Reference<XComponent>(maAccessibleContext, UNO_QUERY));
743         maAccessibleContext.clear();
744 
745         // prepare firing an event
746         aModeChangeEvent.Source = *this;
747         aModeChangeEvent.NewMode = mbDesignMode ? std::u16string_view( u"design" ) : std::u16string_view( u"alive" );
748     }
749 
750     // --- </mutex_lock> ---
751     maModeChangeListeners.notifyEach( &XModeChangeListener::modeChanged, aModeChangeEvent );
752 }
753 
754 // XBoundComponent
755 
addUpdateListener(const Reference<XUpdateListener> & l)756 void SAL_CALL FmXGridControl::addUpdateListener(const Reference< XUpdateListener >& l)
757 {
758     m_aUpdateListeners.addInterface( l );
759     if( getPeer().is() && m_aUpdateListeners.getLength() == 1 )
760     {
761         Reference< XBoundComponent >  xBound(getPeer(), UNO_QUERY);
762         xBound->addUpdateListener( &m_aUpdateListeners);
763     }
764 }
765 
766 
removeUpdateListener(const Reference<XUpdateListener> & l)767 void SAL_CALL FmXGridControl::removeUpdateListener(const Reference< XUpdateListener >& l)
768 {
769     if( getPeer().is() && m_aUpdateListeners.getLength() == 1 )
770     {
771         Reference< XBoundComponent >  xBound(getPeer(), UNO_QUERY);
772         xBound->removeUpdateListener( &m_aUpdateListeners);
773     }
774     m_aUpdateListeners.removeInterface( l );
775 }
776 
777 
commit()778 sal_Bool SAL_CALL FmXGridControl::commit()
779 {
780     Reference< XBoundComponent >  xBound(getPeer(), UNO_QUERY);
781     if (xBound.is())
782         return xBound->commit();
783     else
784         return true;
785 }
786 
787 // XContainer
788 
addContainerListener(const Reference<XContainerListener> & l)789 void SAL_CALL FmXGridControl::addContainerListener(const Reference< XContainerListener >& l)
790 {
791     m_aContainerListeners.addInterface( l );
792     if( getPeer().is() && m_aContainerListeners.getLength() == 1 )
793     {
794         Reference< XContainer >  xContainer(getPeer(), UNO_QUERY);
795         xContainer->addContainerListener( &m_aContainerListeners);
796     }
797 }
798 
799 
removeContainerListener(const Reference<XContainerListener> & l)800 void SAL_CALL FmXGridControl::removeContainerListener(const Reference< XContainerListener >& l)
801 {
802     if( getPeer().is() && m_aContainerListeners.getLength() == 1 )
803     {
804         Reference< XContainer >  xContainer(getPeer(), UNO_QUERY);
805         xContainer->removeContainerListener( &m_aContainerListeners);
806     }
807     m_aContainerListeners.removeInterface( l );
808 }
809 
810 
queryDispatch(const css::util::URL & aURL,const OUString & aTargetFrameName,sal_Int32 nSearchFlags)811 Reference< css::frame::XDispatch >  SAL_CALL FmXGridControl::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
812 {
813     Reference< css::frame::XDispatchProvider >  xPeerProvider(getPeer(), UNO_QUERY);
814     if (xPeerProvider.is())
815         return xPeerProvider->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
816     else
817         return Reference< css::frame::XDispatch > ();
818 }
819 
820 
queryDispatches(const Sequence<css::frame::DispatchDescriptor> & aDescripts)821 Sequence< Reference< css::frame::XDispatch > > SAL_CALL FmXGridControl::queryDispatches(const Sequence< css::frame::DispatchDescriptor>& aDescripts)
822 {
823     Reference< css::frame::XDispatchProvider >  xPeerProvider(getPeer(), UNO_QUERY);
824     if (xPeerProvider.is())
825         return xPeerProvider->queryDispatches(aDescripts);
826     else
827         return Sequence< Reference< css::frame::XDispatch > >();
828 }
829 
830 
registerDispatchProviderInterceptor(const Reference<css::frame::XDispatchProviderInterceptor> & _xInterceptor)831 void SAL_CALL FmXGridControl::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
832 {
833     Reference< css::frame::XDispatchProviderInterception >  xPeerInterception(getPeer(), UNO_QUERY);
834     if (xPeerInterception.is())
835         xPeerInterception->registerDispatchProviderInterceptor(_xInterceptor);
836 }
837 
838 
releaseDispatchProviderInterceptor(const Reference<css::frame::XDispatchProviderInterceptor> & _xInterceptor)839 void SAL_CALL FmXGridControl::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
840 {
841     Reference< css::frame::XDispatchProviderInterception >  xPeerInterception(getPeer(), UNO_QUERY);
842     if (xPeerInterception.is())
843         xPeerInterception->releaseDispatchProviderInterceptor(_xInterceptor);
844 }
845 
846 
addGridControlListener(const Reference<XGridControlListener> & _listener)847 void SAL_CALL FmXGridControl::addGridControlListener( const Reference< XGridControlListener >& _listener )
848 {
849     ::osl::MutexGuard aGuard( GetMutex() );
850 
851     m_aGridControlListeners.addInterface( _listener );
852     if ( getPeer().is() && 1 == m_aGridControlListeners.getLength() )
853     {
854         Reference< XGridControl > xPeerGrid( getPeer(), UNO_QUERY );
855         if ( xPeerGrid.is() )
856             xPeerGrid->addGridControlListener( &m_aGridControlListeners );
857     }
858 }
859 
860 
removeGridControlListener(const Reference<XGridControlListener> & _listener)861 void SAL_CALL FmXGridControl::removeGridControlListener( const Reference< XGridControlListener >& _listener )
862 {
863     ::osl::MutexGuard aGuard( GetMutex() );
864 
865     if( getPeer().is() && 1 == m_aGridControlListeners.getLength() )
866     {
867         Reference< XGridControl > xPeerGrid( getPeer(), UNO_QUERY );
868         if ( xPeerGrid.is() )
869             xPeerGrid->removeGridControlListener( &m_aGridControlListeners );
870     }
871 
872     m_aGridControlListeners.removeInterface( _listener );
873 }
874 
875 
getCurrentColumnPosition()876 sal_Int16 SAL_CALL FmXGridControl::getCurrentColumnPosition()
877 {
878     Reference< XGridControl > xGrid( getPeer(), UNO_QUERY );
879     return xGrid.is() ? xGrid->getCurrentColumnPosition() : -1;
880 }
881 
882 
setCurrentColumnPosition(sal_Int16 nPos)883 void SAL_CALL FmXGridControl::setCurrentColumnPosition(sal_Int16 nPos)
884 {
885     Reference< XGridControl > xGrid( getPeer(), UNO_QUERY );
886     if ( xGrid.is() )
887     {
888         SolarMutexGuard aGuard;
889         xGrid->setCurrentColumnPosition( nPos );
890     }
891 }
892 
893 // XElementAccess
894 
hasElements()895 sal_Bool SAL_CALL FmXGridControl::hasElements()
896 {
897     Reference< XElementAccess >  xPeer(getPeer(), UNO_QUERY);
898     return xPeer.is() && xPeer->hasElements();
899 }
900 
901 
getElementType()902 Type SAL_CALL FmXGridControl::getElementType(  )
903 {
904     return cppu::UnoType<css::awt::XTextComponent>::get();
905 }
906 
907 // XEnumerationAccess
908 
createEnumeration()909 Reference< XEnumeration >  SAL_CALL FmXGridControl::createEnumeration()
910 {
911     Reference< XEnumerationAccess >  xPeer(getPeer(), UNO_QUERY);
912     if (xPeer.is())
913         return xPeer->createEnumeration();
914     else
915         return new ::comphelper::OEnumerationByIndex(this);
916 }
917 
918 // XIndexAccess
919 
getCount()920 sal_Int32 SAL_CALL FmXGridControl::getCount()
921 {
922     Reference< XIndexAccess >  xPeer(getPeer(), UNO_QUERY);
923     return xPeer.is() ? xPeer->getCount() : 0;
924 }
925 
926 
getByIndex(sal_Int32 _nIndex)927 Any SAL_CALL FmXGridControl::getByIndex(sal_Int32 _nIndex)
928 {
929     Reference< XIndexAccess >  xPeer(getPeer(), UNO_QUERY);
930     if (!xPeer.is())
931         throw IndexOutOfBoundsException();
932 
933     return xPeer->getByIndex(_nIndex);
934 }
935 
936 // css::util::XModeSelector
937 
setMode(const OUString & Mode)938 void SAL_CALL FmXGridControl::setMode(const OUString& Mode)
939 {
940     Reference< css::util::XModeSelector >  xPeer(getPeer(), UNO_QUERY);
941     if (!xPeer.is())
942         throw NoSupportException();
943 
944     xPeer->setMode(Mode);
945 }
946 
947 
getMode()948 OUString SAL_CALL FmXGridControl::getMode()
949 {
950     Reference< css::util::XModeSelector >  xPeer(getPeer(), UNO_QUERY);
951     return xPeer.is() ? xPeer->getMode() : OUString();
952 }
953 
954 
getSupportedModes()955 css::uno::Sequence<OUString> SAL_CALL FmXGridControl::getSupportedModes()
956 {
957     Reference< css::util::XModeSelector >  xPeer(getPeer(), UNO_QUERY);
958     return xPeer.is() ? xPeer->getSupportedModes() : css::uno::Sequence<OUString>();
959 }
960 
961 
supportsMode(const OUString & Mode)962 sal_Bool SAL_CALL FmXGridControl::supportsMode(const OUString& Mode)
963 {
964     Reference< css::util::XModeSelector >  xPeer(getPeer(), UNO_QUERY);
965     return xPeer.is() && xPeer->supportsMode(Mode);
966 }
967 
setFocus()968 void SAL_CALL FmXGridControl::setFocus()
969 {
970     FmXGridPeer* pPeer = comphelper::getUnoTunnelImplementation<FmXGridPeer>(getPeer());
971     if (pPeer)
972     {
973         VclPtr<FmGridControl> xGrid = pPeer->GetAs<FmGridControl>();
974         bool bAlreadyHasFocus = xGrid->HasChildPathFocus() || xGrid->ControlHasFocus();
975         // if the focus is already in the control don't grab focus again which
976         // would grab focus away from any native widgets hosted in the control
977         if (bAlreadyHasFocus)
978             return;
979     }
980     UnoControl::setFocus();
981 }
982 
983 // helper class which prevents that in the peer's header the FmGridListener must be known
984 class FmXGridPeer::GridListenerDelegator : public FmGridListener
985 {
986 protected:
987     FmXGridPeer*        m_pPeer;
988 
989 public:
990     explicit GridListenerDelegator( FmXGridPeer* _pPeer );
991     virtual ~GridListenerDelegator();
992 
993 protected:
994     virtual void selectionChanged() override;
995     virtual void columnChanged() override;
996 };
997 
998 
GridListenerDelegator(FmXGridPeer * _pPeer)999 FmXGridPeer::GridListenerDelegator::GridListenerDelegator(FmXGridPeer* _pPeer)
1000     :m_pPeer(_pPeer)
1001 {
1002     DBG_ASSERT(m_pPeer, "GridListenerDelegator::GridListenerDelegator");
1003 }
1004 
~GridListenerDelegator()1005 FmXGridPeer::GridListenerDelegator::~GridListenerDelegator()
1006 {
1007 }
1008 
1009 
selectionChanged()1010 void FmXGridPeer::GridListenerDelegator::selectionChanged()
1011 {
1012     m_pPeer->selectionChanged();
1013 }
1014 
1015 
columnChanged()1016 void FmXGridPeer::GridListenerDelegator::columnChanged()
1017 {
1018     m_pPeer->columnChanged();
1019 }
1020 
selectionChanged()1021 void FmXGridPeer::selectionChanged()
1022 {
1023     EventObject aSource;
1024     aSource.Source = static_cast< ::cppu::OWeakObject* >(this);
1025     m_aSelectionListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aSource);
1026 }
1027 
1028 
columnChanged()1029 void FmXGridPeer::columnChanged()
1030 {
1031     EventObject aEvent( *this );
1032     m_aGridControlListeners.notifyEach( &XGridControlListener::columnChanged, aEvent );
1033 }
1034 
1035 
FmXGridPeer(const Reference<XComponentContext> & _rxContext)1036 FmXGridPeer::FmXGridPeer(const Reference< XComponentContext >& _rxContext)
1037             :m_xContext(_rxContext)
1038             ,m_aModifyListeners(m_aMutex)
1039             ,m_aUpdateListeners(m_aMutex)
1040             ,m_aContainerListeners(m_aMutex)
1041             ,m_aSelectionListeners(m_aMutex)
1042             ,m_aGridControlListeners(m_aMutex)
1043             ,m_aMode("DataMode")
1044             ,m_nCursorListening(0)
1045             ,m_bInterceptingDispatch(false)
1046 {
1047     // Create must be called after this constructor
1048     m_pGridListener.reset( new GridListenerDelegator( this ) );
1049 }
1050 
1051 
imp_CreateControl(vcl::Window * pParent,WinBits nStyle)1052 VclPtr<FmGridControl> FmXGridPeer::imp_CreateControl(vcl::Window* pParent, WinBits nStyle)
1053 {
1054     return VclPtr<FmGridControl>::Create(m_xContext, pParent, this, nStyle);
1055 }
1056 
1057 
Create(vcl::Window * pParent,WinBits nStyle)1058 void FmXGridPeer::Create(vcl::Window* pParent, WinBits nStyle)
1059 {
1060     VclPtr<FmGridControl> pWin = imp_CreateControl(pParent, nStyle);
1061     DBG_ASSERT(pWin != nullptr, "FmXGridPeer::Create : imp_CreateControl didn't return a control !");
1062 
1063     pWin->SetStateProvider(LINK(this, FmXGridPeer, OnQueryGridSlotState));
1064     pWin->SetSlotExecutor(LINK(this, FmXGridPeer, OnExecuteGridSlot));
1065 
1066     // want to hear about row selections
1067     pWin->setGridListener( m_pGridListener.get() );
1068 
1069     // Init must always be called
1070     pWin->Init();
1071     pWin->SetComponentInterface(this);
1072 
1073     getSupportedURLs();
1074 }
1075 
~FmXGridPeer()1076 FmXGridPeer::~FmXGridPeer()
1077 {
1078     setRowSet(Reference< XRowSet > ());
1079     setColumns(Reference< XIndexContainer > ());
1080 }
1081 
1082 UNO3_GETIMPLEMENTATION2_IMPL(FmXGridPeer, VCLXWindow);
1083 
1084 // XEventListener
1085 
disposing(const EventObject & e)1086 void FmXGridPeer::disposing(const EventObject& e)
1087 {
1088     bool bKnownSender = false;
1089 
1090     Reference< XIndexContainer >  xCols( e.Source, UNO_QUERY );
1091     if ( xCols.is() )
1092     {
1093         setColumns(Reference< XIndexContainer > ());
1094         bKnownSender = true;
1095     }
1096 
1097     Reference< XRowSet >  xCursor(e.Source, UNO_QUERY);
1098     if (xCursor.is())
1099     {
1100         setRowSet( m_xCursor );
1101         m_xCursor = nullptr;
1102         bKnownSender = true;
1103     }
1104 
1105 
1106     if ( !bKnownSender && m_pDispatchers )
1107     {
1108         const Sequence< URL>& aSupportedURLs = getSupportedURLs();
1109         const URL* pSupportedURLs = aSupportedURLs.getConstArray();
1110         for ( sal_Int32 i=0; i < ( aSupportedURLs.getLength() ) && !bKnownSender; ++i, ++pSupportedURLs )
1111         {
1112             if ( m_pDispatchers[i] == e.Source )
1113             {
1114                 m_pDispatchers[i]->removeStatusListener( static_cast< css::frame::XStatusListener* >( this ), *pSupportedURLs );
1115                 m_pDispatchers[i] = nullptr;
1116                 m_pStateCache[i] = false;
1117                 bKnownSender = true;
1118             }
1119         }
1120     }
1121 
1122     if ( !bKnownSender )
1123         VCLXWindow::disposing(e);
1124 }
1125 
1126 
addModifyListener(const Reference<css::util::XModifyListener> & l)1127 void FmXGridPeer::addModifyListener(const Reference< css::util::XModifyListener >& l)
1128 {
1129     m_aModifyListeners.addInterface( l );
1130 }
1131 
1132 
removeModifyListener(const Reference<css::util::XModifyListener> & l)1133 void FmXGridPeer::removeModifyListener(const Reference< css::util::XModifyListener >& l)
1134 {
1135     m_aModifyListeners.removeInterface( l );
1136 }
1137 
1138 
1139 #define LAST_KNOWN_TYPE     FormComponentType::PATTERNFIELD
queryFieldDataType(const Type & xType)1140 Sequence< sal_Bool > SAL_CALL FmXGridPeer::queryFieldDataType( const Type& xType )
1141 {
1142     // a 'conversion table'
1143     static const bool bCanConvert[LAST_KNOWN_TYPE][4] =
1144     {
1145         { false, false, false, false }, //  FormComponentType::CONTROL
1146         { false, false, false, false }, //  FormComponentType::COMMANDBUTTON
1147         { false, false, false, false }, //  FormComponentType::RADIOBUTTON
1148         { false, false, false, false }, //  FormComponentType::IMAGEBUTTON
1149         { false, false, false, true  }, //  FormComponentType::CHECKBOX
1150         { false, false, false, false }, //  FormComponentType::LISTBOX
1151         { false, false, false, false }, //  FormComponentType::COMBOBOX
1152         { false, false, false, false }, //  FormComponentType::GROUPBOX
1153         { true , false, false, false }, //  FormComponentType::TEXTFIELD
1154         { false, false, false, false }, //  FormComponentType::FIXEDTEXT
1155         { false, false, false, false }, //  FormComponentType::GRIDCONTROL
1156         { false, false, false, false }, //  FormComponentType::FILECONTROL
1157         { false, false, false, false }, //  FormComponentType::HIDDENCONTROL
1158         { false, false, false, false }, //  FormComponentType::IMAGECONTROL
1159         { true , true , true , false }, //  FormComponentType::DATEFIELD
1160         { true , true , false, false }, //  FormComponentType::TIMEFIELD
1161         { true , true , false, false }, //  FormComponentType::NUMERICFIELD
1162         { true , true , false, false }, //  FormComponentType::CURRENCYFIELD
1163         { true , false, false, false }  //  FormComponentType::PATTERNFIELD
1164     };
1165 
1166 
1167     sal_Int16 nMapColumn = -1;
1168     switch (xType.getTypeClass())
1169     {
1170         case TypeClass_STRING           : nMapColumn = 0; break;
1171         case TypeClass_FLOAT:
1172         case TypeClass_DOUBLE           : nMapColumn = 1; break;
1173         case TypeClass_SHORT:
1174         case TypeClass_LONG:
1175         case TypeClass_UNSIGNED_LONG:
1176         case TypeClass_UNSIGNED_SHORT   : nMapColumn = 2; break;
1177         case TypeClass_BOOLEAN          : nMapColumn = 3; break;
1178         default:
1179             break;
1180     }
1181 
1182     Reference< XIndexContainer >  xColumns = getColumns();
1183 
1184     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1185     sal_Int32 nColumns = pGrid->GetViewColCount();
1186 
1187     std::vector< std::unique_ptr<DbGridColumn> > const & aColumns = pGrid->GetColumns();
1188 
1189     Sequence<sal_Bool> aReturnSequence(nColumns);
1190     sal_Bool* pReturnArray = aReturnSequence.getArray();
1191 
1192     bool bRequestedAsAny = (xType.getTypeClass() == TypeClass_ANY);
1193 
1194     DbGridColumn* pCol;
1195     Reference< css::sdb::XColumn >  xFieldContent;
1196     Reference< XPropertySet >  xCurrentColumn;
1197     for (sal_Int32 i=0; i<nColumns; ++i)
1198     {
1199         if (bRequestedAsAny)
1200         {
1201             pReturnArray[i] = true;
1202             continue;
1203         }
1204 
1205         pReturnArray[i] = false;
1206 
1207         sal_uInt16 nModelPos = pGrid->GetModelColumnPos(pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(i)));
1208         DBG_ASSERT(nModelPos != sal_uInt16(-1), "FmXGridPeer::queryFieldDataType : no model pos !");
1209 
1210         pCol = aColumns[ nModelPos ].get();
1211         const DbGridRowRef xRow = pGrid->GetSeekRow();
1212         xFieldContent = (xRow.is() && xRow->HasField(pCol->GetFieldPos())) ? xRow->GetField(pCol->GetFieldPos()).getColumn() : Reference< css::sdb::XColumn > ();
1213         if (!xFieldContent.is())
1214             // can't supply anything without a field content
1215             // FS - 07.12.99 - 54391
1216             continue;
1217 
1218         xColumns->getByIndex(nModelPos) >>= xCurrentColumn;
1219         if (!::comphelper::hasProperty(FM_PROP_CLASSID, xCurrentColumn))
1220             continue;
1221 
1222         sal_Int16 nClassId = sal_Int16();
1223         xCurrentColumn->getPropertyValue(FM_PROP_CLASSID) >>= nClassId;
1224         if (nClassId>LAST_KNOWN_TYPE)
1225             continue;
1226         DBG_ASSERT(nClassId>0, "FmXGridPeer::queryFieldDataType : somebody changed the definition of the FormComponentType enum !");
1227 
1228         if (nMapColumn != -1)
1229             pReturnArray[i] = bCanConvert[nClassId-1][nMapColumn];
1230     }
1231 
1232     return aReturnSequence;
1233 }
1234 
1235 
queryFieldData(sal_Int32 nRow,const Type & xType)1236 Sequence< Any > SAL_CALL FmXGridPeer::queryFieldData( sal_Int32 nRow, const Type& xType )
1237 {
1238     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1239     DBG_ASSERT(pGrid && pGrid->IsOpen(), "FmXGridPeer::queryFieldData : have no valid grid window !");
1240     if (!pGrid || !pGrid->IsOpen())
1241         return Sequence< Any>();
1242 
1243     // move the control to the specified row
1244     if (!pGrid->SeekRow(nRow))
1245     {
1246         throw IllegalArgumentException();
1247     }
1248 
1249     // don't use GetCurrentRow as this isn't affected by the above SeekRow
1250     // FS - 30.09.99 - 68644
1251     DbGridRowRef xPaintRow = pGrid->GetPaintRow();
1252     ENSURE_OR_THROW( xPaintRow.is(), "invalid paint row" );
1253 
1254     // I need the columns of the control for GetFieldText
1255     std::vector< std::unique_ptr<DbGridColumn> > const & aColumns = pGrid->GetColumns();
1256 
1257     // and through all the columns
1258     sal_Int32 nColumnCount = pGrid->GetViewColCount();
1259 
1260     Sequence< Any> aReturnSequence(nColumnCount);
1261     Any* pReturnArray = aReturnSequence.getArray();
1262 
1263     bool bRequestedAsAny = (xType.getTypeClass() == TypeClass_ANY);
1264     Reference< css::sdb::XColumn >  xFieldContent;
1265     for (sal_Int32 i=0; i < nColumnCount; ++i)
1266     {
1267         sal_uInt16 nModelPos = pGrid->GetModelColumnPos(pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(i)));
1268         DBG_ASSERT(nModelPos != sal_uInt16(-1), "FmXGridPeer::queryFieldData : invalid model pos !");
1269 
1270         // don't use GetCurrentFieldValue to determine the field content as this isn't affected by the above SeekRow
1271         // FS - 30.09.99 - 68644
1272         DbGridColumn* pCol = aColumns[ nModelPos ].get();
1273         xFieldContent = xPaintRow->HasField( pCol->GetFieldPos() )
1274                     ?   xPaintRow->GetField( pCol->GetFieldPos() ).getColumn()
1275                     :   Reference< XColumn > ();
1276 
1277         if ( !xFieldContent.is() )
1278             continue;
1279 
1280         if (bRequestedAsAny)
1281         {
1282             Reference< XPropertySet >  xFieldSet(xFieldContent, UNO_QUERY);
1283             pReturnArray[i] = xFieldSet->getPropertyValue(FM_PROP_VALUE);
1284         }
1285         else
1286         {
1287             switch (xType.getTypeClass())
1288             {
1289                 // Strings are dealt with directly by the GetFieldText
1290                 case TypeClass_STRING           :
1291                 {
1292                     OUString sText = aColumns[ nModelPos ]->GetCellText( xPaintRow.get(), pGrid->getNumberFormatter() );
1293                     pReturnArray[i] <<= sText;
1294                 }
1295                 break;
1296                 // everything else is requested in the DatabaseVariant
1297                 case TypeClass_FLOAT            : pReturnArray[i] <<= xFieldContent->getFloat(); break;
1298                 case TypeClass_DOUBLE           : pReturnArray[i] <<= xFieldContent->getDouble(); break;
1299                 case TypeClass_SHORT            : pReturnArray[i] <<= xFieldContent->getShort(); break;
1300                 case TypeClass_LONG             : pReturnArray[i] <<= static_cast<sal_Int32>(xFieldContent->getLong()); break;
1301                 case TypeClass_UNSIGNED_SHORT   : pReturnArray[i] <<= static_cast<sal_uInt16>(xFieldContent->getShort()); break;
1302                 case TypeClass_UNSIGNED_LONG    : pReturnArray[i] <<= static_cast<sal_uInt32>(xFieldContent->getLong()); break;
1303                 case TypeClass_BOOLEAN          : pReturnArray[i] <<= xFieldContent->getBoolean(); break;
1304                 default:
1305                 {
1306                     throw IllegalArgumentException();
1307                 }
1308             }
1309         }
1310     }
1311     return aReturnSequence;
1312 }
1313 
1314 
CellModified()1315 void FmXGridPeer::CellModified()
1316 {
1317     EventObject aEvt;
1318     aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
1319     m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
1320 }
1321 
1322 // XPropertyChangeListener
1323 
propertyChange(const PropertyChangeEvent & evt)1324 void FmXGridPeer::propertyChange(const PropertyChangeEvent& evt)
1325 {
1326     SolarMutexGuard aGuard;
1327         // want to do a lot of VCL stuff here ...
1328         // this should not be (deadlock) critical, as by definition, every component should release
1329         // any own mutexes before notifying
1330 
1331     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1332     if (!pGrid)
1333         return;
1334 
1335     // Database event
1336     if (evt.PropertyName == FM_PROP_VALUE || m_xCursor == evt.Source)
1337         pGrid->propertyChange(evt);
1338     else if (pGrid && m_xColumns.is() && m_xColumns->hasElements())
1339     {
1340         // next find which column has changed
1341         css::uno::Reference<css::uno::XInterface> xCurrent;
1342         sal_Int32 i;
1343 
1344         for ( i = 0; i < m_xColumns->getCount(); i++)
1345         {
1346             xCurrent.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
1347             if (evt.Source == xCurrent)
1348                 break;
1349         }
1350 
1351         if (i >= m_xColumns->getCount())
1352             // this is valid because we are listening at the cursor, too (RecordCount, -status, edit mode)
1353             return;
1354 
1355         sal_uInt16 nId = pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i));
1356         bool bInvalidateColumn = false;
1357 
1358         if (evt.PropertyName == FM_PROP_LABEL)
1359         {
1360             OUString aName = ::comphelper::getString(evt.NewValue);
1361             if (aName != pGrid->GetColumnTitle(nId))
1362                 pGrid->SetColumnTitle(nId, aName);
1363         }
1364         else if (evt.PropertyName == FM_PROP_WIDTH)
1365         {
1366             sal_Int32 nWidth = 0;
1367             if (evt.NewValue.getValueType().getTypeClass() == TypeClass_VOID)
1368                 nWidth = pGrid->GetDefaultColumnWidth(pGrid->GetColumnTitle(nId));
1369                 // GetDefaultColumnWidth already considered the zoom factor
1370             else
1371             {
1372                 sal_Int32 nTest = 0;
1373                 if (evt.NewValue >>= nTest)
1374                 {
1375                     nWidth = pGrid->LogicToPixel(Point(nTest, 0), MapMode(MapUnit::Map10thMM)).X();
1376                     // take the zoom factor into account
1377                     nWidth = pGrid->CalcZoom(nWidth);
1378                 }
1379             }
1380             if (nWidth != (sal_Int32(pGrid->GetColumnWidth(nId))))
1381             {
1382                 if (pGrid->IsEditing())
1383                 {
1384                     pGrid->DeactivateCell();
1385                     pGrid->ActivateCell();
1386                 }
1387                 pGrid->SetColumnWidth(nId, nWidth);
1388             }
1389         }
1390         else if (evt.PropertyName == FM_PROP_HIDDEN)
1391         {
1392             DBG_ASSERT(evt.NewValue.getValueType().getTypeClass() == TypeClass_BOOLEAN,
1393                 "FmXGridPeer::propertyChange : the property 'hidden' should be of type boolean !");
1394             if (::comphelper::getBOOL(evt.NewValue))
1395                 pGrid->HideColumn(nId);
1396             else
1397                 pGrid->ShowColumn(nId);
1398         }
1399         else if (evt.PropertyName == FM_PROP_ALIGN)
1400         {
1401             // in design mode it doesn't matter
1402             if (!isDesignMode())
1403             {
1404                 DbGridColumn* pCol = pGrid->GetColumns()[i].get();
1405 
1406                 pCol->SetAlignmentFromModel(-1);
1407                 bInvalidateColumn = true;
1408             }
1409         }
1410         else if (evt.PropertyName == FM_PROP_FORMATKEY)
1411         {
1412             if (!isDesignMode())
1413                 bInvalidateColumn = true;
1414         }
1415 
1416         // need to invalidate the affected column ?
1417         if (bInvalidateColumn)
1418         {
1419             bool bWasEditing = pGrid->IsEditing();
1420             if (bWasEditing)
1421                 pGrid->DeactivateCell();
1422 
1423             ::tools::Rectangle aColRect = pGrid->GetFieldRect(nId);
1424             aColRect.SetTop( 0 );
1425             aColRect.SetBottom( pGrid->GetSizePixel().Height() );
1426             pGrid->Invalidate(aColRect);
1427 
1428             if (bWasEditing)
1429                 pGrid->ActivateCell();
1430         }
1431     }
1432 }
1433 
1434 // XBoundComponent
1435 
addUpdateListener(const Reference<XUpdateListener> & l)1436 void FmXGridPeer::addUpdateListener(const Reference< XUpdateListener >& l)
1437 {
1438     m_aUpdateListeners.addInterface(l);
1439 }
1440 
1441 
removeUpdateListener(const Reference<XUpdateListener> & l)1442 void FmXGridPeer::removeUpdateListener(const Reference< XUpdateListener >& l)
1443 {
1444     m_aUpdateListeners.removeInterface(l);
1445 }
1446 
1447 
commit()1448 sal_Bool FmXGridPeer::commit()
1449 {
1450     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1451     if (!m_xCursor.is() || !pGrid)
1452         return true;
1453 
1454     EventObject aEvt(static_cast< ::cppu::OWeakObject* >(this));
1455     ::comphelper::OInterfaceIteratorHelper2 aIter(m_aUpdateListeners);
1456     bool bCancel = false;
1457     while (aIter.hasMoreElements() && !bCancel)
1458         if ( !static_cast< XUpdateListener* >( aIter.next() )->approveUpdate( aEvt ) )
1459             bCancel = true;
1460 
1461     if (!bCancel)
1462         bCancel = !pGrid->commit();
1463 
1464     if (!bCancel)
1465         m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvt );
1466     return !bCancel;
1467 }
1468 
1469 
cursorMoved(const EventObject & _rEvent)1470 void FmXGridPeer::cursorMoved(const EventObject& _rEvent)
1471 {
1472     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1473     // we are not interested in moving to insert row only in the reset event
1474     // which is fired after positioning and the insert row
1475     if (pGrid && pGrid->IsOpen() && !::comphelper::getBOOL(Reference< XPropertySet > (_rEvent.Source, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_ISNEW)))
1476         pGrid->positioned();
1477 }
1478 
1479 
rowChanged(const EventObject &)1480 void FmXGridPeer::rowChanged(const EventObject& /*_rEvent*/)
1481 {
1482     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1483     if (pGrid && pGrid->IsOpen())
1484     {
1485         if (m_xCursor->rowUpdated() && !pGrid->IsCurrentAppending())
1486             pGrid->RowModified(pGrid->GetCurrentPos());
1487         else if (m_xCursor->rowInserted())
1488             pGrid->inserted();
1489     }
1490 }
1491 
1492 
rowSetChanged(const EventObject &)1493 void FmXGridPeer::rowSetChanged(const EventObject& /*event*/)
1494 {
1495     // not interested in ...
1496     // (our parent is a form which means we get a loaded or reloaded after this rowSetChanged)
1497 }
1498 
1499 // XLoadListener
1500 
loaded(const EventObject &)1501 void FmXGridPeer::loaded(const EventObject& /*rEvent*/)
1502 {
1503     updateGrid(m_xCursor);
1504 }
1505 
1506 
unloaded(const EventObject &)1507 void FmXGridPeer::unloaded(const EventObject& /*rEvent*/)
1508 {
1509     updateGrid( Reference< XRowSet > (nullptr) );
1510 }
1511 
1512 
reloading(const EventObject &)1513 void FmXGridPeer::reloading(const EventObject& /*aEvent*/)
1514 {
1515     // empty the grid
1516     updateGrid( Reference< XRowSet > (nullptr) );
1517 }
1518 
1519 
unloading(const EventObject &)1520 void FmXGridPeer::unloading(const EventObject& /*aEvent*/)
1521 {
1522     // empty the grid
1523     updateGrid( Reference< XRowSet > (nullptr) );
1524 }
1525 
1526 
reloaded(const EventObject & aEvent)1527 void FmXGridPeer::reloaded(const EventObject& aEvent)
1528 {
1529     {
1530         const sal_Int32 cnt = m_xColumns->getCount();
1531         for(sal_Int32 i=0; i<cnt; ++i)
1532         {
1533             Reference< XLoadListener> xll(m_xColumns->getByIndex(i), UNO_QUERY);
1534             if(xll.is())
1535             {
1536                 xll->reloaded(aEvent);
1537             }
1538         }
1539     }
1540     updateGrid(m_xCursor);
1541 }
1542 
1543 // XGridPeer
1544 
getColumns()1545 Reference< XIndexContainer >  FmXGridPeer::getColumns()
1546 {
1547     return m_xColumns;
1548 }
1549 
1550 
addColumnListeners(const Reference<XPropertySet> & xCol)1551 void FmXGridPeer::addColumnListeners(const Reference< XPropertySet >& xCol)
1552 {
1553     static const std::u16string_view aPropsListenedTo[] =
1554     {
1555         u"" FM_PROP_LABEL, u"" FM_PROP_WIDTH, u"" FM_PROP_HIDDEN, u"" FM_PROP_ALIGN,
1556         u"" FM_PROP_FORMATKEY
1557     };
1558 
1559     // as not all properties have to be supported by all columns we have to check this
1560     // before adding a listener
1561     Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo();
1562     for (size_t i=0; i<SAL_N_ELEMENTS(aPropsListenedTo); ++i)
1563     {
1564         if ( xInfo->hasPropertyByName( OUString(aPropsListenedTo[i]) ) )
1565         {
1566             Property aPropDesc = xInfo->getPropertyByName( OUString(aPropsListenedTo[i]) );
1567             if ( 0 != ( aPropDesc.Attributes & PropertyAttribute::BOUND ) )
1568                 xCol->addPropertyChangeListener( OUString(aPropsListenedTo[i]), this );
1569         }
1570     }
1571 }
1572 
1573 
removeColumnListeners(const Reference<XPropertySet> & xCol)1574 void FmXGridPeer::removeColumnListeners(const Reference< XPropertySet >& xCol)
1575 {
1576     // the same props as in addColumnListeners... linux has problems with global static UStrings, so
1577     // we have to do it this way...
1578     static const std::u16string_view aPropsListenedTo[] =
1579     {
1580         u"" FM_PROP_LABEL, u"" FM_PROP_WIDTH, u"" FM_PROP_HIDDEN, u"" FM_PROP_ALIGN,
1581         u"" FM_PROP_FORMATKEY
1582     };
1583 
1584     Reference< XPropertySetInfo >  xInfo = xCol->getPropertySetInfo();
1585     for (const auto & i : aPropsListenedTo)
1586         if (xInfo->hasPropertyByName(OUString(i)))
1587             xCol->removePropertyChangeListener(OUString(i), this);
1588 }
1589 
1590 
setColumns(const Reference<XIndexContainer> & Columns)1591 void FmXGridPeer::setColumns(const Reference< XIndexContainer >& Columns)
1592 {
1593     SolarMutexGuard aGuard;
1594 
1595     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1596 
1597     if (m_xColumns.is())
1598     {
1599         Reference< XPropertySet > xCol;
1600         for (sal_Int32 i = 0; i < m_xColumns->getCount(); i++)
1601         {
1602             xCol.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
1603             removeColumnListeners(xCol);
1604         }
1605         Reference< XContainer >  xContainer(m_xColumns, UNO_QUERY);
1606         xContainer->removeContainerListener(this);
1607 
1608         Reference< XSelectionSupplier >  xSelSupplier(m_xColumns, UNO_QUERY);
1609         xSelSupplier->removeSelectionChangeListener(this);
1610 
1611         Reference< XReset >  xColumnReset(m_xColumns, UNO_QUERY);
1612         if (xColumnReset.is())
1613             xColumnReset->removeResetListener(static_cast<XResetListener*>(this));
1614     }
1615     if (Columns.is())
1616     {
1617         Reference< XContainer >  xContainer(Columns, UNO_QUERY);
1618         xContainer->addContainerListener(this);
1619 
1620         Reference< XSelectionSupplier >  xSelSupplier(Columns, UNO_QUERY);
1621         xSelSupplier->addSelectionChangeListener(this);
1622 
1623         Reference< XPropertySet >  xCol;
1624         for (sal_Int32 i = 0; i < Columns->getCount(); i++)
1625         {
1626             xCol.set(Columns->getByIndex(i), css::uno::UNO_QUERY);
1627             addColumnListeners(xCol);
1628         }
1629 
1630         Reference< XReset >  xColumnReset(Columns, UNO_QUERY);
1631         if (xColumnReset.is())
1632             xColumnReset->addResetListener(static_cast<XResetListener*>(this));
1633     }
1634     m_xColumns = Columns;
1635     if (pGrid)
1636     {
1637         pGrid->InitColumnsByModels(m_xColumns);
1638 
1639         if (m_xColumns.is())
1640         {
1641             EventObject aEvt(m_xColumns);
1642             selectionChanged(aEvt);
1643         }
1644     }
1645 }
1646 
1647 
setDesignMode(sal_Bool bOn)1648 void FmXGridPeer::setDesignMode(sal_Bool bOn)
1649 {
1650     if (bOn != isDesignMode())
1651     {
1652         VclPtr<vcl::Window> pWin = GetWindow();
1653         if (pWin)
1654             static_cast<FmGridControl*>(pWin.get())->SetDesignMode(bOn);
1655     }
1656 
1657     if (bOn)
1658         DisConnectFromDispatcher();
1659     else
1660         UpdateDispatches(); // will connect if not already connected and just update else
1661 }
1662 
1663 
isDesignMode()1664 sal_Bool FmXGridPeer::isDesignMode()
1665 {
1666     VclPtr<vcl::Window> pWin = GetWindow();
1667     if (pWin)
1668         return static_cast<FmGridControl*>(pWin.get())->IsDesignMode();
1669     else
1670         return false;
1671 }
1672 
1673 
elementInserted(const ContainerEvent & evt)1674 void FmXGridPeer::elementInserted(const ContainerEvent& evt)
1675 {
1676     SolarMutexGuard aGuard;
1677 
1678     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1679     // take handle column into account
1680     if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove() || m_xColumns->getCount() == static_cast<sal_Int32>(pGrid->GetModelColCount()))
1681         return;
1682 
1683     Reference< XPropertySet >  xNewColumn(evt.Element, css::uno::UNO_QUERY);
1684     addColumnListeners(xNewColumn);
1685 
1686     OUString aName = ::comphelper::getString(xNewColumn->getPropertyValue(FM_PROP_LABEL));
1687     Any aWidth = xNewColumn->getPropertyValue(FM_PROP_WIDTH);
1688     sal_Int32 nWidth = 0;
1689     if (aWidth >>= nWidth)
1690         nWidth = pGrid->LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
1691 
1692     pGrid->AppendColumn(aName, static_cast<sal_uInt16>(nWidth), static_cast<sal_Int16>(::comphelper::getINT32(evt.Accessor)));
1693 
1694     // now set the column
1695     DbGridColumn* pCol = pGrid->GetColumns()[ ::comphelper::getINT32(evt.Accessor) ].get();
1696     pCol->setModel(xNewColumn);
1697 
1698     Any aHidden = xNewColumn->getPropertyValue(FM_PROP_HIDDEN);
1699     if (::comphelper::getBOOL(aHidden))
1700         pGrid->HideColumn(pCol->GetId());
1701 
1702     FormControlFactory( m_xContext ).initializeTextFieldLineEnds( xNewColumn );
1703 }
1704 
1705 
elementReplaced(const ContainerEvent & evt)1706 void FmXGridPeer::elementReplaced(const ContainerEvent& evt)
1707 {
1708     SolarMutexGuard aGuard;
1709 
1710     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1711 
1712     // take handle column into account
1713     if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove())
1714         return;
1715 
1716     Reference< XPropertySet > xNewColumn(evt.Element, css::uno::UNO_QUERY);
1717     Reference< XPropertySet > xOldColumn(
1718         evt.ReplacedElement, css::uno::UNO_QUERY);
1719 
1720     bool bWasEditing = pGrid->IsEditing();
1721     if (bWasEditing)
1722         pGrid->DeactivateCell();
1723 
1724     pGrid->RemoveColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(::comphelper::getINT32(evt.Accessor))));
1725 
1726     removeColumnListeners(xOldColumn);
1727     addColumnListeners(xNewColumn);
1728 
1729     OUString aName = ::comphelper::getString(xNewColumn->getPropertyValue(FM_PROP_LABEL));
1730     Any aWidth = xNewColumn->getPropertyValue(FM_PROP_WIDTH);
1731     sal_Int32 nWidth = 0;
1732     if (aWidth >>= nWidth)
1733         nWidth = pGrid->LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
1734     sal_uInt16 nNewId = pGrid->AppendColumn(aName, static_cast<sal_uInt16>(nWidth), static_cast<sal_Int16>(::comphelper::getINT32(evt.Accessor)));
1735     sal_uInt16 nNewPos = pGrid->GetModelColumnPos(nNewId);
1736 
1737     // set the model of the new column
1738     DbGridColumn* pCol = pGrid->GetColumns()[ nNewPos ].get();
1739 
1740     // for initializing this grid column, we need the fields of the grid's data source
1741     Reference< XColumnsSupplier > xSuppColumns;
1742     CursorWrapper* pGridDataSource = pGrid->getDataSource();
1743     if ( pGridDataSource )
1744         xSuppColumns.set(Reference< XInterface >( *pGridDataSource ), css::uno::UNO_QUERY);
1745     Reference< XNameAccess > xColumnsByName;
1746     if ( xSuppColumns.is() )
1747         xColumnsByName = xSuppColumns->getColumns();
1748     Reference< XIndexAccess > xColumnsByIndex( xColumnsByName, UNO_QUERY );
1749 
1750     if ( xColumnsByIndex.is() )
1751         FmGridControl::InitColumnByField( pCol, xNewColumn, xColumnsByName, xColumnsByIndex );
1752     else
1753         // the simple version, applies when the grid is not yet connected to a data source
1754         pCol->setModel(xNewColumn);
1755 
1756     if (bWasEditing)
1757         pGrid->ActivateCell();
1758 }
1759 
1760 
elementRemoved(const ContainerEvent & evt)1761 void FmXGridPeer::elementRemoved(const ContainerEvent& evt)
1762 {
1763     SolarMutexGuard aGuard;
1764 
1765     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1766 
1767     // take handle column into account
1768     if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove() || m_xColumns->getCount() == static_cast<sal_Int32>(pGrid->GetModelColCount()))
1769         return;
1770 
1771     pGrid->RemoveColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(::comphelper::getINT32(evt.Accessor))));
1772 
1773     Reference< XPropertySet > xOldColumn(evt.Element, css::uno::UNO_QUERY);
1774     removeColumnListeners(xOldColumn);
1775 }
1776 
1777 
setProperty(const OUString & PropertyName,const Any & Value)1778 void FmXGridPeer::setProperty( const OUString& PropertyName, const Any& Value)
1779 {
1780     SolarMutexGuard aGuard;
1781 
1782     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1783 
1784     bool bVoid = !Value.hasValue();
1785 
1786     if ( PropertyName == FM_PROP_TEXTLINECOLOR )
1787     {
1788         ::Color aTextLineColor( bVoid ? COL_TRANSPARENT : ::Color(ColorTransparency, ::comphelper::getINT32( Value )) );
1789         if (bVoid)
1790         {
1791             pGrid->SetTextLineColor();
1792             pGrid->GetDataWindow().SetTextLineColor();
1793         }
1794         else
1795         {
1796             pGrid->SetTextLineColor(aTextLineColor);
1797             pGrid->GetDataWindow().SetTextLineColor(aTextLineColor);
1798         }
1799 
1800         // need to forward this to the columns
1801         std::vector< std::unique_ptr<DbGridColumn> > const & rColumns = pGrid->GetColumns();
1802         for (auto const & pLoop : rColumns)
1803         {
1804             FmXGridCell* pXCell = pLoop->GetCell();
1805             if (pXCell)
1806             {
1807                 if (bVoid)
1808                     pXCell->SetTextLineColor();
1809                 else
1810                     pXCell->SetTextLineColor(aTextLineColor);
1811             }
1812         }
1813 
1814         if (isDesignMode())
1815             pGrid->Invalidate();
1816     }
1817     else if ( PropertyName == FM_PROP_FONTEMPHASISMARK )
1818     {
1819         vcl::Font aGridFont = pGrid->GetControlFont();
1820         sal_Int16 nValue = ::comphelper::getINT16(Value);
1821         aGridFont.SetEmphasisMark( static_cast<FontEmphasisMark>(nValue) );
1822         pGrid->SetControlFont( aGridFont );
1823     }
1824     else if ( PropertyName == FM_PROP_FONTRELIEF )
1825     {
1826         vcl::Font aGridFont = pGrid->GetControlFont();
1827         sal_Int16 nValue = ::comphelper::getINT16(Value);
1828         aGridFont.SetRelief( static_cast<FontRelief>(nValue) );
1829         pGrid->SetControlFont( aGridFont );
1830     }
1831     else if ( PropertyName == FM_PROP_HELPURL )
1832     {
1833         OUString sHelpURL;
1834         OSL_VERIFY( Value >>= sHelpURL );
1835         INetURLObject aHID( sHelpURL );
1836         if ( aHID.GetProtocol() == INetProtocol::Hid )
1837             sHelpURL = aHID.GetURLPath();
1838         pGrid->SetHelpId( OUStringToOString( sHelpURL, RTL_TEXTENCODING_UTF8 ) );
1839     }
1840     else if ( PropertyName == FM_PROP_DISPLAYSYNCHRON )
1841     {
1842         pGrid->setDisplaySynchron(::comphelper::getBOOL(Value));
1843     }
1844     else if ( PropertyName == FM_PROP_CURSORCOLOR )
1845     {
1846         if (bVoid)
1847             pGrid->SetCursorColor(COL_TRANSPARENT);
1848         else
1849             pGrid->SetCursorColor( ::Color(ColorTransparency, ::comphelper::getINT32(Value)));
1850         if (isDesignMode())
1851             pGrid->Invalidate();
1852     }
1853     else if ( PropertyName == FM_PROP_ALWAYSSHOWCURSOR )
1854     {
1855         pGrid->EnablePermanentCursor(::comphelper::getBOOL(Value));
1856         if (isDesignMode())
1857             pGrid->Invalidate();
1858     }
1859     else if ( PropertyName == FM_PROP_FONT )
1860     {
1861         if ( bVoid )
1862             pGrid->SetControlFont( vcl::Font() );
1863         else
1864         {
1865             css::awt::FontDescriptor aFont;
1866             if (Value >>= aFont)
1867             {
1868                 vcl::Font aNewVclFont;
1869                 if (aFont != ::comphelper::getDefaultFont())    // is this the default
1870                     aNewVclFont = ImplCreateFont( aFont );
1871 
1872                 // need to add relief and emphasis (they're stored in a VCL-Font, but not in a FontDescriptor
1873                 vcl::Font aOldVclFont = pGrid->GetControlFont();
1874                 aNewVclFont.SetRelief( aOldVclFont.GetRelief() );
1875                 aNewVclFont.SetEmphasisMark( aOldVclFont.GetEmphasisMark() );
1876 
1877                 // now set it ...
1878                 pGrid->SetControlFont( aNewVclFont );
1879 
1880                 // if our row-height property is void (which means "calculate it font-dependent") we have
1881                 // to adjust the control's row height
1882                 Reference< XPropertySet >  xModelSet(getColumns(), UNO_QUERY);
1883                 if (xModelSet.is() && ::comphelper::hasProperty(FM_PROP_ROWHEIGHT, xModelSet))
1884                 {
1885                     Any aHeight = xModelSet->getPropertyValue(FM_PROP_ROWHEIGHT);
1886                     if (!aHeight.hasValue())
1887                         pGrid->SetDataRowHeight(0);
1888                 }
1889 
1890             }
1891         }
1892     }
1893     else if ( PropertyName == FM_PROP_BACKGROUNDCOLOR )
1894     {
1895         if ( bVoid )
1896         {
1897             pGrid->SetControlBackground();
1898         }
1899         else
1900         {
1901             ::Color aColor( ColorTransparency, ::comphelper::getINT32(Value) );
1902             pGrid->SetBackground( aColor );
1903             pGrid->SetControlBackground( aColor );
1904         }
1905     }
1906     else if ( PropertyName == FM_PROP_TEXTCOLOR )
1907     {
1908         if ( bVoid )
1909         {
1910             pGrid->SetControlForeground();
1911         }
1912         else
1913         {
1914             ::Color aColor( ColorTransparency, ::comphelper::getINT32(Value) );
1915             pGrid->SetTextColor( aColor );
1916             pGrid->SetControlForeground( aColor );
1917         }
1918     }
1919     else if ( PropertyName == FM_PROP_ROWHEIGHT )
1920     {
1921         sal_Int32 nLogHeight(0);
1922         if (Value >>= nLogHeight)
1923         {
1924             sal_Int32 nHeight = pGrid->LogicToPixel(Point(0, nLogHeight), MapMode(MapUnit::Map10thMM)).Y();
1925             // take the zoom factor into account
1926             nHeight = pGrid->CalcZoom(nHeight);
1927             pGrid->SetDataRowHeight(nHeight);
1928         }
1929         else if (bVoid)
1930             pGrid->SetDataRowHeight(0);
1931     }
1932     else if ( PropertyName == FM_PROP_HASNAVIGATION )
1933     {
1934         bool bValue( true );
1935         OSL_VERIFY( Value >>= bValue );
1936         pGrid->EnableNavigationBar( bValue );
1937     }
1938     else if ( PropertyName == FM_PROP_RECORDMARKER )
1939     {
1940         bool bValue( true );
1941         OSL_VERIFY( Value >>= bValue );
1942         pGrid->EnableHandle( bValue );
1943     }
1944     else if ( PropertyName == FM_PROP_ENABLED )
1945     {
1946         bool bValue( true );
1947         OSL_VERIFY( Value >>= bValue );
1948 
1949         // In design mode, disable only the data window.
1950         // Else the control cannot be configured anymore.
1951         if (isDesignMode())
1952             pGrid->GetDataWindow().Enable( bValue );
1953         else
1954             pGrid->Enable( bValue );
1955     }
1956     else
1957         VCLXWindow::setProperty( PropertyName, Value );
1958 }
1959 
1960 
CreateAccessibleContext()1961 Reference< XAccessibleContext > FmXGridPeer::CreateAccessibleContext()
1962 {
1963     Reference< XAccessibleContext > xContext;
1964 
1965     // use the AccessibleContext provided by the VCL window
1966     VclPtr<vcl::Window> pGrid = GetWindow();
1967     if ( pGrid )
1968     {
1969         Reference< XAccessible > xAcc( pGrid->GetAccessible() );
1970         if ( xAcc.is() )
1971             xContext = xAcc->getAccessibleContext();
1972         // TODO: this has a slight conceptual problem:
1973 
1974         // We know that the XAccessible and XAccessibleContext implementation of the browse
1975         // box is the same (the class implements both interfaces), which, speaking strictly,
1976         // is bad here (means when a browse box acts as UnoControl): We (the FmXGridPeer) are
1977         // the XAccessible here, and the browse box should be able to provide us an XAccessibleContext,
1978         // but it should _not_ be the XAccessible itself.
1979         // However, as long as no client implementation uses dirty hacks such as querying an
1980         // XAccessibleContext for XAccessible, this should not be a problem.
1981     }
1982 
1983     if ( !xContext.is() )
1984         xContext = VCLXWindow::CreateAccessibleContext( );
1985 
1986     return xContext;
1987 }
1988 
1989 
getProperty(const OUString & _rPropertyName)1990 Any FmXGridPeer::getProperty( const OUString& _rPropertyName )
1991 {
1992     Any aProp;
1993     if (GetWindow())
1994     {
1995         VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
1996         vcl::Window* pDataWindow  = &pGrid->GetDataWindow();
1997 
1998         if ( _rPropertyName == FM_PROP_NAME )
1999         {
2000             vcl::Font aFont = pDataWindow->GetControlFont();
2001             aProp <<= ImplCreateFontDescriptor( aFont );
2002         }
2003         else if ( _rPropertyName == FM_PROP_TEXTCOLOR )
2004         {
2005             aProp <<= pDataWindow->GetControlForeground();
2006         }
2007         else if ( _rPropertyName == FM_PROP_BACKGROUNDCOLOR )
2008         {
2009             aProp <<= pDataWindow->GetControlBackground();
2010         }
2011         else if ( _rPropertyName == FM_PROP_ROWHEIGHT )
2012         {
2013             sal_Int32 nPixelHeight = pGrid->GetDataRowHeight();
2014             // take the zoom factor into account
2015             nPixelHeight = pGrid->CalcReverseZoom(nPixelHeight);
2016             aProp <<= static_cast<sal_Int32>(pGrid->PixelToLogic(Point(0, nPixelHeight), MapMode(MapUnit::Map10thMM)).Y());
2017         }
2018         else if ( _rPropertyName == FM_PROP_HASNAVIGATION )
2019         {
2020             bool bHasNavBar = pGrid->HasNavigationBar();
2021             aProp <<= bHasNavBar;
2022         }
2023         else if ( _rPropertyName == FM_PROP_RECORDMARKER )
2024         {
2025             bool bHasHandle = pGrid->HasHandle();
2026             aProp <<= bHasHandle;
2027         }
2028         else if ( _rPropertyName == FM_PROP_ENABLED )
2029         {
2030             aProp <<= pDataWindow->IsEnabled();
2031         }
2032         else
2033             aProp = VCLXWindow::getProperty( _rPropertyName );
2034     }
2035     return aProp;
2036 }
2037 
2038 
dispose()2039 void FmXGridPeer::dispose()
2040 {
2041     EventObject aEvt;
2042     aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
2043     m_aModifyListeners.disposeAndClear(aEvt);
2044     m_aUpdateListeners.disposeAndClear(aEvt);
2045     m_aContainerListeners.disposeAndClear(aEvt);
2046 
2047     // release all interceptors
2048     Reference< XDispatchProviderInterceptor > xInterceptor( m_xFirstDispatchInterceptor );
2049     m_xFirstDispatchInterceptor.clear();
2050     while ( xInterceptor.is() )
2051     {
2052         // tell the interceptor it has a new (means no) predecessor
2053         xInterceptor->setMasterDispatchProvider( nullptr );
2054 
2055         // ask for its successor
2056         Reference< XDispatchProvider > xSlave = xInterceptor->getSlaveDispatchProvider();
2057         // and give it the new (means no) successoert
2058         xInterceptor->setSlaveDispatchProvider( nullptr );
2059 
2060         // start over with the next chain element
2061         xInterceptor.set(xSlave, css::uno::UNO_QUERY);
2062     }
2063 
2064     DisConnectFromDispatcher();
2065 
2066     // unregister all listeners
2067     if (m_xCursor.is())
2068     {
2069         m_xCursor->removeRowSetListener(this);
2070 
2071         Reference< XReset >  xReset(m_xCursor, UNO_QUERY);
2072         if (xReset.is())
2073             xReset->removeResetListener(this);
2074         Reference< XLoadable >  xLoadable(m_xCursor, UNO_QUERY);
2075         if (xLoadable.is())
2076             xLoadable->removeLoadListener(this);
2077         Reference< XPropertySet >  xSet(m_xCursor, UNO_QUERY);
2078         if (xSet.is())
2079         {
2080             xSet->removePropertyChangeListener(FM_PROP_ISMODIFIED, this);
2081             xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2082         }
2083         m_xCursor.clear();
2084     }
2085 
2086     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2087     if (pGrid)
2088     {
2089         pGrid->setDataSource(Reference< XRowSet > ());
2090         pGrid->DisposeAccessible();
2091     }
2092 
2093     VCLXWindow::dispose();
2094 }
2095 
2096 // XContainer
2097 
addContainerListener(const Reference<XContainerListener> & l)2098 void FmXGridPeer::addContainerListener(const Reference< XContainerListener >& l)
2099 {
2100     m_aContainerListeners.addInterface( l );
2101 }
2102 
removeContainerListener(const Reference<XContainerListener> & l)2103 void FmXGridPeer::removeContainerListener(const Reference< XContainerListener >& l)
2104 {
2105     m_aContainerListeners.removeInterface( l );
2106 }
2107 
2108 // css::data::XDatabaseCursorSupplier
2109 
startCursorListening()2110 void FmXGridPeer::startCursorListening()
2111 {
2112     if (!m_nCursorListening)
2113     {
2114         if (m_xCursor.is())
2115             m_xCursor->addRowSetListener(this);
2116 
2117         Reference< XReset >  xReset(m_xCursor, UNO_QUERY);
2118         if (xReset.is())
2119             xReset->addResetListener(this);
2120 
2121         // register all listeners
2122         Reference< XPropertySet >  xSet(m_xCursor, UNO_QUERY);
2123         if (xSet.is())
2124         {
2125             xSet->addPropertyChangeListener(FM_PROP_ISMODIFIED, this);
2126             xSet->addPropertyChangeListener(FM_PROP_ROWCOUNT, this);
2127         }
2128     }
2129     m_nCursorListening++;
2130 }
2131 
2132 
stopCursorListening()2133 void FmXGridPeer::stopCursorListening()
2134 {
2135     if (--m_nCursorListening)
2136         return;
2137 
2138     if (m_xCursor.is())
2139         m_xCursor->removeRowSetListener(this);
2140 
2141     Reference< XReset >  xReset(m_xCursor, UNO_QUERY);
2142     if (xReset.is())
2143         xReset->removeResetListener(this);
2144 
2145     Reference< XPropertySet >  xSet(m_xCursor, UNO_QUERY);
2146     if (xSet.is())
2147     {
2148         xSet->removePropertyChangeListener(FM_PROP_ISMODIFIED, this);
2149         xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
2150     }
2151 }
2152 
2153 
updateGrid(const Reference<XRowSet> & _rxCursor)2154 void FmXGridPeer::updateGrid(const Reference< XRowSet >& _rxCursor)
2155 {
2156     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2157     if (pGrid)
2158         pGrid->setDataSource(_rxCursor);
2159 }
2160 
2161 
getRowSet()2162 Reference< XRowSet >  FmXGridPeer::getRowSet()
2163 {
2164     return m_xCursor;
2165 }
2166 
2167 
setRowSet(const Reference<XRowSet> & _rDatabaseCursor)2168 void FmXGridPeer::setRowSet(const Reference< XRowSet >& _rDatabaseCursor)
2169 {
2170     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2171     if (!pGrid || !m_xColumns.is() || !m_xColumns->getCount())
2172         return;
2173     // unregister all listeners
2174     if (m_xCursor.is())
2175     {
2176         Reference< XLoadable >  xLoadable(m_xCursor, UNO_QUERY);
2177         // only if the form is loaded we set the rowset
2178         if (xLoadable.is())
2179         {
2180             stopCursorListening();
2181             xLoadable->removeLoadListener(this);
2182         }
2183     }
2184 
2185     m_xCursor = _rDatabaseCursor;
2186 
2187     if (!pGrid)
2188         return;
2189 
2190     Reference< XLoadable >  xLoadable(m_xCursor, UNO_QUERY);
2191     // only if the form is loaded we set the rowset
2192     if (xLoadable.is() && xLoadable->isLoaded())
2193         pGrid->setDataSource(m_xCursor);
2194     else
2195         pGrid->setDataSource(Reference< XRowSet > ());
2196 
2197     if (xLoadable.is())
2198     {
2199         startCursorListening();
2200         xLoadable->addLoadListener(this);
2201     }
2202 }
2203 
2204 
addGridControlListener(const Reference<XGridControlListener> & _listener)2205 void SAL_CALL FmXGridPeer::addGridControlListener( const Reference< XGridControlListener >& _listener )
2206 {
2207     m_aGridControlListeners.addInterface( _listener );
2208 }
2209 
2210 
removeGridControlListener(const Reference<XGridControlListener> & _listener)2211 void SAL_CALL FmXGridPeer::removeGridControlListener( const Reference< XGridControlListener >& _listener )
2212 {
2213     m_aGridControlListeners.removeInterface( _listener );
2214 }
2215 
2216 
getCurrentColumnPosition()2217 sal_Int16 FmXGridPeer::getCurrentColumnPosition()
2218 {
2219     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2220     return pGrid ? pGrid->GetViewColumnPos(pGrid->GetCurColumnId()) : -1;
2221 }
2222 
2223 
setCurrentColumnPosition(sal_Int16 nPos)2224 void FmXGridPeer::setCurrentColumnPosition(sal_Int16 nPos)
2225 {
2226     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2227     if (pGrid)
2228         pGrid->GoToColumnId(pGrid->GetColumnIdFromViewPos(nPos));
2229 }
2230 
2231 
selectionChanged(const EventObject & evt)2232 void FmXGridPeer::selectionChanged(const EventObject& evt)
2233 {
2234     SolarMutexGuard aGuard;
2235 
2236     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2237     if (!pGrid)
2238         return;
2239 
2240     Reference< css::view::XSelectionSupplier >  xSelSupplier(evt.Source, UNO_QUERY);
2241     Any aSelection = xSelSupplier->getSelection();
2242     DBG_ASSERT(aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE, "FmXGridPeer::selectionChanged : invalid selection !");
2243     Reference< XPropertySet >  xSelection;
2244     aSelection >>= xSelection;
2245     if (xSelection.is())
2246     {
2247         Reference< XPropertySet > xCol;
2248         sal_Int32 i = 0;
2249         sal_Int32 nColCount = m_xColumns->getCount();
2250 
2251         for (; i < nColCount; ++i)
2252         {
2253             m_xColumns->getByIndex(i) >>= xCol;
2254             if ( xCol == xSelection )
2255             {
2256                 pGrid->markColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
2257                 break;
2258             }
2259         }
2260         // The columns have to be 1-based for the VCL control.
2261         // If necessary, pass on the selection to the VCL control
2262         if ( i != pGrid->GetSelectedColumn() )
2263         {   // (if this does not take effect, the selectionChanged was implicitly triggered by the control itself)
2264             if ( i < nColCount )
2265             {
2266                 pGrid->SelectColumnPos(pGrid->GetViewColumnPos(pGrid->GetColumnIdFromModelPos( static_cast<sal_uInt16>(i) )) + 1);
2267                 // SelectColumnPos has led to an implicit ActivateCell again
2268                 if (pGrid->IsEditing())
2269                     pGrid->DeactivateCell();
2270             }
2271             else
2272                 pGrid->SetNoSelection();
2273         }
2274     }
2275     else
2276         pGrid->markColumn(USHRT_MAX);
2277 }
2278 
2279 // XElementAccess
2280 
hasElements()2281 sal_Bool FmXGridPeer::hasElements()
2282 {
2283     return getCount() != 0;
2284 }
2285 
2286 
getElementType()2287 Type SAL_CALL FmXGridPeer::getElementType(  )
2288 {
2289     return cppu::UnoType<css::awt::XControl>::get();
2290 }
2291 
2292 // XEnumerationAccess
2293 
createEnumeration()2294 Reference< XEnumeration >  FmXGridPeer::createEnumeration()
2295 {
2296     return new ::comphelper::OEnumerationByIndex(this);
2297 }
2298 
2299 // XIndexAccess
2300 
getCount()2301 sal_Int32 FmXGridPeer::getCount()
2302 {
2303     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2304     if (pGrid)
2305         return pGrid->GetViewColCount();
2306     else
2307         return 0;
2308 }
2309 
2310 
getByIndex(sal_Int32 _nIndex)2311 Any FmXGridPeer::getByIndex(sal_Int32 _nIndex)
2312 {
2313     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2314     if (_nIndex < 0 ||
2315         _nIndex >= getCount() || !pGrid)
2316         throw IndexOutOfBoundsException();
2317 
2318     Any aElement;
2319     // get the columnid
2320     sal_uInt16 nId = pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(_nIndex));
2321     // get the list position
2322     sal_uInt16 nPos = pGrid->GetModelColumnPos(nId);
2323 
2324     if ( nPos == GRID_COLUMN_NOT_FOUND )
2325         return aElement;
2326 
2327     DbGridColumn* pCol = pGrid->GetColumns()[ nPos ].get();
2328     Reference< css::awt::XControl >  xControl(pCol->GetCell());
2329     aElement <<= xControl;
2330 
2331     return aElement;
2332 }
2333 
2334 // css::util::XModeSelector
2335 
setMode(const OUString & Mode)2336 void FmXGridPeer::setMode(const OUString& Mode)
2337 {
2338     if (!supportsMode(Mode))
2339         throw NoSupportException();
2340 
2341     if (Mode == m_aMode)
2342         return;
2343 
2344     m_aMode = Mode;
2345 
2346     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2347     if ( Mode == "FilterMode" )
2348         pGrid->SetFilterMode(true);
2349     else
2350     {
2351         pGrid->SetFilterMode(false);
2352         pGrid->setDataSource(m_xCursor);
2353     }
2354 }
2355 
2356 
getMode()2357 OUString FmXGridPeer::getMode()
2358 {
2359     return m_aMode;
2360 }
2361 
2362 
getSupportedModes()2363 css::uno::Sequence<OUString> FmXGridPeer::getSupportedModes()
2364 {
2365     static css::uno::Sequence<OUString> const aModes
2366     {
2367         "DataMode",
2368         "FilterMode"
2369     };
2370     return aModes;
2371 }
2372 
2373 
supportsMode(const OUString & Mode)2374 sal_Bool FmXGridPeer::supportsMode(const OUString& Mode)
2375 {
2376     css::uno::Sequence<OUString> aModes(getSupportedModes());
2377     return comphelper::findValue(aModes, Mode) != -1;
2378 }
2379 
2380 
columnVisible(DbGridColumn const * pColumn)2381 void FmXGridPeer::columnVisible(DbGridColumn const * pColumn)
2382 {
2383     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2384 
2385     sal_Int32 _nIndex = pGrid->GetModelColumnPos(pColumn->GetId());
2386     Reference< css::awt::XControl >  xControl(pColumn->GetCell());
2387     ContainerEvent aEvt;
2388     aEvt.Source   = static_cast<XContainer*>(this);
2389     aEvt.Accessor <<= _nIndex;
2390     aEvt.Element  <<= xControl;
2391 
2392     m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvt );
2393 }
2394 
2395 
columnHidden(DbGridColumn const * pColumn)2396 void FmXGridPeer::columnHidden(DbGridColumn const * pColumn)
2397 {
2398     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2399 
2400     sal_Int32 _nIndex = pGrid->GetModelColumnPos(pColumn->GetId());
2401     Reference< css::awt::XControl >  xControl(pColumn->GetCell());
2402     ContainerEvent aEvt;
2403     aEvt.Source   = static_cast<XContainer*>(this);
2404     aEvt.Accessor <<= _nIndex;
2405     aEvt.Element  <<= xControl;
2406 
2407     m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvt );
2408 }
2409 
2410 
draw(sal_Int32 x,sal_Int32 y)2411 void FmXGridPeer::draw( sal_Int32 x, sal_Int32 y )
2412 {
2413     VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2414     EditBrowseBoxFlags nOldFlags = pGrid->GetBrowserFlags();
2415     pGrid->SetBrowserFlags(nOldFlags | EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT);
2416 
2417     VCLXWindow::draw(x, y);
2418 
2419     pGrid->SetBrowserFlags(nOldFlags);
2420 }
2421 
2422 
queryDispatch(const css::util::URL & aURL,const OUString & aTargetFrameName,sal_Int32 nSearchFlags)2423 Reference< css::frame::XDispatch >  FmXGridPeer::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
2424 {
2425     Reference< css::frame::XDispatch >  xResult;
2426 
2427     // first ask our interceptor chain
2428     if (m_xFirstDispatchInterceptor.is() && !m_bInterceptingDispatch)
2429     {
2430         m_bInterceptingDispatch = true;
2431         // safety against recursion : as we are master of the first chain element and slave of the last one we would
2432         // have an infinite loop without this if no dispatcher can fulfill the request
2433         xResult = m_xFirstDispatchInterceptor->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
2434         m_bInterceptingDispatch = false;
2435     }
2436 
2437     // then ask ourself : we don't have any dispatches
2438     return xResult;
2439 }
2440 
2441 
queryDispatches(const Sequence<css::frame::DispatchDescriptor> & aDescripts)2442 Sequence< Reference< css::frame::XDispatch > > FmXGridPeer::queryDispatches(const Sequence< css::frame::DispatchDescriptor>& aDescripts)
2443 {
2444     if (m_xFirstDispatchInterceptor.is())
2445         return m_xFirstDispatchInterceptor->queryDispatches(aDescripts);
2446 
2447     // then ask ourself : we don't have any dispatches
2448     return Sequence< Reference< css::frame::XDispatch > >();
2449 }
2450 
2451 
registerDispatchProviderInterceptor(const Reference<css::frame::XDispatchProviderInterceptor> & _xInterceptor)2452 void FmXGridPeer::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
2453 {
2454     if (!_xInterceptor.is())
2455         return;
2456 
2457     if (m_xFirstDispatchInterceptor.is())
2458     {
2459         // there is already an interceptor; the new one will become its master
2460         _xInterceptor->setSlaveDispatchProvider(m_xFirstDispatchInterceptor);
2461         m_xFirstDispatchInterceptor->setMasterDispatchProvider(m_xFirstDispatchInterceptor);
2462     }
2463     else
2464     {
2465         // it is the first interceptor; set ourself as slave
2466         _xInterceptor->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
2467     }
2468 
2469     // we are the master of the chain's first interceptor
2470     m_xFirstDispatchInterceptor = _xInterceptor;
2471     m_xFirstDispatchInterceptor->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
2472 
2473     // we have a new interceptor and we're alive ?
2474     if (!isDesignMode())
2475         // -> check for new dispatchers
2476         UpdateDispatches();
2477 }
2478 
2479 
releaseDispatchProviderInterceptor(const Reference<css::frame::XDispatchProviderInterceptor> & _xInterceptor)2480 void FmXGridPeer::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
2481 {
2482     if (!_xInterceptor.is())
2483         return;
2484 
2485     Reference< css::frame::XDispatchProviderInterceptor >  xChainWalk(m_xFirstDispatchInterceptor);
2486 
2487     if (m_xFirstDispatchInterceptor == _xInterceptor)
2488     {   // our chain will have a new first element
2489         Reference< css::frame::XDispatchProviderInterceptor >  xSlave(m_xFirstDispatchInterceptor->getSlaveDispatchProvider(), UNO_QUERY);
2490         m_xFirstDispatchInterceptor = xSlave;
2491     }
2492     // do this before removing the interceptor from the chain as we won't know it's slave afterwards)
2493 
2494     while (xChainWalk.is())
2495     {
2496         // walk along the chain of interceptors and look for the interceptor that has to be removed
2497         Reference< css::frame::XDispatchProviderInterceptor >  xSlave(xChainWalk->getSlaveDispatchProvider(), UNO_QUERY);
2498 
2499         if (xChainWalk == _xInterceptor)
2500         {
2501             // old master may be an interceptor too
2502             Reference< css::frame::XDispatchProviderInterceptor >  xMaster(xChainWalk->getMasterDispatchProvider(), UNO_QUERY);
2503 
2504             // unchain the interceptor that has to be removed
2505             xChainWalk->setSlaveDispatchProvider(Reference< css::frame::XDispatchProvider > ());
2506             xChainWalk->setMasterDispatchProvider(Reference< css::frame::XDispatchProvider > ());
2507 
2508             // reconnect the chain
2509             if (xMaster.is())
2510             {
2511                 if (xSlave.is())
2512                     xMaster->setSlaveDispatchProvider(xSlave);
2513                 else
2514                     // it's the first interceptor of the chain, set ourself as slave
2515                     xMaster->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
2516             }
2517             else
2518             {
2519                 // the chain's first element was removed, set ourself as new master of the second one
2520                 if (xSlave.is())
2521                     xSlave->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
2522             }
2523         }
2524 
2525         xChainWalk = xSlave;
2526     }
2527     // our interceptor chain has changed and we're alive ?
2528     if (!isDesignMode())
2529         // -> check the dispatchers
2530         UpdateDispatches();
2531 }
2532 
2533 
statusChanged(const css::frame::FeatureStateEvent & Event)2534 void FmXGridPeer::statusChanged(const css::frame::FeatureStateEvent& Event)
2535 {
2536     DBG_ASSERT(m_pStateCache, "FmXGridPeer::statusChanged : invalid call !");
2537     DBG_ASSERT(m_pDispatchers, "FmXGridPeer::statusChanged : invalid call !");
2538 
2539     Sequence< css::util::URL>& aUrls = getSupportedURLs();
2540 
2541     const std::vector<DbGridControlNavigationBarState>& aSlots = getSupportedGridSlots();
2542 
2543     auto pUrl = std::find_if(aUrls.begin(), aUrls.end(),
2544         [&Event](const css::util::URL& rUrl) { return rUrl.Main == Event.FeatureURL.Main; });
2545     if (pUrl != aUrls.end())
2546     {
2547         auto i = static_cast<sal_uInt32>(std::distance(aUrls.begin(), pUrl));
2548         DBG_ASSERT(m_pDispatchers[i] == Event.Source, "FmXGridPeer::statusChanged : the event source is a little bit suspect !");
2549         m_pStateCache[i] = Event.IsEnabled;
2550         VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2551         if (aSlots[i] != DbGridControlNavigationBarState::Undo)
2552             pGrid->GetNavigationBar().InvalidateState(aSlots[i]);
2553     }
2554     DBG_ASSERT(pUrl != aUrls.end(), "FmXGridPeer::statusChanged : got a call for an unknown url !");
2555 }
2556 
2557 
approveReset(const EventObject &)2558 sal_Bool FmXGridPeer::approveReset(const EventObject& /*rEvent*/)
2559 {
2560     return true;
2561 }
2562 
2563 
select(const Any & _rSelection)2564 sal_Bool SAL_CALL FmXGridPeer::select( const Any& _rSelection )
2565 {
2566     Sequence< Any > aBookmarks;
2567     if ( !( _rSelection >>= aBookmarks ) )
2568         throw IllegalArgumentException();
2569 
2570     return GetAs< FmGridControl >()->selectBookmarks(aBookmarks);
2571 
2572     // TODO:
2573     // speaking strictly, we would have to adjust our model, as our ColumnSelection may have changed.
2574     // Our model is a XSelectionSupplier, too, it handles the selection of single columns.
2575     // This is somewhat strange, as selection should be a view (not a model) aspect.
2576     // So for a clean solution, we should handle column selection ourself, and the model shouldn't
2577     // deal with selection at all.
2578 }
2579 
2580 
getSelection()2581 Any SAL_CALL FmXGridPeer::getSelection(  )
2582 {
2583     VclPtr< FmGridControl > pVclControl = GetAs< FmGridControl >();
2584     Sequence< Any > aSelectionBookmarks = pVclControl->getSelectionBookmarks();
2585     return makeAny(aSelectionBookmarks);
2586 }
2587 
2588 
addSelectionChangeListener(const Reference<XSelectionChangeListener> & _rxListener)2589 void SAL_CALL FmXGridPeer::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
2590 {
2591     m_aSelectionListeners.addInterface( _rxListener );
2592 }
2593 
2594 
removeSelectionChangeListener(const Reference<XSelectionChangeListener> & _rxListener)2595 void SAL_CALL FmXGridPeer::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
2596 {
2597     m_aSelectionListeners.removeInterface( _rxListener );
2598 }
2599 
2600 
resetted(const EventObject & rEvent)2601 void FmXGridPeer::resetted(const EventObject& rEvent)
2602 {
2603     if (m_xColumns == rEvent.Source)
2604     {   // my model was reset -> refresh the grid content
2605         SolarMutexGuard aGuard;
2606         VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2607         if (!pGrid)
2608             return;
2609         pGrid->resetCurrentRow();
2610     }
2611     // if the cursor fired a reset event we seem to be on the insert row
2612     else if (m_xCursor == rEvent.Source)
2613     {
2614         SolarMutexGuard aGuard;
2615         VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
2616         if (pGrid && pGrid->IsOpen())
2617             pGrid->positioned();
2618     }
2619 }
2620 
2621 
getSupportedGridSlots()2622 const std::vector<DbGridControlNavigationBarState>& FmXGridPeer::getSupportedGridSlots()
2623 {
2624     static const std::vector<DbGridControlNavigationBarState> aSupported {
2625         DbGridControlNavigationBarState::First,
2626         DbGridControlNavigationBarState::Prev,
2627         DbGridControlNavigationBarState::Next,
2628         DbGridControlNavigationBarState::Last,
2629         DbGridControlNavigationBarState::New,
2630         DbGridControlNavigationBarState::Undo
2631     };
2632     return aSupported;
2633 }
2634 
2635 
getSupportedURLs()2636 Sequence< css::util::URL>& FmXGridPeer::getSupportedURLs()
2637 {
2638     static Sequence< css::util::URL> aSupported = [&]()
2639     {
2640         static const char* sSupported[] = {
2641             FMURL_RECORD_MOVEFIRST,
2642             FMURL_RECORD_MOVEPREV,
2643             FMURL_RECORD_MOVENEXT,
2644             FMURL_RECORD_MOVELAST,
2645             FMURL_RECORD_MOVETONEW,
2646             FMURL_RECORD_UNDO
2647         };
2648         Sequence< css::util::URL> tmp(SAL_N_ELEMENTS(sSupported));
2649         css::util::URL* pSupported = tmp.getArray();
2650 
2651         for ( sal_Int32 i = 0; i < tmp.getLength(); ++i, ++pSupported)
2652             pSupported->Complete = OUString::createFromAscii(sSupported[i]);
2653 
2654         // let a css::util::URL-transformer normalize the URLs
2655         Reference< css::util::XURLTransformer >  xTransformer(
2656             util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
2657         for (css::util::URL & rURL : tmp)
2658             xTransformer->parseStrict(rURL);
2659         return tmp;
2660     }();
2661 
2662     return aSupported;
2663 }
2664 
2665 
UpdateDispatches()2666 void FmXGridPeer::UpdateDispatches()
2667 {
2668     if (!m_pStateCache)
2669     {   // we don't have any dispatchers yet -> do the initial connect
2670         ConnectToDispatcher();
2671         return;
2672     }
2673 
2674     sal_uInt16 nDispatchersGot = 0;
2675     const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
2676     const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
2677     Reference< css::frame::XDispatch >  xNewDispatch;
2678     for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
2679     {
2680         xNewDispatch = queryDispatch(*pSupportedURLs, OUString(), 0);
2681         if (xNewDispatch != m_pDispatchers[i])
2682         {
2683             if (m_pDispatchers[i].is())
2684                 m_pDispatchers[i]->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
2685             m_pDispatchers[i] = xNewDispatch;
2686             if (m_pDispatchers[i].is())
2687                 m_pDispatchers[i]->addStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
2688         }
2689         if (m_pDispatchers[i].is())
2690             ++nDispatchersGot;
2691     }
2692 
2693     if (!nDispatchersGot)
2694     {
2695         m_pStateCache.reset();
2696         m_pDispatchers.reset();
2697     }
2698 }
2699 
2700 
ConnectToDispatcher()2701 void FmXGridPeer::ConnectToDispatcher()
2702 {
2703     DBG_ASSERT((m_pStateCache != nullptr) == (m_pDispatchers != nullptr), "FmXGridPeer::ConnectToDispatcher : inconsistent !");
2704     if (m_pStateCache)
2705     {   // already connected -> just do an update
2706         UpdateDispatches();
2707         return;
2708     }
2709 
2710     const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
2711 
2712     // _before_ adding the status listeners (as the add should result in a statusChanged-call) !
2713     m_pStateCache.reset(new bool[aSupportedURLs.getLength()]);
2714     m_pDispatchers.reset(new Reference< css::frame::XDispatch > [aSupportedURLs.getLength()]);
2715 
2716     sal_uInt16 nDispatchersGot = 0;
2717     const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
2718     for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
2719     {
2720         m_pStateCache[i] = false;
2721         m_pDispatchers[i] = queryDispatch(*pSupportedURLs, OUString(), 0);
2722         if (m_pDispatchers[i].is())
2723         {
2724             m_pDispatchers[i]->addStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
2725             ++nDispatchersGot;
2726         }
2727     }
2728 
2729     if (!nDispatchersGot)
2730     {
2731         m_pStateCache.reset();
2732         m_pDispatchers.reset();
2733     }
2734 }
2735 
2736 
DisConnectFromDispatcher()2737 void FmXGridPeer::DisConnectFromDispatcher()
2738 {
2739     if (!m_pStateCache || !m_pDispatchers)
2740         return;
2741     // we're not connected
2742 
2743     const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
2744     const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
2745     for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
2746     {
2747         if (m_pDispatchers[i].is())
2748             m_pDispatchers[i]->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
2749     }
2750 
2751     m_pStateCache.reset();
2752     m_pDispatchers.reset();
2753 }
2754 
2755 
IMPL_LINK(FmXGridPeer,OnQueryGridSlotState,DbGridControlNavigationBarState,nSlot,int)2756 IMPL_LINK(FmXGridPeer, OnQueryGridSlotState, DbGridControlNavigationBarState, nSlot, int)
2757 {
2758     if (!m_pStateCache)
2759         return -1;  // unspecified
2760 
2761     // search the given slot with our supported sequence
2762     const std::vector<DbGridControlNavigationBarState>& aSupported = getSupportedGridSlots();
2763     for (size_t i=0; i<aSupported.size(); ++i)
2764     {
2765         if (aSupported[i] == nSlot)
2766         {
2767             if (!m_pDispatchers[i].is())
2768                 return -1;  // nothing known about this slot
2769             else
2770                 return m_pStateCache[i] ? 1 : 0;
2771         }
2772     }
2773 
2774     return  -1;
2775 }
2776 
2777 
IMPL_LINK(FmXGridPeer,OnExecuteGridSlot,DbGridControlNavigationBarState,nSlot,bool)2778 IMPL_LINK(FmXGridPeer, OnExecuteGridSlot, DbGridControlNavigationBarState, nSlot, bool)
2779 {
2780     if (!m_pDispatchers)
2781         return false;   // not handled
2782 
2783     Sequence< css::util::URL>& aUrls = getSupportedURLs();
2784     const css::util::URL* pUrls = aUrls.getConstArray();
2785 
2786     const std::vector<DbGridControlNavigationBarState>& aSlots = getSupportedGridSlots();
2787 
2788     DBG_ASSERT(static_cast<sal_Int32>(aSlots.size()) == aUrls.getLength(), "FmXGridPeer::OnExecuteGridSlot : inconsistent data returned by getSupportedURLs/getSupportedGridSlots!");
2789 
2790     for (size_t i=0; i<aSlots.size(); ++i, ++pUrls)
2791     {
2792         if (aSlots[i] == nSlot)
2793         {
2794             if (m_pDispatchers[i].is())
2795             {
2796                 // commit any changes done so far, if it's not the undoRecord URL
2797                 if ( pUrls->Complete == FMURL_RECORD_UNDO || commit() )
2798                     m_pDispatchers[i]->dispatch(*pUrls, Sequence< PropertyValue>());
2799 
2800                 return true;   // handled
2801             }
2802         }
2803     }
2804 
2805     return false;   // not handled
2806 }
2807 
2808 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2809