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