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 <sbagrid.hrc>
21 #include <core_resource.hxx>
22 #include <helpids.h>
23 #include <uiservices.hxx>
24 
25 #include <sot/exchange.hxx>
26 #include <svx/svxids.hrc>
27 
28 #include <svx/numinf.hxx>
29 #include <svx/dbaexchange.hxx>
30 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
31 
32 #include <sbagrid.hxx>
33 #include <dlgattr.hxx>
34 #include <dlgsize.hxx>
35 #include <com/sun/star/beans/XPropertyState.hpp>
36 #include <com/sun/star/form/XLoadable.hpp>
37 #include <com/sun/star/form/ControlFontDialog.hpp>
38 #include <com/sun/star/sdb/CommandType.hpp>
39 #include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp>
40 #include <com/sun/star/sdb/XResultSetAccess.hpp>
41 #include <com/sun/star/form/XForm.hpp>
42 #include <com/sun/star/container/XIndexContainer.hpp>
43 #include <com/sun/star/util/NumberFormat.hpp>
44 
45 #include <com/sun/star/view/XSelectionSupplier.hpp>
46 #include <com/sun/star/form/DataSelectionType.hpp>
47 #include <com/sun/star/awt/TextAlign.hpp>
48 #include <com/sun/star/awt/XTextComponent.hpp>
49 #include <com/sun/star/util/Date.hpp>
50 #include <com/sun/star/util/Time.hpp>
51 #include <com/sun/star/util/DateTime.hpp>
52 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
53 #include <tools/diagnose_ex.h>
54 
55 #include <sal/log.hxx>
56 #include <svl/intitem.hxx>
57 #include <svx/algitem.hxx>
58 #include <tools/multisel.hxx>
59 #include <svl/numuno.hxx>
60 #include <svl/itempool.hxx>
61 #include <svl/itemset.hxx>
62 #include <svl/rngitem.hxx>
63 #include <toolkit/helper/vclunohelper.hxx>
64 
65 #include <vcl/svapp.hxx>
66 #include <vcl/waitobj.hxx>
67 
68 #include <svl/zforlist.hxx>
69 #include <cppuhelper/queryinterface.hxx>
70 #include <connectivity/dbtools.hxx>
71 #include <connectivity/dbconversion.hxx>
72 #include <cppuhelper/typeprovider.hxx>
73 #include <comphelper/processfactory.hxx>
74 #include <comphelper/types.hxx>
75 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
76 #include <com/sun/star/sdbc/DataType.hpp>
77 #include <browserids.hxx>
78 #include <stringconstants.hxx>
79 #include <strings.hrc>
80 #include <strings.hxx>
81 #include <dbu_reghelper.hxx>
82 #include <dbexchange.hxx>
83 #include <TableRowExchange.hxx>
84 #include <TableRow.hxx>
85 #include <FieldDescriptions.hxx>
86 #include <svtools/stringtransfer.hxx>
87 #include <vcl/stdtext.hxx>
88 #include <UITools.hxx>
89 #include <TokenWriter.hxx>
90 #include <osl/diagnose.h>
91 #include <algorithm>
92 
93 using namespace ::com::sun::star::ui::dialogs;
94 using namespace ::com::sun::star::uno;
95 using namespace ::com::sun::star::sdb;
96 using namespace ::com::sun::star::sdbc;
97 using namespace ::com::sun::star::sdbcx;
98 using namespace ::com::sun::star::beans;
99 using namespace ::com::sun::star::container;
100 using namespace ::com::sun::star::datatransfer;
101 using namespace ::com::sun::star::lang;
102 using namespace ::com::sun::star::view;
103 using namespace ::com::sun::star::form;
104 using namespace ::com::sun::star::frame;
105 using namespace ::com::sun::star::util;
106 using namespace ::dbaui;
107 using namespace ::dbtools;
108 using namespace ::svx;
109 using namespace ::svt;
110 
createRegistryInfo_SbaXGridControl()111 extern "C" void createRegistryInfo_SbaXGridControl()
112 {
113     static OMultiInstanceAutoRegistration< SbaXGridControl > aAutoRegistration;
114 }
115 
getSupportedServiceNames()116 css::uno::Sequence<OUString> SAL_CALL SbaXGridControl::getSupportedServiceNames()
117 {
118     return getSupportedServiceNames_Static();
119 }
120 
Create(const Reference<XMultiServiceFactory> & _rxFactory)121 Reference< XInterface > SbaXGridControl::Create(const Reference<XMultiServiceFactory >& _rxFactory)
122 {
123     return *(new SbaXGridControl( comphelper::getComponentContext(_rxFactory) ));
124 }
125 
126 // SbaXGridControl
127 
getImplementationName()128 OUString SAL_CALL SbaXGridControl::getImplementationName()
129 {
130     return getImplementationName_Static();
131 }
132 
getImplementationName_Static()133 OUString SbaXGridControl::getImplementationName_Static()
134 {
135     return "com.sun.star.comp.dbu.SbaXGridControl";
136 }
137 
getSupportedServiceNames_Static()138 Sequence< OUString> SbaXGridControl::getSupportedServiceNames_Static()
139 {
140     return { "com.sun.star.form.control.InteractionGridControl", "com.sun.star.form.control.GridControl",
141          "com.sun.star.awt.UnoControl" };
142 }
143 
SbaXGridControl(const Reference<XComponentContext> & _rM)144 SbaXGridControl::SbaXGridControl(const Reference< XComponentContext >& _rM)
145     : FmXGridControl(_rM)
146 {
147 }
148 
~SbaXGridControl()149 SbaXGridControl::~SbaXGridControl()
150 {
151 }
152 
imp_CreatePeer(vcl::Window * pParent)153 FmXGridPeer* SbaXGridControl::imp_CreatePeer(vcl::Window* pParent)
154 {
155     FmXGridPeer* pReturn = new SbaXGridPeer(m_xContext);
156 
157     // translate properties into WinBits
158     WinBits nStyle = WB_TABSTOP;
159     Reference< XPropertySet >  xModelSet(getModel(), UNO_QUERY);
160     if (xModelSet.is())
161     {
162         try
163         {
164             if (::comphelper::getINT16(xModelSet->getPropertyValue(PROPERTY_BORDER)))
165                 nStyle |= WB_BORDER;
166         }
167         catch(Exception&)
168         {
169         }
170 
171     }
172 
173     pReturn->Create(pParent, nStyle);
174     return pReturn;
175 }
176 
queryInterface(const Type & _rType)177 Any SAL_CALL SbaXGridControl::queryInterface(const Type& _rType)
178 {
179     Any aRet = FmXGridControl::queryInterface(_rType);
180     return aRet.hasValue() ? aRet : ::cppu::queryInterface(_rType,static_cast<css::frame::XDispatch*>(this));
181 }
182 
getTypes()183 Sequence< Type > SAL_CALL SbaXGridControl::getTypes(  )
184 {
185     return comphelper::concatSequences(
186         FmXGridControl::getTypes(),
187         Sequence { cppu::UnoType<css::frame::XDispatch>::get() });
188 }
189 
getImplementationId()190 Sequence< sal_Int8 > SAL_CALL SbaXGridControl::getImplementationId(  )
191 {
192     return css::uno::Sequence<sal_Int8>();
193 }
194 
createPeer(const Reference<css::awt::XToolkit> & rToolkit,const Reference<css::awt::XWindowPeer> & rParentPeer)195 void SAL_CALL SbaXGridControl::createPeer(const Reference< css::awt::XToolkit > & rToolkit, const Reference< css::awt::XWindowPeer > & rParentPeer)
196 {
197     FmXGridControl::createPeer(rToolkit, rParentPeer);
198 
199     OSL_ENSURE(!mbCreatingPeer, "FmXGridControl::createPeer : recursion!");
200         // see the base class' createPeer for a comment on this
201 
202     // TODO: why the hell this whole class does not use any mutex?
203 
204     Reference< css::frame::XDispatch >  xDisp(getPeer(), UNO_QUERY);
205     for (auto const& elem : m_aStatusMultiplexer)
206     {
207         if (elem.second.is() && elem.second->getLength())
208             xDisp->addStatusListener(elem.second.get(), elem.first);
209     }
210 }
211 
dispatch(const css::util::URL & aURL,const Sequence<PropertyValue> & aArgs)212 void SAL_CALL SbaXGridControl::dispatch(const css::util::URL& aURL, const Sequence< PropertyValue >& aArgs)
213 {
214     Reference< css::frame::XDispatch >  xDisp(getPeer(), UNO_QUERY);
215     if (xDisp.is())
216         xDisp->dispatch(aURL, aArgs);
217 }
218 
addStatusListener(const Reference<XStatusListener> & _rxListener,const URL & _rURL)219 void SAL_CALL SbaXGridControl::addStatusListener( const Reference< XStatusListener > & _rxListener, const URL& _rURL )
220 {
221     ::osl::MutexGuard aGuard( GetMutex() );
222     if ( _rxListener.is() )
223     {
224         rtl::Reference<SbaXStatusMultiplexer>& xMultiplexer = m_aStatusMultiplexer[ _rURL ];
225         if ( !xMultiplexer.is() )
226         {
227             xMultiplexer = new SbaXStatusMultiplexer( *this, GetMutex() );
228         }
229 
230         xMultiplexer->addInterface( _rxListener );
231         if ( getPeer().is() )
232         {
233             if ( 1 == xMultiplexer->getLength() )
234             {   // the first external listener for this URL
235                 Reference< XDispatch >  xDisp( getPeer(), UNO_QUERY );
236                 xDisp->addStatusListener( xMultiplexer.get(), _rURL );
237             }
238             else
239             {   // already have other listeners for this URL
240                 _rxListener->statusChanged( xMultiplexer->getLastEvent() );
241             }
242         }
243     }
244 }
245 
removeStatusListener(const Reference<css::frame::XStatusListener> & _rxListener,const css::util::URL & _rURL)246 void SAL_CALL SbaXGridControl::removeStatusListener(const Reference< css::frame::XStatusListener > & _rxListener, const css::util::URL& _rURL)
247 {
248     ::osl::MutexGuard aGuard( GetMutex() );
249 
250     rtl::Reference<SbaXStatusMultiplexer>& xMultiplexer = m_aStatusMultiplexer[_rURL];
251     if (!xMultiplexer.is())
252     {
253         xMultiplexer = new SbaXStatusMultiplexer(*this,GetMutex());
254     }
255 
256     if (getPeer().is() && xMultiplexer->getLength() == 1)
257     {
258         Reference< css::frame::XDispatch >  xDisp(getPeer(), UNO_QUERY);
259         xDisp->removeStatusListener(xMultiplexer.get(), _rURL);
260     }
261     xMultiplexer->removeInterface( _rxListener );
262 }
263 
dispose()264 void SAL_CALL SbaXGridControl::dispose()
265 {
266     SolarMutexGuard aGuard;
267 
268     EventObject aEvt;
269     aEvt.Source = *this;
270 
271     for (auto & elem : m_aStatusMultiplexer)
272     {
273         if (elem.second.is())
274         {
275             elem.second->disposeAndClear(aEvt);
276             elem.second.clear();
277         }
278     }
279     StatusMultiplexerArray().swap(m_aStatusMultiplexer);
280 
281     FmXGridControl::dispose();
282 }
283 
284 // SbaXGridPeer
SbaXGridPeer(const Reference<XComponentContext> & _rM)285 SbaXGridPeer::SbaXGridPeer(const Reference< XComponentContext >& _rM)
286 : FmXGridPeer(_rM)
287 ,m_aStatusListeners(m_aMutex)
288 {
289 }
290 
~SbaXGridPeer()291 SbaXGridPeer::~SbaXGridPeer()
292 {
293 }
294 
dispose()295 void SAL_CALL SbaXGridPeer::dispose()
296 {
297     EventObject aEvt(*this);
298 
299     m_aStatusListeners.disposeAndClear(aEvt);
300 
301     FmXGridPeer::dispose();
302 }
303 
NotifyStatusChanged(const css::util::URL & _rUrl,const Reference<css::frame::XStatusListener> & xControl)304 void SbaXGridPeer::NotifyStatusChanged(const css::util::URL& _rUrl, const Reference< css::frame::XStatusListener > & xControl)
305 {
306     VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >();
307     if (!pGrid)
308         return;
309 
310     css::frame::FeatureStateEvent aEvt;
311     aEvt.Source = *this;
312     aEvt.IsEnabled = !pGrid->IsReadOnlyDB();
313     aEvt.FeatureURL = _rUrl;
314 
315     MapDispatchToBool::const_iterator aURLStatePos = m_aDispatchStates.find( classifyDispatchURL( _rUrl ) );
316     if ( m_aDispatchStates.end() != aURLStatePos )
317         aEvt.State <<= aURLStatePos->second;
318     else
319         aEvt.State <<= false;
320 
321     if (xControl.is())
322         xControl->statusChanged(aEvt);
323     else
324     {
325         ::cppu::OInterfaceContainerHelper * pIter = m_aStatusListeners.getContainer(_rUrl);
326 
327         if (pIter)
328         {
329             ::cppu::OInterfaceIteratorHelper aListIter(*pIter);
330             while (aListIter.hasMoreElements())
331                 static_cast< css::frame::XStatusListener*>(aListIter.next())->statusChanged(aEvt);
332         }
333     }
334 }
335 
queryInterface(const Type & _rType)336 Any SAL_CALL SbaXGridPeer::queryInterface(const Type& _rType)
337 {
338     Any aRet = ::cppu::queryInterface(_rType,static_cast<css::frame::XDispatch*>(this));
339     if(aRet.hasValue())
340         return aRet;
341     return FmXGridPeer::queryInterface(_rType);
342 }
343 
queryDispatch(const css::util::URL & aURL,const OUString & aTargetFrameName,sal_Int32 nSearchFlags)344 Reference< css::frame::XDispatch >  SAL_CALL SbaXGridPeer::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
345 {
346     if  (   ( aURL.Complete == ".uno:GridSlots/BrowserAttribs" ) || ( aURL.Complete == ".uno:GridSlots/RowHeight" )
347         ||  ( aURL.Complete == ".uno:GridSlots/ColumnAttribs" )  || ( aURL.Complete == ".uno:GridSlots/ColumnWidth" )
348         )
349     {
350         return static_cast<css::frame::XDispatch*>(this);
351     }
352 
353     return FmXGridPeer::queryDispatch(aURL, aTargetFrameName, nSearchFlags);
354 }
355 
IMPL_LINK_NOARG(SbaXGridPeer,OnDispatchEvent,void *,void)356 IMPL_LINK_NOARG( SbaXGridPeer, OnDispatchEvent, void*, void )
357 {
358     VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >();
359     if ( pGrid )    // if this fails, we were disposing before arriving here
360     {
361         if ( !Application::IsMainThread() )
362         {
363             // still not in the main thread (see SbaXGridPeer::dispatch). post an event, again
364             // without moving the special even to the back of the queue
365             pGrid->PostUserEvent( LINK( this, SbaXGridPeer, OnDispatchEvent ) );
366         }
367         else
368         {
369             DispatchArgs aArgs = m_aDispatchArgs.front();
370             m_aDispatchArgs.pop();
371 
372             SbaXGridPeer::dispatch( aArgs.aURL, aArgs.aArgs );
373         }
374     }
375 }
376 
classifyDispatchURL(const URL & _rURL)377 SbaXGridPeer::DispatchType SbaXGridPeer::classifyDispatchURL( const URL& _rURL )
378 {
379     DispatchType eURLType = dtUnknown;
380     if ( _rURL.Complete == ".uno:GridSlots/BrowserAttribs" )
381         eURLType = dtBrowserAttribs;
382     else if ( _rURL.Complete == ".uno:GridSlots/RowHeight" )
383         eURLType = dtRowHeight;
384     else if ( _rURL.Complete == ".uno:GridSlots/ColumnAttribs" )
385         eURLType = dtColumnAttribs;
386     else if ( _rURL.Complete == ".uno:GridSlots/ColumnWidth" )
387         eURLType = dtColumnWidth;
388     return eURLType;
389 }
390 
dispatch(const URL & aURL,const Sequence<PropertyValue> & aArgs)391 void SAL_CALL SbaXGridPeer::dispatch(const URL& aURL, const Sequence< PropertyValue >& aArgs)
392 {
393     VclPtr< SbaGridControl > pGrid = GetAs< SbaGridControl >();
394     if (!pGrid)
395         return;
396 
397     if ( !Application::IsMainThread() )
398     {
399         // we're not in the main thread. This is bad, as we want to raise windows here,
400         // and VCL does not like windows to be opened in non-main threads (at least on Win32).
401         // Okay, do this async. No problem with this, as XDispatch::dispatch is defined to be
402         // a one-way method.
403 
404         // save the args
405         DispatchArgs aDispatchArgs;
406         aDispatchArgs.aURL = aURL;
407         aDispatchArgs.aArgs = aArgs;
408         m_aDispatchArgs.push( aDispatchArgs );
409 
410         // post an event
411         // we use the Window::PostUserEvent here, instead of the application::PostUserEvent
412         // this saves us from keeping track of these events - as soon as the window dies,
413         // the events are deleted automatically. For the application way, we would need to
414         // do this ourself.
415         // As we use our grid as window, and the grid dies before we die, this should be no problem.
416         pGrid->PostUserEvent( LINK( this, SbaXGridPeer, OnDispatchEvent ) );
417         return;
418     }
419 
420     SolarMutexGuard aGuard;
421     sal_Int16 nColId = -1;
422     for (const PropertyValue& rArg : aArgs)
423     {
424         if (rArg.Name == "ColumnViewPos")
425         {
426             nColId = pGrid->GetColumnIdFromViewPos(::comphelper::getINT16(rArg.Value));
427             break;
428         }
429         if (rArg.Name == "ColumnModelPos")
430         {
431             nColId = pGrid->GetColumnIdFromModelPos(::comphelper::getINT16(rArg.Value));
432             break;
433         }
434         if (rArg.Name == "ColumnId")
435         {
436             nColId = ::comphelper::getINT16(rArg.Value);
437             break;
438         }
439     }
440 
441     DispatchType eURLType = classifyDispatchURL( aURL );
442 
443     if ( dtUnknown != eURLType )
444     {
445         // notify any status listeners that the dialog is now active (well, about to be active)
446         MapDispatchToBool::const_iterator aThisURLState = m_aDispatchStates.emplace( eURLType, true ).first;
447         NotifyStatusChanged( aURL, nullptr );
448 
449         // execute the dialog
450         switch ( eURLType )
451         {
452             case dtBrowserAttribs:
453                 pGrid->SetBrowserAttrs();
454                 break;
455 
456             case dtRowHeight:
457                 pGrid->SetRowHeight();
458                 break;
459 
460             case dtColumnAttribs:
461             {
462                 OSL_ENSURE(nColId != -1, "SbaXGridPeer::dispatch : invalid parameter !");
463                 if (nColId != -1)
464                     break;
465                 pGrid->SetColAttrs(nColId);
466             }
467             break;
468 
469             case dtColumnWidth:
470             {
471                 OSL_ENSURE(nColId != -1, "SbaXGridPeer::dispatch : invalid parameter !");
472                 if (nColId != -1)
473                     break;
474                 pGrid->SetColWidth(nColId);
475             }
476             break;
477 
478             case dtUnknown:
479                 break;
480         }
481 
482         // notify any status listeners that the dialog vanished
483         m_aDispatchStates.erase( aThisURLState );
484         NotifyStatusChanged( aURL, nullptr );
485     }
486 }
487 
addStatusListener(const Reference<css::frame::XStatusListener> & xControl,const css::util::URL & aURL)488 void SAL_CALL SbaXGridPeer::addStatusListener(const Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL)
489 {
490     ::cppu::OInterfaceContainerHelper* pCont = m_aStatusListeners.getContainer(aURL);
491     if (!pCont)
492         m_aStatusListeners.addInterface(aURL,xControl);
493     else
494         pCont->addInterface(xControl);
495     NotifyStatusChanged(aURL, xControl);
496 }
497 
removeStatusListener(const Reference<css::frame::XStatusListener> & xControl,const css::util::URL & aURL)498 void SAL_CALL SbaXGridPeer::removeStatusListener(const Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL)
499 {
500     ::cppu::OInterfaceContainerHelper* pCont = m_aStatusListeners.getContainer(aURL);
501     if ( pCont )
502         pCont->removeInterface(xControl);
503 }
504 
getTypes()505 Sequence< Type > SAL_CALL SbaXGridPeer::getTypes()
506 {
507     return comphelper::concatSequences(
508         FmXGridPeer::getTypes(),
509         Sequence { cppu::UnoType<css::frame::XDispatch>::get() });
510 }
511 
512 UNO3_GETIMPLEMENTATION2_IMPL(SbaXGridPeer, FmXGridPeer);
513 
imp_CreateControl(vcl::Window * pParent,WinBits nStyle)514 VclPtr<FmGridControl> SbaXGridPeer::imp_CreateControl(vcl::Window* pParent, WinBits nStyle)
515 {
516     return VclPtr<SbaGridControl>::Create( m_xContext, pParent, this, nStyle);
517 }
518 
519 // SbaGridHeader
520 
SbaGridHeader(BrowseBox * pParent)521 SbaGridHeader::SbaGridHeader(BrowseBox* pParent)
522     :FmGridHeader(pParent, WB_STDHEADERBAR | WB_DRAG)
523     ,DragSourceHelper(this)
524 {
525 }
526 
~SbaGridHeader()527 SbaGridHeader::~SbaGridHeader()
528 {
529     disposeOnce();
530 }
531 
dispose()532 void SbaGridHeader::dispose()
533 {
534     DragSourceHelper::dispose();
535     FmGridHeader::dispose();
536 }
537 
StartDrag(sal_Int8 _nAction,const Point & _rPosPixel)538 void SbaGridHeader::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel )
539 {
540     SolarMutexGuard aGuard;
541         // in the new DnD API, the solar mutex is not locked when StartDrag is called
542 
543     ImplStartColumnDrag( _nAction, _rPosPixel );
544 }
545 
MouseButtonDown(const MouseEvent & _rMEvt)546 void SbaGridHeader::MouseButtonDown( const MouseEvent& _rMEvt )
547 {
548     if (_rMEvt.IsLeft())
549         if (_rMEvt.GetClicks() != 2)
550         {
551             // the base class will start a column move here, which we don't want to allow
552             // (at the moment. If we store relative positions with the columns, we can allow column moves...)
553 
554         }
555 
556     FmGridHeader::MouseButtonDown(_rMEvt);
557 }
558 
ImplStartColumnDrag(sal_Int8 _nAction,const Point & _rMousePos)559 void SbaGridHeader::ImplStartColumnDrag(sal_Int8 _nAction, const Point& _rMousePos)
560 {
561     sal_uInt16 nId = GetItemId(_rMousePos);
562     bool bResizingCol = false;
563     if (HEADERBAR_ITEM_NOTFOUND != nId)
564     {
565         tools::Rectangle aColRect = GetItemRect(nId);
566         aColRect.AdjustLeft(nId ? 3 : 0 ); // the handle col (nId == 0) does not have a left margin for resizing
567         aColRect.AdjustRight( -3 );
568         bResizingCol = !aColRect.IsInside(_rMousePos);
569     }
570     if (!bResizingCol)
571     {
572         // force the base class to end its drag mode
573         EndTracking(TrackingEventFlags::Cancel | TrackingEventFlags::End);
574 
575         // because we have 3d-buttons the select handler is called from MouseButtonUp, but StartDrag
576         // occurs earlier (while the mouse button is down)
577         // so for optical reasons we select the column before really starting the drag operation.
578         notifyColumnSelect(nId);
579 
580         static_cast<SbaGridControl*>(GetParent())->StartDrag(_nAction,
581                 Point(
582                     _rMousePos.X() + GetPosPixel().X(),     // we aren't left-justified with our parent, in contrast to the data window
583                     _rMousePos.Y() - GetSizePixel().Height()
584                 )
585             );
586     }
587 }
588 
PreExecuteColumnContextMenu(sal_uInt16 nColId,PopupMenu & rMenu)589 void SbaGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, PopupMenu& rMenu)
590 {
591     FmGridHeader::PreExecuteColumnContextMenu(nColId, rMenu);
592 
593     // some items are valid only if the db isn't readonly
594     bool bDBIsReadOnly = static_cast<SbaGridControl*>(GetParent())->IsReadOnlyDB();
595 
596     if (bDBIsReadOnly)
597     {
598         rMenu.EnableItem(rMenu.GetItemId("hide"), false);
599         PopupMenu* pShowColsMenu = rMenu.GetPopupMenu(rMenu.GetItemId("show"));
600         if (pShowColsMenu)
601         {
602             // at most 16 items which mean "show column <name>"
603             for (sal_uInt16 i=1; i<16; ++i)
604                 pShowColsMenu->EnableItem(i, false);
605             // "show cols/more..." and "show cols/all"
606             pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("more"), false);
607             pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("all"), false);
608         }
609     }
610 
611     // prepend some new items
612     bool bColAttrs = (nColId != sal_uInt16(-1)) && (nColId != 0);
613     if ( bColAttrs && !bDBIsReadOnly)
614     {
615         sal_uInt16 nPos = 0;
616         sal_uInt16 nModelPos = static_cast<SbaGridControl*>(GetParent())->GetModelColumnPos(nColId);
617         Reference< XPropertySet >  xField = static_cast<SbaGridControl*>(GetParent())->getField(nModelPos);
618 
619         if ( xField.is() )
620         {
621             switch( ::comphelper::getINT32(xField->getPropertyValue(PROPERTY_TYPE)) )
622             {
623             case DataType::BINARY:
624             case DataType::VARBINARY:
625             case DataType::LONGVARBINARY:
626             case DataType::SQLNULL:
627             case DataType::OBJECT:
628             case DataType::BLOB:
629             case DataType::CLOB:
630             case DataType::REF:
631                 break;
632             default:
633                 rMenu.InsertItem(ID_BROWSER_COLATTRSET, DBA_RES(RID_STR_COLUMN_FORMAT), MenuItemBits::NONE, OString(), nPos++);
634                 rMenu.SetHelpId(ID_BROWSER_COLATTRSET, HID_BROWSER_COLUMNFORMAT);
635                 rMenu.InsertSeparator(OString(), nPos++);
636             }
637         }
638 
639         rMenu.InsertItem(ID_BROWSER_COLWIDTH, DBA_RES(RID_STR_COLUMN_WIDTH), MenuItemBits::NONE, OString(), nPos++);
640         rMenu.SetHelpId(ID_BROWSER_COLWIDTH, HID_BROWSER_COLUMNWIDTH);
641         rMenu.InsertSeparator(OString(), nPos++);
642     }
643 }
644 
PostExecuteColumnContextMenu(sal_uInt16 nColId,const PopupMenu & rMenu,sal_uInt16 nExecutionResult)645 void SbaGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const PopupMenu& rMenu, sal_uInt16 nExecutionResult)
646 {
647     switch (nExecutionResult)
648     {
649         case ID_BROWSER_COLWIDTH:
650             static_cast<SbaGridControl*>(GetParent())->SetColWidth(nColId);
651             break;
652 
653         case ID_BROWSER_COLATTRSET:
654             static_cast<SbaGridControl*>(GetParent())->SetColAttrs(nColId);
655             break;
656         case ID_BROWSER_COLUMNINFO:
657             {
658                 sal_uInt16 nModelPos = static_cast<SbaGridControl*>(GetParent())->GetModelColumnPos(nColId);
659                 Reference< XPropertySet >  xField = static_cast<SbaGridControl*>(GetParent())->getField(nModelPos);
660 
661                 if(!xField.is())
662                     break;
663                 std::vector< std::shared_ptr<OTableRow> > vClipboardList;
664                 // send it to the clipboard
665                 vClipboardList.push_back(std::make_shared<OTableRow>(xField));
666                 rtl::Reference<OTableRowExchange> pData = new OTableRowExchange(vClipboardList);
667                 pData->CopyToClipboard(GetParent());
668             }
669             break;
670 
671         default: FmGridHeader::PostExecuteColumnContextMenu(nColId, rMenu, nExecutionResult);
672     }
673 }
674 
675 // SbaGridControl
SbaGridControl(Reference<XComponentContext> const & _rM,vcl::Window * pParent,FmXGridPeer * _pPeer,WinBits nBits)676 SbaGridControl::SbaGridControl(Reference< XComponentContext > const & _rM,
677                                vcl::Window* pParent, FmXGridPeer* _pPeer, WinBits nBits)
678     :FmGridControl(_rM,pParent, _pPeer, nBits)
679     ,m_pMasterListener(nullptr)
680     ,m_nAsyncDropEvent(nullptr)
681     ,m_bActivatingForDrop(false)
682 {
683 }
684 
~SbaGridControl()685 SbaGridControl::~SbaGridControl()
686 {
687     disposeOnce();
688 }
689 
dispose()690 void SbaGridControl::dispose()
691 {
692     if (m_nAsyncDropEvent)
693         Application::RemoveUserEvent(m_nAsyncDropEvent);
694     m_nAsyncDropEvent = nullptr;
695     FmGridControl::dispose();
696 }
697 
imp_CreateHeaderBar(BrowseBox * pParent)698 VclPtr<BrowserHeader> SbaGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
699 {
700     return VclPtr<SbaGridHeader>::Create(pParent);
701 }
702 
GetController(long nRow,sal_uInt16 nCol)703 CellController* SbaGridControl::GetController(long nRow, sal_uInt16 nCol)
704 {
705     if ( m_bActivatingForDrop )
706         return nullptr;
707 
708     return FmGridControl::GetController(nRow, nCol);
709 }
710 
PreExecuteRowContextMenu(sal_uInt16 nRow,PopupMenu & rMenu)711 void SbaGridControl::PreExecuteRowContextMenu(sal_uInt16 nRow, PopupMenu& rMenu)
712 {
713     FmGridControl::PreExecuteRowContextMenu(nRow, rMenu);
714 
715     sal_uInt16 nPos = 0;
716 
717     if (!IsReadOnlyDB())
718     {
719         rMenu.InsertItem(ID_BROWSER_TABLEATTR, DBA_RES(RID_STR_TABLE_FORMAT), MenuItemBits::NONE, OString(), nPos++);
720         rMenu.SetHelpId(ID_BROWSER_TABLEATTR, HID_BROWSER_TABLEFORMAT);
721 
722         rMenu.InsertItem(ID_BROWSER_ROWHEIGHT, DBA_RES(RID_STR_ROW_HEIGHT), MenuItemBits::NONE, OString(), nPos++);
723         rMenu.SetHelpId(ID_BROWSER_ROWHEIGHT, HID_BROWSER_ROWHEIGHT);
724         rMenu.InsertSeparator(OString(), nPos++);
725     }
726 
727     if ( GetSelectRowCount() > 0 )
728     {
729         rMenu.InsertItem(ID_BROWSER_COPY, DBA_RES(RID_STR_COPY), MenuItemBits::NONE, OString(), nPos++);
730         rMenu.InsertSeparator(OString(), nPos++);
731     }
732 }
733 
GetDatasourceFormatter()734 SvNumberFormatter* SbaGridControl::GetDatasourceFormatter()
735 {
736     Reference< css::util::XNumberFormatsSupplier >  xSupplier = ::dbtools::getNumberFormats(::dbtools::getConnection(Reference< XRowSet > (getDataSource(),UNO_QUERY)), true, getContext());
737 
738     SvNumberFormatsSupplierObj* pSupplierImpl = comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( xSupplier );
739     if ( !pSupplierImpl )
740         return nullptr;
741 
742     SvNumberFormatter* pFormatter = pSupplierImpl->GetNumberFormatter();
743     return pFormatter;
744 }
745 
SetColWidth(sal_uInt16 nColId)746 void SbaGridControl::SetColWidth(sal_uInt16 nColId)
747 {
748     // get the (UNO) column model
749     sal_uInt16 nModelPos = GetModelColumnPos(nColId);
750     Reference< XIndexAccess >  xCols = GetPeer()->getColumns();
751     Reference< XPropertySet >  xAffectedCol;
752     if (xCols.is() && (nModelPos != sal_uInt16(-1)))
753         xAffectedCol.set(xCols->getByIndex(nModelPos), css::uno::UNO_QUERY);
754 
755     if (xAffectedCol.is())
756     {
757         Any aWidth = xAffectedCol->getPropertyValue(PROPERTY_WIDTH);
758         sal_Int32 nCurWidth = aWidth.hasValue() ? ::comphelper::getINT32(aWidth) : -1;
759 
760         DlgSize aDlgColWidth(GetFrameWeld(), nCurWidth, false);
761         if (aDlgColWidth.run() == RET_OK)
762         {
763             sal_Int32 nValue = aDlgColWidth.GetValue();
764             Any aNewWidth;
765             if (-1 == nValue)
766             {   // set to default
767                 Reference< XPropertyState >  xPropState(xAffectedCol, UNO_QUERY);
768                 if (xPropState.is())
769                 {
770                     try { aNewWidth = xPropState->getPropertyDefault(PROPERTY_WIDTH); } catch(Exception&) { } ;
771                 }
772             }
773             else
774                 aNewWidth <<= nValue;
775             try {  xAffectedCol->setPropertyValue(PROPERTY_WIDTH, aNewWidth); } catch(Exception&) { } ;
776         }
777     }
778 }
779 
SetRowHeight()780 void SbaGridControl::SetRowHeight()
781 {
782     Reference< XPropertySet >  xCols(GetPeer()->getColumns(), UNO_QUERY);
783     if (!xCols.is())
784         return;
785 
786     Any aHeight = xCols->getPropertyValue(PROPERTY_ROW_HEIGHT);
787     sal_Int32 nCurHeight = aHeight.hasValue() ? ::comphelper::getINT32(aHeight) : -1;
788 
789     DlgSize aDlgRowHeight(GetFrameWeld(), nCurHeight, true);
790     if (aDlgRowHeight.run() == RET_OK)
791     {
792         sal_Int32 nValue = aDlgRowHeight.GetValue();
793         Any aNewHeight;
794         if (sal_Int16(-1) == nValue)
795         {   // set to default
796             Reference< XPropertyState >  xPropState(xCols, UNO_QUERY);
797             if (xPropState.is())
798             {
799                 try
800                 {
801                     aNewHeight = xPropState->getPropertyDefault(PROPERTY_ROW_HEIGHT);
802                 }
803                 catch(Exception&)
804                 { }
805             }
806         }
807         else
808             aNewHeight <<= nValue;
809         try
810         {
811             xCols->setPropertyValue(PROPERTY_ROW_HEIGHT, aNewHeight);
812         }
813         catch(Exception&)
814         {
815             OSL_FAIL("setPropertyValue: PROPERTY_ROW_HEIGHT throws an exception");
816         }
817     }
818 }
819 
SetColAttrs(sal_uInt16 nColId)820 void SbaGridControl::SetColAttrs(sal_uInt16 nColId)
821 {
822     SvNumberFormatter* pFormatter = GetDatasourceFormatter();
823     if (!pFormatter)
824         return;
825 
826     sal_uInt16 nModelPos = GetModelColumnPos(nColId);
827 
828     // get the (UNO) column model
829     Reference< XIndexAccess >  xCols = GetPeer()->getColumns();
830     Reference< XPropertySet >  xAffectedCol;
831     if (xCols.is() && (nModelPos != sal_uInt16(-1)))
832         xAffectedCol.set(xCols->getByIndex(nModelPos), css::uno::UNO_QUERY);
833 
834     // get the field the column is bound to
835     Reference< XPropertySet >  xField = getField(nModelPos);
836     ::dbaui::callColumnFormatDialog(xAffectedCol,xField,pFormatter,this);//(Window::GetSettings().GetLanguage());
837 }
838 
SetBrowserAttrs()839 void SbaGridControl::SetBrowserAttrs()
840 {
841     Reference< XPropertySet >  xGridModel(GetPeer()->getColumns(), UNO_QUERY);
842     if (!xGridModel.is())
843         return;
844 
845     try
846     {
847         Reference< XComponentContext > xContext = getContext();
848         css::beans::PropertyValue aArg;
849         css::uno::Sequence<css::uno::Any> aArguments(2);
850         aArg.Name = "IntrospectedObject";
851         aArg.Value <<= xGridModel;
852         aArguments[0] <<= aArg;
853         aArg.Name = "ParentWindow";
854         aArg.Value <<= VCLUnoHelper::GetInterface(this);
855         aArguments[1] <<= aArg;
856         Reference<XExecutableDialog> xExecute(xContext->getServiceManager()->createInstanceWithArgumentsAndContext("com.sun.star.form.ControlFontDialog",
857                                               aArguments, xContext), css::uno::UNO_QUERY_THROW);
858         xExecute->execute();
859     }
860     catch( const Exception& )
861     {
862         DBG_UNHANDLED_EXCEPTION("dbaccess");
863     }
864 }
865 
PostExecuteRowContextMenu(sal_uInt16 nRow,const PopupMenu & rMenu,sal_uInt16 nExecutionResult)866 void SbaGridControl::PostExecuteRowContextMenu(sal_uInt16 nRow, const PopupMenu& rMenu, sal_uInt16 nExecutionResult)
867 {
868     switch (nExecutionResult)
869     {
870         case ID_BROWSER_TABLEATTR:
871             SetBrowserAttrs();
872             break;
873         case ID_BROWSER_ROWHEIGHT:
874             SetRowHeight();
875             break;
876         case ID_BROWSER_COPY:
877             CopySelectedRowsToClipboard();
878             break;
879 
880         default:
881             FmGridControl::PostExecuteRowContextMenu(nRow, rMenu, nExecutionResult);
882             break;
883     }
884 }
885 
Select()886 void SbaGridControl::Select()
887 {
888     // Some selection has changed ...
889     FmGridControl::Select();
890 
891     if (m_pMasterListener)
892         m_pMasterListener->SelectionChanged();
893 }
894 
ActivateCell(long nRow,sal_uInt16 nCol,bool bSetCellFocus)895 void SbaGridControl::ActivateCell(long nRow, sal_uInt16 nCol, bool bSetCellFocus /*= sal_True*/ )
896 {
897     FmGridControl::ActivateCell(nRow, nCol, bSetCellFocus);
898     if (m_pMasterListener)
899         m_pMasterListener->CellActivated();
900 }
901 
DeactivateCell(bool bUpdate)902 void SbaGridControl::DeactivateCell(bool bUpdate /*= sal_True*/)
903 {
904     FmGridControl::DeactivateCell(bUpdate);
905     if (m_pMasterListener)
906         m_pMasterListener->CellDeactivated();
907 }
908 
onRowChange()909 void SbaGridControl::onRowChange()
910 {
911     if ( m_pMasterListener )
912         m_pMasterListener->RowChanged();
913 }
914 
onColumnChange()915 void SbaGridControl::onColumnChange()
916 {
917     if ( m_pMasterListener )
918         m_pMasterListener->ColumnChanged();
919 }
920 
getField(sal_uInt16 nModelPos)921 Reference< XPropertySet >  SbaGridControl::getField(sal_uInt16 nModelPos)
922 {
923     Reference< XPropertySet >  xEmptyReturn;
924     try
925     {
926         // first get the name of the column
927         Reference< XIndexAccess >  xCols = GetPeer()->getColumns();
928         if ( xCols.is() && xCols->getCount() > nModelPos )
929         {
930             Reference< XPropertySet >  xCol(xCols->getByIndex(nModelPos),UNO_QUERY);
931             if ( xCol.is() )
932                 xEmptyReturn.set(xCol->getPropertyValue(PROPERTY_BOUNDFIELD),UNO_QUERY);
933         }
934         else
935             OSL_FAIL("SbaGridControl::getField getColumns returns NULL or ModelPos is > than count!");
936     }
937     catch (const Exception&)
938     {
939         TOOLS_WARN_EXCEPTION("dbaccess", "SbaGridControl::getField Exception occurred");
940     }
941 
942     return xEmptyReturn;
943 }
944 
IsReadOnlyDB() const945 bool SbaGridControl::IsReadOnlyDB() const
946 {
947     // assume yes if anything fails
948     bool bDBIsReadOnly = true;
949 
950     try
951     {
952         // the db is the implemented by the parent of the grid control's model ...
953         Reference< XChild >  xColumns(GetPeer()->getColumns(), UNO_QUERY);
954         if (xColumns.is())
955         {
956             Reference< XRowSet >  xDataSource(xColumns->getParent(), UNO_QUERY);
957             ::dbtools::ensureRowSetConnection( xDataSource, getContext(), nullptr );
958             Reference< XChild >  xConn(::dbtools::getConnection(xDataSource),UNO_QUERY);
959             if (xConn.is())
960             {
961                 // ... and the RO-flag simply is implemented by a property
962                 Reference< XPropertySet >  xDbProps(xConn->getParent(), UNO_QUERY);
963                 if (xDbProps.is())
964                 {
965                     Reference< XPropertySetInfo >  xInfo = xDbProps->getPropertySetInfo();
966                     if (xInfo->hasPropertyByName(PROPERTY_ISREADONLY))
967                         bDBIsReadOnly = ::comphelper::getBOOL(xDbProps->getPropertyValue(PROPERTY_ISREADONLY));
968                 }
969             }
970         }
971     }
972     catch (const Exception&)
973     {
974         TOOLS_WARN_EXCEPTION("dbaccess", "SbaGridControl::IsReadOnlyDB Exception occurred");
975     }
976 
977     return bDBIsReadOnly;
978 }
979 
MouseButtonDown(const BrowserMouseEvent & rMEvt)980 void SbaGridControl::MouseButtonDown( const BrowserMouseEvent& rMEvt)
981 {
982     long nRow = GetRowAtYPosPixel(rMEvt.GetPosPixel().Y());
983     sal_uInt16 nColPos = GetColumnAtXPosPixel(rMEvt.GetPosPixel().X());
984     sal_uInt16 nViewPos = (nColPos == BROWSER_INVALIDID) ? sal_uInt16(-1) : nColPos-1;
985         // 'the handle column' and 'no valid column' will both result in a view position of -1 !
986 
987     bool bHitEmptySpace = (nRow > GetRowCount()) || (nViewPos == sal_uInt16(-1));
988 
989     if (bHitEmptySpace && (rMEvt.GetClicks() == 2) && rMEvt.IsMod1())
990         Control::MouseButtonDown(rMEvt);
991     else
992         FmGridControl::MouseButtonDown(rMEvt);
993 }
994 
StartDrag(sal_Int8 _nAction,const Point & _rPosPixel)995 void SbaGridControl::StartDrag( sal_Int8 _nAction, const Point& _rPosPixel )
996 {
997     SolarMutexGuard aGuard;
998         // in the new DnD API, the solar mutex is not locked when StartDrag is called
999 
1000     bool bHandled = false;
1001 
1002     do
1003     {
1004         // determine if dragging is allowed
1005         // (Yes, this is controller (not view) functionality. But collecting and evaluating all the
1006         // information necessary via UNO would be quite difficult (if not impossible) so
1007         // my laziness says 'do it here'...)
1008         long nRow = GetRowAtYPosPixel(_rPosPixel.Y());
1009         sal_uInt16 nColPos = GetColumnAtXPosPixel(_rPosPixel.X());
1010         sal_uInt16 nViewPos = (nColPos == BROWSER_INVALIDID) ? sal_uInt16(-1) : nColPos-1;
1011             // 'the handle column' and 'no valid column' will both result in a view position of -1 !
1012 
1013         bool bCurrentRowVirtual = IsCurrentAppending() && IsModified();
1014         // the current row doesn't really exist: the user's appending a new one and already has entered some data,
1015         // so the row contains data which has no counter part within the data source
1016 
1017         long nCorrectRowCount = GetRowCount();
1018         if (GetOptions() & DbGridControlOptions::Insert)
1019             --nCorrectRowCount; // there is an empty row for inserting records
1020         if (bCurrentRowVirtual)
1021             --nCorrectRowCount;
1022 
1023         if ((nColPos == BROWSER_INVALIDID) || (nRow >= nCorrectRowCount))
1024             break;
1025 
1026         bool bHitHandle = (nColPos == 0);
1027 
1028         // check which kind of dragging has to be initiated
1029         if  (   bHitHandle                          //  the handle column
1030                                                     // AND
1031             &&  (   GetSelectRowCount()             //  at least one row is selected
1032                                                     // OR
1033                 ||  (   (nRow >= 0)                 //  a row below the header
1034                     &&  !bCurrentRowVirtual         //  we aren't appending a new record
1035                     &&  (nRow != GetCurrentPos())   //  a row which is not the current one
1036                     )                               // OR
1037                 ||  (   (0 == GetSelectRowCount())  // no rows selected
1038                     &&  (-1 == nRow)                // hit the header
1039                     )
1040                 )
1041             )
1042         {   // => start dragging the row
1043             if (GetDataWindow().IsMouseCaptured())
1044                 GetDataWindow().ReleaseMouse();
1045 
1046             if (0 == GetSelectRowCount())
1047                 // no rows selected, but here in this branch
1048                 // -> the user started dragging the upper left corner, which symbolizes the whole table
1049                 SelectAll();
1050 
1051             getMouseEvent().Clear();
1052             implTransferSelectedRows(static_cast<sal_Int16>(nRow), false);
1053 
1054             bHandled = true;
1055         }
1056         else if (   (nRow < 0)                      // the header
1057                 &&  (!bHitHandle)                   // non-handle column
1058                 &&  (nViewPos < GetViewColCount())  // valid (existing) column
1059                 )
1060         {   // => start dragging the column
1061             if (GetDataWindow().IsMouseCaptured())
1062                 GetDataWindow().ReleaseMouse();
1063 
1064             getMouseEvent().Clear();
1065             DoColumnDrag(nViewPos);
1066 
1067             bHandled = true;
1068         }
1069         else if (   !bHitHandle     // non-handle column
1070                 &&  (nRow >= 0)     // non-header row
1071                 )
1072         {   // => start dragging the field content
1073             if (GetDataWindow().IsMouseCaptured())
1074                 GetDataWindow().ReleaseMouse();
1075 
1076             getMouseEvent().Clear();
1077             DoFieldDrag(nViewPos, static_cast<sal_Int16>(nRow));
1078 
1079             bHandled = true;
1080         }
1081     }
1082     while (false);
1083 
1084     if (!bHandled)
1085         FmGridControl::StartDrag(_nAction, _rPosPixel);
1086 }
1087 
DoColumnDrag(sal_uInt16 nColumnPos)1088 void SbaGridControl::DoColumnDrag(sal_uInt16 nColumnPos)
1089 {
1090     Reference< XPropertySet >  xDataSource = getDataSource();
1091     OSL_ENSURE(xDataSource.is(), "SbaGridControl::DoColumnDrag : invalid data source !");
1092 
1093     Reference< XPropertySet > xAffectedCol;
1094     Reference< XPropertySet > xAffectedField;
1095     Reference< XConnection > xActiveConnection;
1096 
1097     // determine the field to drag
1098     OUString sField;
1099     try
1100     {
1101         xActiveConnection = ::dbtools::getConnection(Reference< XRowSet >(getDataSource(),UNO_QUERY));
1102 
1103         sal_uInt16 nModelPos = GetModelColumnPos(GetColumnIdFromViewPos(nColumnPos));
1104         Reference< XIndexContainer >  xCols = GetPeer()->getColumns();
1105         xAffectedCol.set(xCols->getByIndex(nModelPos),UNO_QUERY);
1106         if (xAffectedCol.is())
1107         {
1108             xAffectedCol->getPropertyValue(PROPERTY_CONTROLSOURCE) >>= sField;
1109             xAffectedField.set(xAffectedCol->getPropertyValue(PROPERTY_BOUNDFIELD),UNO_QUERY);
1110         }
1111     }
1112     catch(Exception&)
1113     {
1114         OSL_FAIL("SbaGridControl::DoColumnDrag : something went wrong while getting the column");
1115     }
1116     if (sField.isEmpty())
1117         return;
1118 
1119     rtl::Reference<OColumnTransferable> pDataTransfer = new OColumnTransferable(xDataSource, sField, xAffectedField, xActiveConnection, ColumnTransferFormatFlags::FIELD_DESCRIPTOR | ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
1120     pDataTransfer->StartDrag(this, DND_ACTION_COPY | DND_ACTION_LINK);
1121 }
1122 
CopySelectedRowsToClipboard()1123 void SbaGridControl::CopySelectedRowsToClipboard()
1124 {
1125     OSL_ENSURE( GetSelectRowCount() > 0, "SbaGridControl::CopySelectedRowsToClipboard: invalid call!" );
1126     implTransferSelectedRows( static_cast<sal_Int16>(FirstSelectedRow()), true );
1127 }
1128 
implTransferSelectedRows(sal_Int16 nRowPos,bool _bTrueIfClipboardFalseIfDrag)1129 void SbaGridControl::implTransferSelectedRows( sal_Int16 nRowPos, bool _bTrueIfClipboardFalseIfDrag )
1130 {
1131     Reference< XPropertySet > xForm = getDataSource();
1132     OSL_ENSURE( xForm.is(), "SbaGridControl::implTransferSelectedRows: invalid form!" );
1133 
1134     // build the sequence of numbers of selected rows
1135     Sequence< Any > aSelectedRows;
1136     bool bSelectionBookmarks = true;
1137 
1138     // collect the affected rows
1139     if ((GetSelectRowCount() == 0) && (nRowPos >= 0))
1140     {
1141         aSelectedRows.realloc( 1 );
1142         aSelectedRows[0] <<= static_cast<sal_Int32>(nRowPos + 1);
1143         bSelectionBookmarks = false;
1144     }
1145     else if ( !IsAllSelected() && GetSelectRowCount() )
1146     {
1147         aSelectedRows = getSelectionBookmarks();
1148         bSelectionBookmarks = true;
1149     }
1150 
1151     try
1152     {
1153         rtl::Reference<ODataClipboard> pTransfer = new ODataClipboard( xForm, aSelectedRows, bSelectionBookmarks, getContext() );
1154 
1155         if ( _bTrueIfClipboardFalseIfDrag )
1156             pTransfer->CopyToClipboard( this );
1157         else
1158             pTransfer->StartDrag(this, DND_ACTION_COPY | DND_ACTION_LINK);
1159     }
1160     catch(Exception&)
1161     {
1162     }
1163 }
1164 
DoFieldDrag(sal_uInt16 nColumnPos,sal_Int16 nRowPos)1165 void SbaGridControl::DoFieldDrag(sal_uInt16 nColumnPos, sal_Int16 nRowPos)
1166 {
1167     // the only thing to do here is dragging the pure cell text
1168     // the old implementation copied a SBA_FIELDDATAEXCHANGE_FORMAT, too, (which was rather expensive to obtain),
1169     // but we have no client for this DnD format anymore (the mail part of SO 5.2 was the only client)
1170 
1171     OUString sCellText;
1172     try
1173     {
1174         Reference< XGridFieldDataSupplier >  xFieldData(static_cast< XGridPeer* >(GetPeer()), UNO_QUERY);
1175         Sequence<sal_Bool> aSupportingText = xFieldData->queryFieldDataType(cppu::UnoType<decltype(sCellText)>::get());
1176         if (aSupportingText.getConstArray()[nColumnPos])
1177         {
1178             Sequence< Any> aCellContents = xFieldData->queryFieldData(nRowPos, cppu::UnoType<decltype(sCellText)>::get());
1179             sCellText = ::comphelper::getString(aCellContents.getConstArray()[nColumnPos]);
1180             ::svt::OStringTransfer::StartStringDrag(sCellText, this, DND_ACTION_COPY);
1181         }
1182     }
1183     catch(Exception&)
1184     {
1185         OSL_FAIL("SbaGridControl::DoFieldDrag : could not retrieve the cell's contents !");
1186         return;
1187     }
1188 
1189 }
1190 
1191 /// unary_function Functor object for class ZZ returntype is void
1192     struct SbaGridControlPrec
1193     {
operator ()SbaGridControlPrec1194         bool operator()(const DataFlavorExVector::value_type& _aType)
1195         {
1196             switch (_aType.mnSotId)
1197             {
1198                 case SotClipboardFormatId::DBACCESS_TABLE:   // table descriptor
1199                 case SotClipboardFormatId::DBACCESS_QUERY:   // query descriptor
1200                 case SotClipboardFormatId::DBACCESS_COMMAND: // SQL command
1201                     return true;
1202                 default: break;
1203             }
1204             return false;
1205         }
1206     };
AcceptDrop(const BrowserAcceptDropEvent & rEvt)1207 sal_Int8 SbaGridControl::AcceptDrop( const BrowserAcceptDropEvent& rEvt )
1208 {
1209     sal_Int8 nAction = DND_ACTION_NONE;
1210 
1211     // we need a valid connection
1212     if (!::dbtools::getConnection(Reference< XRowSet > (getDataSource(),UNO_QUERY)).is())
1213         return nAction;
1214 
1215     if ( IsDropFormatSupported( SotClipboardFormatId::STRING ) ) do
1216     {   // odd construction, but spares us a lot of (explicit ;) goto's
1217 
1218         if (!GetEmptyRow().is())
1219             // without an empty row we're not in update mode
1220             break;
1221 
1222         const long        nRow = GetRowAtYPosPixel(rEvt.maPosPixel.Y(), false);
1223         const sal_uInt16  nCol = GetColumnId(GetColumnAtXPosPixel(rEvt.maPosPixel.X()));
1224 
1225         long nCorrectRowCount = GetRowCount();
1226         if (GetOptions() & DbGridControlOptions::Insert)
1227             --nCorrectRowCount; // there is an empty row for inserting records
1228         if (IsCurrentAppending())
1229             --nCorrectRowCount; // the current data record doesn't really exist, we are appending a new one
1230 
1231         if ( (nCol == BROWSER_INVALIDID) || (nRow >= nCorrectRowCount) || (nCol == 0) )
1232             // no valid cell under the mouse cursor
1233             break;
1234 
1235         tools::Rectangle aRect = GetCellRect(nRow, nCol, false);
1236         if (!aRect.IsInside(rEvt.maPosPixel))
1237             // not dropped within a cell (a cell isn't as wide as the column - the are small spaces)
1238             break;
1239 
1240         if ((IsModified() || (GetCurrentRow().is() && GetCurrentRow()->IsModified())) && (GetCurrentPos() != nRow))
1241             // there is a current and modified row or cell and he text is to be dropped into another one
1242             break;
1243 
1244         CellControllerRef xCurrentController = Controller();
1245         if (xCurrentController.is() && xCurrentController->IsModified() && ((nRow != GetCurRow()) || (nCol != GetCurColumnId())))
1246             // the current controller is modified and the user wants to drop in another cell -> no chance
1247             // (when leaving the modified cell an error may occur - this is deadly while dragging)
1248             break;
1249 
1250         Reference< XPropertySet >  xField = getField(GetModelColumnPos(nCol));
1251         if (!xField.is())
1252             // the column is not valid bound (for instance a binary field)
1253             break;
1254 
1255         try
1256         {
1257             if (::comphelper::getBOOL(xField->getPropertyValue(PROPERTY_ISREADONLY)))
1258                 break;
1259         }
1260         catch (const Exception& )
1261         {
1262             // assume RO
1263             break;
1264         }
1265 
1266         try
1267         {
1268             // assume that text can be dropped into a field if the column has a css::awt::XTextComponent interface
1269             Reference< XIndexAccess >  xColumnControls(static_cast<css::form::XGridPeer*>(GetPeer()), UNO_QUERY);
1270             if (xColumnControls.is())
1271             {
1272                 Reference< css::awt::XTextComponent >  xColControl(
1273                     xColumnControls->getByIndex(GetViewColumnPos(nCol)),
1274                     css::uno::UNO_QUERY);
1275                 if (xColControl.is())
1276                 {
1277                     m_bActivatingForDrop = true;
1278                     GoToRowColumnId(nRow, nCol);
1279                     m_bActivatingForDrop = false;
1280 
1281                     nAction = DND_ACTION_COPY;
1282                 }
1283             }
1284         }
1285         catch( const Exception& )
1286         {
1287             DBG_UNHANDLED_EXCEPTION("dbaccess");
1288         }
1289 
1290     } while (false);
1291 
1292     if(nAction != DND_ACTION_COPY && GetEmptyRow().is())
1293     {
1294         const DataFlavorExVector& _rFlavors = GetDataFlavors();
1295         if(std::any_of(_rFlavors.begin(),_rFlavors.end(),SbaGridControlPrec()))
1296             nAction = DND_ACTION_COPY;
1297     }
1298 
1299     return (DND_ACTION_NONE != nAction) ? nAction : FmGridControl::AcceptDrop(rEvt);
1300 }
1301 
ExecuteDrop(const BrowserExecuteDropEvent & rEvt)1302 sal_Int8 SbaGridControl::ExecuteDrop( const BrowserExecuteDropEvent& rEvt )
1303 {
1304     // we need some properties of our data source
1305     Reference< XPropertySet >  xDataSource = getDataSource();
1306     if (!xDataSource.is())
1307         return DND_ACTION_NONE;
1308 
1309     // we need a valid connection
1310     if (!::dbtools::getConnection(Reference< XRowSet > (xDataSource,UNO_QUERY)).is())
1311         return DND_ACTION_NONE;
1312 
1313     if ( IsDropFormatSupported( SotClipboardFormatId::STRING ) )
1314     {
1315         long    nRow = GetRowAtYPosPixel(rEvt.maPosPixel.Y(), false);
1316         sal_uInt16  nCol = GetColumnAtXPosPixel(rEvt.maPosPixel.X());
1317 
1318         long nCorrectRowCount = GetRowCount();
1319         if (GetOptions() & DbGridControlOptions::Insert)
1320             --nCorrectRowCount; // there is an empty row for inserting records
1321         if (IsCurrentAppending())
1322             --nCorrectRowCount; // the current data record doesn't really exist, we are appending a new one
1323 
1324         OSL_ENSURE((nCol != BROWSER_INVALIDID) && (nRow < nCorrectRowCount), "SbaGridControl::Drop : dropped on an invalid position !");
1325             // AcceptDrop should have caught this
1326 
1327         // from now we work with ids instead of positions
1328         nCol = GetColumnId(nCol);
1329 
1330         GoToRowColumnId(nRow, nCol);
1331         if (!IsEditing())
1332             ActivateCell();
1333 
1334         CellControllerRef xCurrentController = Controller();
1335         if (!xCurrentController.is() || nullptr == dynamic_cast< const EditCellController* >(xCurrentController.get()))
1336             return DND_ACTION_NONE;
1337         Edit& rEdit = static_cast<Edit&>(xCurrentController->GetWindow());
1338 
1339         // get the dropped string
1340         TransferableDataHelper aDropped( rEvt.maDropEvent.Transferable );
1341         OUString sDropped;
1342         if ( !aDropped.GetString( SotClipboardFormatId::STRING, sDropped ) )
1343             return DND_ACTION_NONE;
1344 
1345         rEdit.SetText( sDropped );
1346         xCurrentController->SetModified();
1347         rEdit.Modify();
1348             // SetText itself doesn't call a Modify as it isn't a user interaction
1349 
1350         return DND_ACTION_COPY;
1351     }
1352 
1353     if(GetEmptyRow().is())
1354     {
1355         const DataFlavorExVector& _rFlavors = GetDataFlavors();
1356         if( std::any_of(_rFlavors.begin(),_rFlavors.end(), SbaGridControlPrec()) )
1357         {
1358             TransferableDataHelper aDropped( rEvt.maDropEvent.Transferable );
1359             m_aDataDescriptor = ODataAccessObjectTransferable::extractObjectDescriptor(aDropped);
1360             if (m_nAsyncDropEvent)
1361                 Application::RemoveUserEvent(m_nAsyncDropEvent);
1362             m_nAsyncDropEvent = Application::PostUserEvent(LINK(this, SbaGridControl, AsynchDropEvent), nullptr, true);
1363             return DND_ACTION_COPY;
1364         }
1365     }
1366 
1367     return DND_ACTION_NONE;
1368 }
1369 
getDataSource() const1370 Reference< XPropertySet >  SbaGridControl::getDataSource() const
1371 {
1372     Reference< XPropertySet >  xReturn;
1373 
1374     Reference< XChild >  xColumns(GetPeer()->getColumns(), UNO_QUERY);
1375     if (xColumns.is())
1376         xReturn.set(xColumns->getParent(), UNO_QUERY);
1377 
1378     return xReturn;
1379 }
1380 
IMPL_LINK_NOARG(SbaGridControl,AsynchDropEvent,void *,void)1381 IMPL_LINK_NOARG(SbaGridControl, AsynchDropEvent, void*, void)
1382 {
1383     m_nAsyncDropEvent = nullptr;
1384 
1385     Reference< XPropertySet >  xDataSource = getDataSource();
1386     if ( xDataSource.is() )
1387     {
1388         bool bCountFinal = false;
1389         xDataSource->getPropertyValue(PROPERTY_ISROWCOUNTFINAL) >>= bCountFinal;
1390         if ( !bCountFinal )
1391             setDataSource(nullptr); // detach from grid control
1392         Reference< XResultSetUpdate > xResultSetUpdate(xDataSource,UNO_QUERY);
1393         rtl::Reference<ODatabaseImportExport> pImExport = new ORowSetImportExport(GetFrameWeld(),xResultSetUpdate,m_aDataDescriptor, getContext());
1394         Hide();
1395         try
1396         {
1397             pImExport->initialize(m_aDataDescriptor);
1398             if (m_pMasterListener)
1399                 m_pMasterListener->BeforeDrop();
1400             if(!pImExport->Read())
1401             {
1402                 OUString sError = DBA_RES(STR_NO_COLUMNNAME_MATCHING);
1403                 throwGenericSQLException(sError,nullptr);
1404             }
1405             if (m_pMasterListener)
1406                 m_pMasterListener->AfterDrop();
1407             Show();
1408         }
1409         catch(const SQLException& e)
1410         {
1411             if (m_pMasterListener)
1412                 m_pMasterListener->AfterDrop();
1413             Show();
1414             ::dbtools::showError( ::dbtools::SQLExceptionInfo(e), VCLUnoHelper::GetInterface(this), getContext() );
1415         }
1416         catch(const Exception& )
1417         {
1418             DBG_UNHANDLED_EXCEPTION("dbaccess");
1419             if (m_pMasterListener)
1420                 m_pMasterListener->AfterDrop();
1421             Show();
1422         }
1423         if ( !bCountFinal )
1424             setDataSource(Reference< XRowSet >(xDataSource,UNO_QUERY));
1425     }
1426     m_aDataDescriptor.clear();
1427 }
1428 
GetAccessibleObjectDescription(::vcl::AccessibleBrowseBoxObjType eObjType,sal_Int32 _nPosition) const1429 OUString SbaGridControl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType eObjType,sal_Int32 _nPosition) const
1430 {
1431     OUString sRet;
1432     if ( ::vcl::BBTYPE_BROWSEBOX == eObjType )
1433     {
1434         SolarMutexGuard aGuard;
1435         sRet = DBA_RES(STR_DATASOURCE_GRIDCONTROL_DESC);
1436     }
1437     else
1438         sRet = FmGridControl::GetAccessibleObjectDescription( eObjType,_nPosition);
1439     return sRet;
1440 }
1441 
1442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1443