1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/log.hxx>
22 #include <svx/gridctrl.hxx>
23 #include <gridcell.hxx>
24 #include <svx/fmtools.hxx>
25 #include <svtools/stringtransfer.hxx>
26 #include <connectivity/dbtools.hxx>
27 #include <connectivity/dbconversion.hxx>
30 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
31 #include <com/sun/star/accessibility/XAccessible.hpp>
32 #include <com/sun/star/sdb/XResultSetAccess.hpp>
33 #include <com/sun/star/sdb/RowChangeAction.hpp>
34 #include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
35 #include <com/sun/star/sdbc/SQLException.hpp>
36 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
37 #include <com/sun/star/sdbc/XRowSet.hpp>
38 #include <com/sun/star/sdbcx/Privilege.hpp>
39 #include <com/sun/star/util/NumberFormatter.hpp>
40 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
43 #include <com/sun/star/container/XIndexAccess.hpp>
44 #include <tools/diagnose_ex.h>
45 #include <tools/debug.hxx>
46 #include <tools/fract.hxx>
47 #include <vcl/builder.hxx>
48 #include <vcl/menu.hxx>
49 #include <vcl/settings.hxx>
50 #include <vcl/commandevent.hxx>
51 #include <vcl/svapp.hxx>
53 #include <svx/strings.hrc>
55 #include <svx/dialmgr.hxx>
56 #include <sdbdatacolumn.hxx>
58 #include <comphelper/property.hxx>
59 #include <comphelper/types.hxx>
60 #include <cppuhelper/implbase.hxx>
67 using namespace ::dbtools
;
68 using namespace ::dbtools::DBTypeConversion
;
69 using namespace ::svxform
;
70 using namespace ::svt
;
71 using namespace ::com::sun::star::beans
;
72 using namespace ::com::sun::star::lang
;
73 using namespace ::com::sun::star::uno
;
74 using namespace ::com::sun::star::sdbc
;
75 using namespace ::com::sun::star::sdbcx
;
76 using namespace ::com::sun::star::sdb
;
77 using namespace ::com::sun::star::datatransfer
;
78 using namespace ::com::sun::star::container
;
79 using namespace com::sun::star::accessibility
;
81 #define ROWSTATUS(row) (!row.is() ? "NULL" : row->GetStatus() == GridRowStatus::Clean ? "CLEAN" : row->GetStatus() == GridRowStatus::Modified ? "MODIFIED" : row->GetStatus() == GridRowStatus::Deleted ? "DELETED" : "INVALID")
83 constexpr auto DEFAULT_BROWSE_MODE
=
84 BrowserMode::COLUMNSELECTION
85 | BrowserMode::MULTISELECTION
86 | BrowserMode::KEEPHIGHLIGHT
87 | BrowserMode::TRACKING_TIPS
90 | BrowserMode::HEADERBAR_NEW
;
92 class RowSetEventListener
: public ::cppu::WeakImplHelper
<XRowsChangeListener
>
94 VclPtr
<DbGridControl
> m_pControl
;
96 explicit RowSetEventListener(DbGridControl
* i_pControl
) : m_pControl(i_pControl
)
102 virtual void SAL_CALL
disposing(const css::lang::EventObject
& /*i_aEvt*/) override
105 virtual void SAL_CALL
rowsChanged(const css::sdb::RowsChangeEvent
& i_aEvt
) override
107 if ( i_aEvt
.Action
!= RowChangeAction::UPDATE
)
110 ::DbGridControl::GrantControlAccess aAccess
;
111 CursorWrapper
* pSeek
= m_pControl
->GetSeekCursor(aAccess
);
112 const DbGridRowRef
& rSeekRow
= m_pControl
->GetSeekRow(aAccess
);
113 for(const Any
& rBookmark
: i_aEvt
.Bookmarks
)
115 pSeek
->moveToBookmark(rBookmark
);
117 rSeekRow
->SetState(pSeek
, true);
118 sal_Int32 nSeekPos
= pSeek
->getRow() - 1;
119 m_pControl
->SetSeekPos(nSeekPos
,aAccess
);
120 m_pControl
->RowModified(nSeekPos
);
125 class GridFieldValueListener
;
126 typedef std::map
<sal_uInt16
, GridFieldValueListener
*> ColumnFieldValueListeners
;
128 class GridFieldValueListener
: protected ::comphelper::OPropertyChangeListener
131 DbGridControl
& m_rParent
;
132 rtl::Reference
<::comphelper::OPropertyChangeMultiplexer
> m_pRealListener
;
134 sal_Int16 m_nSuspended
;
135 bool m_bDisposed
: 1;
138 GridFieldValueListener(DbGridControl
& _rParent
, const Reference
< XPropertySet
>& xField
, sal_uInt16 _nId
);
139 virtual ~GridFieldValueListener() override
;
141 virtual void _propertyChanged(const PropertyChangeEvent
& evt
) override
;
143 void suspend() { ++m_nSuspended
; }
144 void resume() { --m_nSuspended
; }
149 GridFieldValueListener::GridFieldValueListener(DbGridControl
& _rParent
, const Reference
< XPropertySet
>& _rField
, sal_uInt16 _nId
)
150 :OPropertyChangeListener(m_aMutex
)
158 m_pRealListener
= new ::comphelper::OPropertyChangeMultiplexer(this, _rField
);
159 m_pRealListener
->addProperty(FM_PROP_VALUE
);
163 GridFieldValueListener::~GridFieldValueListener()
168 void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent
& /*_evt*/)
170 DBG_ASSERT(m_nSuspended
>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
171 if (m_nSuspended
<= 0)
172 m_rParent
.FieldValueChanged(m_nId
);
175 void GridFieldValueListener::dispose()
179 DBG_ASSERT(!m_pRealListener
, "GridFieldValueListener::dispose : inconsistent !");
183 if (m_pRealListener
.is())
185 m_pRealListener
->dispose();
186 m_pRealListener
.clear();
190 m_rParent
.FieldListenerDisposing(m_nId
);
193 class DisposeListenerGridBridge
: public FmXDisposeListener
195 DbGridControl
& m_rParent
;
196 rtl::Reference
<FmXDisposeMultiplexer
> m_xRealListener
;
199 DisposeListenerGridBridge( DbGridControl
& _rParent
, const Reference
< XComponent
>& _rxObject
);
200 virtual ~DisposeListenerGridBridge() override
;
202 virtual void disposing(sal_Int16 _nId
) override
{ m_rParent
.disposing(_nId
); }
205 DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl
& _rParent
, const Reference
< XComponent
>& _rxObject
)
206 :FmXDisposeListener()
212 m_xRealListener
= new FmXDisposeMultiplexer(this, _rxObject
);
216 DisposeListenerGridBridge::~DisposeListenerGridBridge()
218 if (m_xRealListener
.is())
220 m_xRealListener
->dispose();
224 const DbGridControlNavigationBarState ControlMap
[] =
226 DbGridControlNavigationBarState::Text
,
227 DbGridControlNavigationBarState::Absolute
,
228 DbGridControlNavigationBarState::Of
,
229 DbGridControlNavigationBarState::Count
,
230 DbGridControlNavigationBarState::First
,
231 DbGridControlNavigationBarState::Next
,
232 DbGridControlNavigationBarState::Prev
,
233 DbGridControlNavigationBarState::Last
,
234 DbGridControlNavigationBarState::New
,
235 DbGridControlNavigationBarState::NONE
238 bool CompareBookmark(const Any
& aLeft
, const Any
& aRight
)
240 return aLeft
== aRight
;
243 class FmXGridSourcePropListener
: public ::comphelper::OPropertyChangeListener
245 VclPtr
<DbGridControl
> m_pParent
;
247 // a DbGridControl has no mutex, so we use our own as the base class expects one
249 sal_Int16 m_nSuspended
;
252 explicit FmXGridSourcePropListener(DbGridControl
* _pParent
);
254 void suspend() { ++m_nSuspended
; }
255 void resume() { --m_nSuspended
; }
257 virtual void _propertyChanged(const PropertyChangeEvent
& evt
) override
;
260 FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl
* _pParent
)
261 :OPropertyChangeListener(m_aMutex
)
265 DBG_ASSERT(m_pParent
, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
268 void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent
& evt
)
270 DBG_ASSERT(m_nSuspended
>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
271 if (m_nSuspended
<= 0)
272 m_pParent
->DataSourcePropertyChanged(evt
);
275 const int nReserveNumDigits
= 7;
277 NavigationBar::AbsolutePos::AbsolutePos(std::unique_ptr
<weld::Entry
> xEntry
, NavigationBar
* pBar
)
278 : RecordItemWindowBase(std::move(xEntry
))
283 bool NavigationBar::AbsolutePos::DoKeyInput(const KeyEvent
& rEvt
)
285 if (rEvt
.GetKeyCode() == KEY_TAB
)
287 m_xParent
->GetParent()->GrabFocus();
290 return RecordItemWindowBase::DoKeyInput(rEvt
);
293 void NavigationBar::AbsolutePos::PositionFired(sal_Int64 nRecord
)
295 m_xParent
->PositionDataSource(nRecord
);
296 m_xParent
->InvalidateState(DbGridControlNavigationBarState::Absolute
);
299 void NavigationBar::PositionDataSource(sal_Int32 nRecord
)
303 // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
304 // so protect against this recursion
305 m_bPositioning
= true;
306 static_cast<DbGridControl
*>(GetParent())->MoveToPosition(nRecord
- 1);
307 m_bPositioning
= false;
310 NavigationBar::NavigationBar(vcl::Window
* pParent
)
311 : InterimItemWindow(pParent
, "svx/ui/navigationbar.ui", "NavigationBar")
312 , m_xRecordText(m_xBuilder
->weld_label("recordtext"))
313 , m_xAbsolute(new NavigationBar::AbsolutePos(m_xBuilder
->weld_entry("entry-noframe"), this))
314 , m_xRecordOf(m_xBuilder
->weld_label("recordof"))
315 , m_xRecordCount(m_xBuilder
->weld_label("recordcount"))
316 , m_xFirstBtn(m_xBuilder
->weld_button("first"))
317 , m_xPrevBtn(m_xBuilder
->weld_button("prev"))
318 , m_xNextBtn(m_xBuilder
->weld_button("next"))
319 , m_xLastBtn(m_xBuilder
->weld_button("last"))
320 , m_xNewBtn(m_xBuilder
->weld_button("new"))
322 , m_bPositioning(false)
324 vcl::Font
aApplFont(Application::GetSettings().GetStyleSettings().GetToolFont());
325 m_xAbsolute
->set_font(aApplFont
);
326 aApplFont
.SetTransparent(true);
327 m_xRecordText
->set_font(aApplFont
);
328 m_xRecordOf
->set_font(aApplFont
);
329 m_xRecordCount
->set_font(aApplFont
);
331 m_xFirstBtn
->set_help_id(HID_GRID_TRAVEL_FIRST
);
332 m_xPrevBtn
->set_help_id(HID_GRID_TRAVEL_PREV
);
333 m_xNextBtn
->set_help_id(HID_GRID_TRAVEL_NEXT
);
334 m_xLastBtn
->set_help_id(HID_GRID_TRAVEL_LAST
);
335 m_xNewBtn
->set_help_id(HID_GRID_TRAVEL_NEW
);
336 m_xAbsolute
->set_help_id(HID_GRID_TRAVEL_ABSOLUTE
);
337 m_xRecordCount
->set_help_id(HID_GRID_NUMBEROFRECORDS
);
339 // set handlers for buttons
340 m_xFirstBtn
->connect_clicked(LINK(this,NavigationBar
,OnClick
));
341 m_xLastBtn
->connect_clicked(LINK(this,NavigationBar
,OnClick
));
342 m_xNewBtn
->connect_clicked(LINK(this,NavigationBar
,OnClick
));
344 // instead of connect_clicked because we want a button held down to
345 // repeat the next/prev
346 m_xPrevBtn
->connect_mouse_press(LINK(this, NavigationBar
, PrevMousePressHdl
));
347 m_xNextBtn
->connect_mouse_press(LINK(this, NavigationBar
, NextMousePressHdl
));
348 m_xPrevBtn
->connect_mouse_release(LINK(this, NavigationBar
, PrevMouseReleaseHdl
));
349 m_xNextBtn
->connect_mouse_release(LINK(this, NavigationBar
, NextMouseReleaseHdl
));
351 auto nRepeatTime
= Application::GetSettings().GetMouseSettings().GetButtonRepeat();
352 m_aNextRepeat
.SetTimeout(nRepeatTime
);
353 m_aNextRepeat
.SetInvokeHandler(LINK(this, NavigationBar
, NextRepeatTimerHdl
));
354 m_aPrevRepeat
.SetTimeout(nRepeatTime
);
355 m_aPrevRepeat
.SetInvokeHandler(LINK(this, NavigationBar
, PrevRepeatTimerHdl
));
357 m_xRecordText
->set_label(SvxResId(RID_STR_REC_TEXT
));
358 m_xRecordOf
->set_label(SvxResId(RID_STR_REC_FROM_TEXT
));
359 m_xRecordCount
->set_label(OUString('?'));
361 auto nReserveWidth
= m_xRecordCount
->get_approximate_digit_width() * nReserveNumDigits
;
362 m_xAbsolute
->GetWidget()->set_size_request(nReserveWidth
, -1);
363 m_xRecordCount
->set_size_request(nReserveWidth
, -1);
366 NavigationBar::~NavigationBar()
371 void NavigationBar::dispose()
373 m_xRecordText
.reset();
376 m_xRecordCount
.reset();
382 InterimItemWindow::dispose();
385 sal_uInt16
NavigationBar::ArrangeControls()
387 return m_xContainer
->get_preferred_size().Width();
390 IMPL_LINK_NOARG(NavigationBar
, PrevRepeatTimerHdl
, Timer
*, void)
392 OnClick(*m_xPrevBtn
);
395 IMPL_LINK_NOARG(NavigationBar
, NextRepeatTimerHdl
, Timer
*, void)
397 OnClick(*m_xNextBtn
);
400 IMPL_LINK_NOARG(NavigationBar
, PrevMousePressHdl
, const MouseEvent
&, bool)
402 if (!m_xPrevBtn
->get_sensitive())
404 PrevRepeatTimerHdl(nullptr);
405 if (!m_xPrevBtn
->get_sensitive())
407 m_aPrevRepeat
.Start();
411 IMPL_LINK_NOARG(NavigationBar
, PrevMouseReleaseHdl
, const MouseEvent
&, bool)
413 m_aPrevRepeat
.Stop();
417 IMPL_LINK_NOARG(NavigationBar
, NextMousePressHdl
, const MouseEvent
&, bool)
419 if (m_xNextBtn
->get_sensitive())
421 NextRepeatTimerHdl(nullptr);
422 m_aNextRepeat
.Start();
427 IMPL_LINK_NOARG(NavigationBar
, NextMouseReleaseHdl
, const MouseEvent
&, bool)
429 m_aNextRepeat
.Stop();
433 IMPL_LINK(NavigationBar
, OnClick
, weld::Button
&, rButton
, void)
435 DbGridControl
* pParent
= static_cast<DbGridControl
*>(GetParent());
437 if (pParent
->m_aMasterSlotExecutor
.IsSet())
439 bool lResult
= false;
440 if (&rButton
== m_xFirstBtn
.get())
441 lResult
= pParent
->m_aMasterSlotExecutor
.Call(DbGridControlNavigationBarState::First
);
442 else if( &rButton
== m_xPrevBtn
.get() )
443 lResult
= pParent
->m_aMasterSlotExecutor
.Call(DbGridControlNavigationBarState::Prev
);
444 else if( &rButton
== m_xNextBtn
.get() )
445 lResult
= pParent
->m_aMasterSlotExecutor
.Call(DbGridControlNavigationBarState::Next
);
446 else if( &rButton
== m_xLastBtn
.get() )
447 lResult
= pParent
->m_aMasterSlotExecutor
.Call(DbGridControlNavigationBarState::Last
);
448 else if( &rButton
== m_xNewBtn
.get() )
449 lResult
= pParent
->m_aMasterSlotExecutor
.Call(DbGridControlNavigationBarState::New
);
452 // the link already handled it
456 if (&rButton
== m_xFirstBtn
.get())
457 pParent
->MoveToFirst();
458 else if( &rButton
== m_xPrevBtn
.get() )
459 pParent
->MoveToPrev();
460 else if( &rButton
== m_xNextBtn
.get() )
461 pParent
->MoveToNext();
462 else if( &rButton
== m_xLastBtn
.get() )
463 pParent
->MoveToLast();
464 else if( &rButton
== m_xNewBtn
.get() )
465 pParent
->AppendNew();
468 void NavigationBar::InvalidateAll(sal_Int32 nCurrentPos
, bool bAll
)
470 if (!(m_nCurrentPos
!= nCurrentPos
|| nCurrentPos
< 0 || bAll
))
473 DbGridControl
* pParent
= static_cast<DbGridControl
*>(GetParent());
475 sal_Int32 nAdjustedRowCount
= pParent
->GetRowCount() - ((pParent
->GetOptions() & DbGridControlOptions::Insert
) ? 2 : 1);
477 // check if everything needs to be invalidated
478 bAll
= bAll
|| m_nCurrentPos
<= 0;
479 bAll
= bAll
|| nCurrentPos
<= 0;
480 bAll
= bAll
|| m_nCurrentPos
>= nAdjustedRowCount
;
481 bAll
= bAll
|| nCurrentPos
>= nAdjustedRowCount
;
485 m_nCurrentPos
= nCurrentPos
;
487 while (ControlMap
[i
] != DbGridControlNavigationBarState::NONE
)
488 SetState(ControlMap
[i
++]);
490 else // is in the center
492 m_nCurrentPos
= nCurrentPos
;
493 SetState(DbGridControlNavigationBarState::Count
);
494 SetState(DbGridControlNavigationBarState::Absolute
);
498 bool NavigationBar::GetState(DbGridControlNavigationBarState nWhich
) const
500 DbGridControl
* pParent
= static_cast<DbGridControl
*>(GetParent());
502 if (!pParent
->IsOpen() || pParent
->IsDesignMode() || !pParent
->IsEnabled()
503 || pParent
->IsFilterMode() )
507 // check if we have a master state provider
508 if (pParent
->m_aMasterStateProvider
.IsSet())
510 tools::Long nState
= pParent
->m_aMasterStateProvider
.Call( nWhich
);
515 bool bAvailable
= true;
519 case DbGridControlNavigationBarState::First
:
520 case DbGridControlNavigationBarState::Prev
:
521 bAvailable
= m_nCurrentPos
> 0;
523 case DbGridControlNavigationBarState::Next
:
524 if(pParent
->m_bRecordCountFinal
)
526 bAvailable
= m_nCurrentPos
< pParent
->GetRowCount() - 1;
527 if (!bAvailable
&& pParent
->GetOptions() & DbGridControlOptions::Insert
)
528 bAvailable
= (m_nCurrentPos
== pParent
->GetRowCount() - 2) && pParent
->IsModified();
531 case DbGridControlNavigationBarState::Last
:
532 if(pParent
->m_bRecordCountFinal
)
534 if (pParent
->GetOptions() & DbGridControlOptions::Insert
)
535 bAvailable
= pParent
->IsCurrentAppending() ? pParent
->GetRowCount() > 1 :
536 m_nCurrentPos
!= pParent
->GetRowCount() - 2;
538 bAvailable
= m_nCurrentPos
!= pParent
->GetRowCount() - 1;
541 case DbGridControlNavigationBarState::New
:
542 bAvailable
= (pParent
->GetOptions() & DbGridControlOptions::Insert
) && pParent
->GetRowCount() && m_nCurrentPos
< pParent
->GetRowCount() - 1;
544 case DbGridControlNavigationBarState::Absolute
:
545 bAvailable
= pParent
->GetRowCount() > 0;
553 void NavigationBar::SetState(DbGridControlNavigationBarState nWhich
)
555 bool bAvailable
= GetState(nWhich
);
556 DbGridControl
* pParent
= static_cast<DbGridControl
*>(GetParent());
557 weld::Widget
* pWnd
= nullptr;
560 case DbGridControlNavigationBarState::First
:
561 pWnd
= m_xFirstBtn
.get();
563 case DbGridControlNavigationBarState::Prev
:
564 pWnd
= m_xPrevBtn
.get();
566 case DbGridControlNavigationBarState::Next
:
567 pWnd
= m_xNextBtn
.get();
569 case DbGridControlNavigationBarState::Last
:
570 pWnd
= m_xLastBtn
.get();
572 case DbGridControlNavigationBarState::New
:
573 pWnd
= m_xNewBtn
.get();
575 case DbGridControlNavigationBarState::Absolute
:
576 pWnd
= m_xAbsolute
->GetWidget();
578 m_xAbsolute
->set_text(OUString::number(m_nCurrentPos
+ 1));
580 m_xAbsolute
->set_text(OUString());
582 case DbGridControlNavigationBarState::Text
:
583 pWnd
= m_xRecordText
.get();
585 case DbGridControlNavigationBarState::Of
:
586 pWnd
= m_xRecordOf
.get();
588 case DbGridControlNavigationBarState::Count
:
590 pWnd
= m_xRecordCount
.get();
594 if (pParent
->GetOptions() & DbGridControlOptions::Insert
)
596 if (pParent
->IsCurrentAppending() && !pParent
->IsModified())
597 aText
= OUString::number(pParent
->GetRowCount());
599 aText
= OUString::number(pParent
->GetRowCount() - 1);
602 aText
= OUString::number(pParent
->GetRowCount());
603 if(!pParent
->m_bRecordCountFinal
)
609 // add the number of selected rows, if applicable
610 if (pParent
->GetSelectRowCount())
612 OUString aExtendedInfo
= aText
+ " (" +
613 OUString::number(pParent
->GetSelectRowCount()) + ")";
614 m_xRecordCount
->set_label(aExtendedInfo
);
617 m_xRecordCount
->set_label(aText
);
619 pParent
->SetRealRowCount(aText
);
623 DBG_ASSERT(pWnd
, "no window");
624 if (!(pWnd
&& (pWnd
->get_sensitive() != bAvailable
)))
627 // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
628 // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
630 // For further explanation see Bug 69900.
631 pWnd
->set_sensitive(bAvailable
);
634 if (pWnd
== m_xNextBtn
.get())
635 m_aNextRepeat
.Stop();
636 else if (pWnd
== m_xPrevBtn
.get())
637 m_aPrevRepeat
.Stop();
641 DbGridRow::DbGridRow():m_eStatus(GridRowStatus::Clean
), m_bIsNew(true)
644 DbGridRow::DbGridRow(CursorWrapper
* pCur
, bool bPaintCursor
)
648 if (pCur
&& pCur
->Is())
650 Reference
< XIndexAccess
> xColumns(pCur
->getColumns(), UNO_QUERY
);
651 for (sal_Int32 i
= 0; i
< xColumns
->getCount(); ++i
)
653 Reference
< XPropertySet
> xColSet(
654 xColumns
->getByIndex(i
), css::uno::UNO_QUERY
);
655 m_aVariants
.emplace_back( new DataColumn(xColSet
) );
658 if (pCur
->rowDeleted())
659 m_eStatus
= GridRowStatus::Deleted
;
663 m_eStatus
= (pCur
->isAfterLast() || pCur
->isBeforeFirst()) ? GridRowStatus::Invalid
: GridRowStatus::Clean
;
666 const Reference
< XPropertySet
>& xSet
= pCur
->getPropertySet();
669 m_bIsNew
= ::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ISNEW
));
670 if (!m_bIsNew
&& (pCur
->isAfterLast() || pCur
->isBeforeFirst()))
671 m_eStatus
= GridRowStatus::Invalid
;
672 else if (::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ISMODIFIED
)))
673 m_eStatus
= GridRowStatus::Modified
;
675 m_eStatus
= GridRowStatus::Clean
;
678 m_eStatus
= GridRowStatus::Invalid
;
681 if (!m_bIsNew
&& IsValid())
682 m_aBookmark
= pCur
->getBookmark();
687 m_eStatus
= GridRowStatus::Invalid
;
690 DbGridRow::~DbGridRow()
694 void DbGridRow::SetState(CursorWrapper
* pCur
, bool bPaintCursor
)
696 if (pCur
&& pCur
->Is())
698 if (pCur
->rowDeleted())
700 m_eStatus
= GridRowStatus::Deleted
;
705 m_eStatus
= GridRowStatus::Clean
;
708 const Reference
< XPropertySet
>& xSet
= pCur
->getPropertySet();
709 DBG_ASSERT(xSet
.is(), "DbGridRow::SetState : invalid cursor !");
711 if (::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ISMODIFIED
)))
712 m_eStatus
= GridRowStatus::Modified
;
713 m_bIsNew
= ::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ISNEW
));
721 if (!m_bIsNew
&& IsValid())
722 m_aBookmark
= pCur
->getBookmark();
728 DBG_UNHANDLED_EXCEPTION("svx");
730 m_eStatus
= GridRowStatus::Invalid
;
737 m_eStatus
= GridRowStatus::Invalid
;
742 DbGridControl::DbGridControl(
743 Reference
< XComponentContext
> const & _rxContext
,
744 vcl::Window
* pParent
,
746 :EditBrowseBox(pParent
, EditBrowseBoxFlags::NONE
, nBits
, DEFAULT_BROWSE_MODE
)
747 ,m_xContext(_rxContext
)
748 ,m_aBar(VclPtr
<NavigationBar
>::Create(this))
749 ,m_nAsynAdjustEvent(nullptr)
750 ,m_pDataSourcePropListener(nullptr)
751 ,m_pFieldListeners(nullptr)
752 ,m_pGridListener(nullptr)
755 ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate())
756 ,m_nMode(DEFAULT_BROWSE_MODE
)
758 ,m_nDeleteEvent(nullptr)
759 ,m_nOptions(DbGridControlOptions::Readonly
)
760 ,m_nOptionMask(DbGridControlOptions::Insert
| DbGridControlOptions::Update
| DbGridControlOptions::Delete
)
761 ,m_nLastColId(sal_uInt16(-1))
763 ,m_bDesignMode(false)
764 ,m_bRecordCountFinal(false)
765 ,m_bNavigationBar(true)
766 ,m_bSynchDisplay(true)
768 ,m_bFilterMode(false)
769 ,m_bWantDestruction(false)
770 ,m_bPendingAdjustRows(false)
771 ,m_bHideScrollbars( false )
775 OUString
sName(SvxResId(RID_STR_NAVIGATIONBAR
));
776 m_aBar
->SetAccessibleName(sName
);
778 ImplInitWindow( InitWindowFacet::All
);
781 void DbGridControl::InsertHandleColumn()
783 // BrowseBox has problems when painting without a handleColumn (hide it here)
785 BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
787 BrowseBox::InsertHandleColumn(0);
790 void DbGridControl::Init()
792 VclPtr
<BrowserHeader
> pNewHeader
= CreateHeaderBar(this);
793 pHeader
->SetMouseTransparent(false);
795 SetHeaderBar(pNewHeader
);
797 SetCursorColor(Color(0xFF, 0, 0));
799 InsertHandleColumn();
802 DbGridControl::~DbGridControl()
807 void DbGridControl::dispose()
813 m_bWantDestruction
= true;
814 osl::MutexGuard
aGuard(m_aDestructionSafety
);
815 if (m_pFieldListeners
)
816 DisconnectFromFields();
817 m_pCursorDisposeListener
.reset();
821 Application::RemoveUserEvent(m_nDeleteEvent
);
823 if (m_pDataSourcePropMultiplexer
.is())
825 m_pDataSourcePropMultiplexer
->dispose();
826 m_pDataSourcePropMultiplexer
.clear(); // this should delete the multiplexer
827 delete m_pDataSourcePropListener
;
828 m_pDataSourcePropListener
= nullptr;
830 m_xRowSetListener
.clear();
832 m_pDataCursor
.reset();
833 m_pSeekCursor
.reset();
835 m_aBar
.disposeAndClear();
837 EditBrowseBox::dispose();
840 void DbGridControl::StateChanged( StateChangedType nType
)
842 EditBrowseBox::StateChanged( nType
);
846 case StateChangedType::Mirroring
:
847 ImplInitWindow( InitWindowFacet::WritingMode
);
851 case StateChangedType::Zoom
:
853 ImplInitWindow( InitWindowFacet::Font
);
855 // and give it a chance to rearrange
856 Point aPoint
= GetControlArea().TopLeft();
857 sal_uInt16 nX
= static_cast<sal_uInt16
>(aPoint
.X());
858 ArrangeControls(nX
, static_cast<sal_uInt16
>(aPoint
.Y()));
859 ReserveControlArea(nX
);
862 case StateChangedType::ControlFont
:
863 ImplInitWindow( InitWindowFacet::Font
);
866 case StateChangedType::ControlForeground
:
867 ImplInitWindow( InitWindowFacet::Foreground
);
870 case StateChangedType::ControlBackground
:
871 ImplInitWindow( InitWindowFacet::Background
);
878 void DbGridControl::DataChanged( const DataChangedEvent
& rDCEvt
)
880 EditBrowseBox::DataChanged( rDCEvt
);
881 if ( (rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) &&
882 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
884 ImplInitWindow( InitWindowFacet::All
);
889 void DbGridControl::Select()
891 EditBrowseBox::Select();
893 // as the selected rows may have changed, update the according display in our navigation bar
894 m_aBar
->InvalidateState(DbGridControlNavigationBarState::Count
);
897 m_pGridListener
->selectionChanged();
900 void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat
)
902 for (auto const & pCol
: m_aColumns
)
904 pCol
->ImplInitWindow( GetDataWindow(), _eInitWhat
);
907 if ( _eInitWhat
& InitWindowFacet::WritingMode
)
909 if ( m_bNavigationBar
)
911 m_aBar
->EnableRTL( IsRTLEnabled() );
915 if ( _eInitWhat
& InitWindowFacet::Font
)
917 if ( m_bNavigationBar
)
919 if ( IsControlFont() )
920 m_aBar
->SetControlFont( GetControlFont() );
922 m_aBar
->SetControlFont();
924 m_aBar
->SetZoom( GetZoom() );
928 if ( !(_eInitWhat
& InitWindowFacet::Background
) )
931 if (IsControlBackground())
933 GetDataWindow().SetBackground(GetControlBackground());
934 GetDataWindow().SetControlBackground(GetControlBackground());
935 GetDataWindow().SetFillColor(GetControlBackground());
939 GetDataWindow().SetControlBackground();
940 GetDataWindow().SetFillColor(GetFillColor());
944 void DbGridControl::RemoveRows(bool bNewCursor
)
946 // Did the data cursor change?
949 m_pSeekCursor
.reset();
950 m_xPaintRow
= m_xDataRow
= m_xEmptyRow
= m_xCurrentRow
= m_xSeekRow
= nullptr;
951 m_nCurrentPos
= m_nSeekPos
= -1;
952 m_nOptions
= DbGridControlOptions::Readonly
;
954 RowRemoved(0, GetRowCount(), false);
963 void DbGridControl::RemoveRows()
965 // we're going to remove all columns and all row, so deactivate the current cell
969 // de-initialize all columns
970 // if there are columns, free all controllers
971 for (auto const & pColumn
: m_aColumns
)
974 m_pSeekCursor
.reset();
975 m_pDataCursor
.reset();
977 m_xPaintRow
= m_xDataRow
= m_xEmptyRow
= m_xCurrentRow
= m_xSeekRow
= nullptr;
978 m_nCurrentPos
= m_nSeekPos
= m_nTotalCount
= -1;
979 m_nOptions
= DbGridControlOptions::Readonly
;
981 // reset number of sentences to zero in the browser
982 EditBrowseBox::RemoveRows();
983 m_aBar
->InvalidateAll(m_nCurrentPos
, true);
986 void DbGridControl::ArrangeControls(sal_uInt16
& nX
, sal_uInt16 nY
)
988 // positioning of the controls
989 if (m_bNavigationBar
)
991 tools::Rectangle
aRect(GetControlArea());
992 m_aBar
->SetPosSizePixel(Point(0, nY
+ 1), Size(aRect
.GetSize().Width(), aRect
.GetSize().Height() - 1));
993 nX
= m_aBar
->ArrangeControls();
997 void DbGridControl::EnableHandle(bool bEnable
)
999 if (m_bHandle
== bEnable
)
1002 // HandleColumn is only hidden because there are a lot of problems while painting otherwise
1003 RemoveColumn( HandleColumnId
);
1004 m_bHandle
= bEnable
;
1005 InsertHandleColumn();
1010 bool adjustModeForScrollbars( BrowserMode
& _rMode
, bool _bNavigationBar
, bool _bHideScrollbars
)
1012 BrowserMode nOldMode
= _rMode
;
1014 if ( !_bNavigationBar
)
1016 _rMode
&= ~BrowserMode::AUTO_HSCROLL
;
1019 if ( _bHideScrollbars
)
1021 _rMode
|= BrowserMode::NO_HSCROLL
| BrowserMode::NO_VSCROLL
;
1022 _rMode
&= ~BrowserMode( BrowserMode::AUTO_HSCROLL
| BrowserMode::AUTO_VSCROLL
);
1026 _rMode
|= BrowserMode::AUTO_HSCROLL
| BrowserMode::AUTO_VSCROLL
;
1027 _rMode
&= ~BrowserMode( BrowserMode::NO_HSCROLL
| BrowserMode::NO_VSCROLL
);
1030 // note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular,
1031 // _bHideScrollbars is ignored then
1032 if ( _bNavigationBar
)
1034 _rMode
|= BrowserMode::AUTO_HSCROLL
;
1035 _rMode
&= ~BrowserMode::NO_HSCROLL
;
1038 return nOldMode
!= _rMode
;
1042 void DbGridControl::EnableNavigationBar(bool bEnable
)
1044 if (m_bNavigationBar
== bEnable
)
1047 m_bNavigationBar
= bEnable
;
1053 m_aBar
->InvalidateAll(m_nCurrentPos
, true);
1055 if ( adjustModeForScrollbars( m_nMode
, m_bNavigationBar
, m_bHideScrollbars
) )
1058 // get size of the reserved ControlArea
1059 Point aPoint
= GetControlArea().TopLeft();
1060 sal_uInt16 nX
= static_cast<sal_uInt16
>(aPoint
.X());
1062 ArrangeControls(nX
, static_cast<sal_uInt16
>(aPoint
.Y()));
1063 ReserveControlArea(nX
);
1070 if ( adjustModeForScrollbars( m_nMode
, m_bNavigationBar
, m_bHideScrollbars
) )
1073 ReserveControlArea();
1077 DbGridControlOptions
DbGridControl::SetOptions(DbGridControlOptions nOpt
)
1079 DBG_ASSERT(!m_xCurrentRow
.is() || !m_xCurrentRow
->IsModified(),
1080 "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
1082 // for the next setDataSource (which is triggered by a refresh, for instance)
1083 m_nOptionMask
= nOpt
;
1085 // normalize the new options
1086 Reference
< XPropertySet
> xDataSourceSet
= m_pDataCursor
->getPropertySet();
1087 if (xDataSourceSet
.is())
1089 // check what kind of options are available
1090 sal_Int32 nPrivileges
= 0;
1091 xDataSourceSet
->getPropertyValue(FM_PROP_PRIVILEGES
) >>= nPrivileges
;
1092 if ((nPrivileges
& Privilege::INSERT
) == 0)
1093 nOpt
&= ~DbGridControlOptions::Insert
;
1094 if ((nPrivileges
& Privilege::UPDATE
) == 0)
1095 nOpt
&= ~DbGridControlOptions::Update
;
1096 if ((nPrivileges
& Privilege::DELETE
) == 0)
1097 nOpt
&= ~DbGridControlOptions::Delete
;
1100 nOpt
= DbGridControlOptions::Readonly
;
1102 // need to do something after that ?
1103 if (nOpt
== m_nOptions
)
1106 // the 'update' option only affects our BrowserMode (with or w/o focus rect)
1107 BrowserMode nNewMode
= m_nMode
;
1108 if (!(m_nMode
& BrowserMode::CURSOR_WO_FOCUS
))
1110 if (nOpt
& DbGridControlOptions::Update
)
1111 nNewMode
|= BrowserMode::HIDECURSOR
;
1113 nNewMode
&= ~BrowserMode::HIDECURSOR
;
1116 nNewMode
&= ~BrowserMode::HIDECURSOR
;
1117 // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1119 if (nNewMode
!= m_nMode
)
1125 // _after_ setting the mode because this results in an ActivateCell
1128 bool bInsertChanged
= (nOpt
& DbGridControlOptions::Insert
) != (m_nOptions
& DbGridControlOptions::Insert
);
1130 // we need to set this before the code below because it indirectly uses m_nOptions
1132 // the 'insert' option affects our empty row
1135 if (m_nOptions
& DbGridControlOptions::Insert
)
1136 { // the insert option is to be set
1137 m_xEmptyRow
= new DbGridRow();
1138 RowInserted(GetRowCount());
1141 { // the insert option is to be reset
1142 m_xEmptyRow
= nullptr;
1143 if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1144 GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1145 RowRemoved(GetRowCount());
1149 // the 'delete' options has no immediate consequences
1156 void DbGridControl::ForceHideScrollbars()
1158 if ( m_bHideScrollbars
)
1161 m_bHideScrollbars
= true;
1163 if ( adjustModeForScrollbars( m_nMode
, m_bNavigationBar
, m_bHideScrollbars
) )
1167 void DbGridControl::EnablePermanentCursor(bool bEnable
)
1169 if (IsPermanentCursorEnabled() == bEnable
)
1174 m_nMode
&= ~BrowserMode::HIDECURSOR
; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect
1175 m_nMode
|= BrowserMode::CURSOR_WO_FOCUS
;
1179 if (m_nOptions
& DbGridControlOptions::Update
)
1180 m_nMode
|= BrowserMode::HIDECURSOR
; // no cursor at all
1182 m_nMode
&= ~BrowserMode::HIDECURSOR
; // at least the "non-permanent" cursor
1184 m_nMode
&= ~BrowserMode::CURSOR_WO_FOCUS
;
1188 bool bWasEditing
= IsEditing();
1194 bool DbGridControl::IsPermanentCursorEnabled() const
1196 return (m_nMode
& BrowserMode::CURSOR_WO_FOCUS
) && !(m_nMode
& BrowserMode::HIDECURSOR
);
1199 void DbGridControl::refreshController(sal_uInt16 _nColId
, GrantControlAccess
/*_aAccess*/)
1201 if ((GetCurColumnId() == _nColId
) && IsEditing())
1202 { // the controller which is currently active needs to be refreshed
1208 void DbGridControl::setDataSource(const Reference
< XRowSet
>& _xCursor
, DbGridControlOptions nOpts
)
1210 if (!_xCursor
.is() && !m_pDataCursor
)
1213 if (m_pDataSourcePropMultiplexer
.is())
1215 m_pDataSourcePropMultiplexer
->dispose();
1216 m_pDataSourcePropMultiplexer
.clear(); // this should delete the multiplexer
1217 delete m_pDataSourcePropListener
;
1218 m_pDataSourcePropListener
= nullptr;
1220 m_xRowSetListener
.clear();
1222 // is the new cursor valid ?
1223 // the cursor is only valid if it contains some columns
1224 // if there is no cursor or the cursor is not valid we have to clean up and leave
1225 if (!_xCursor
.is() || !Reference
< XColumnsSupplier
> (_xCursor
, UNO_QUERY_THROW
)->getColumns()->hasElements())
1231 // did the data cursor change?
1232 sal_uInt16 nCurPos
= GetColumnPos(GetCurColumnId());
1234 SetUpdateMode(false);
1236 DisconnectFromFields();
1238 m_pCursorDisposeListener
.reset();
1241 ::osl::MutexGuard
aGuard(m_aAdjustSafety
);
1242 if (m_nAsynAdjustEvent
)
1244 // the adjust was thought to work with the old cursor which we don't have anymore
1245 RemoveUserEvent(m_nAsynAdjustEvent
);
1246 m_nAsynAdjustEvent
= nullptr;
1250 // get a new formatter and data cursor
1251 m_xFormatter
= nullptr;
1252 Reference
< css::util::XNumberFormatsSupplier
> xSupplier
= getNumberFormats(getConnection(_xCursor
), true);
1255 m_xFormatter
= css::util::NumberFormatter::create(m_xContext
);
1256 m_xFormatter
->attachNumberFormatsSupplier(xSupplier
);
1258 // retrieve the datebase of the Numberformatter
1261 xSupplier
->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate
;
1268 m_pDataCursor
.reset(new CursorWrapper(_xCursor
));
1270 // now create a cursor for painting rows
1271 // we need that cursor only if we are not in insert only mode
1272 Reference
< XResultSet
> xClone
;
1273 Reference
< XResultSetAccess
> xAccess( _xCursor
, UNO_QUERY
);
1276 xClone
= xAccess
.is() ? xAccess
->createResultSet() : Reference
< XResultSet
> ();
1282 m_pSeekCursor
.reset(new CursorWrapper(xClone
));
1284 // property listening on the data source
1285 // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
1286 // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
1287 // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
1288 // and forwards the property changes to our special method "DataSourcePropertyChanged".)
1291 m_pDataSourcePropListener
= new FmXGridSourcePropListener(this);
1292 m_pDataSourcePropMultiplexer
= new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener
, m_pDataCursor
->getPropertySet() );
1293 m_pDataSourcePropMultiplexer
->addProperty(FM_PROP_ISMODIFIED
);
1294 m_pDataSourcePropMultiplexer
->addProperty(FM_PROP_ISNEW
);
1297 BrowserMode nOldMode
= m_nMode
;
1302 Reference
< XPropertySet
> xSet(_xCursor
, UNO_QUERY
);
1305 // check what kind of options are available
1306 sal_Int32 nConcurrency
= ResultSetConcurrency::READ_ONLY
;
1307 xSet
->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY
) >>= nConcurrency
;
1309 if ( ResultSetConcurrency::UPDATABLE
== nConcurrency
)
1311 sal_Int32 nPrivileges
= 0;
1312 xSet
->getPropertyValue(FM_PROP_PRIVILEGES
) >>= nPrivileges
;
1314 // Insert Option should be set if insert only otherwise you won't see any rows
1315 // and no insertion is possible
1316 if ((m_nOptionMask
& DbGridControlOptions::Insert
)
1317 && ((nPrivileges
& Privilege::INSERT
) == Privilege::INSERT
) && (nOpts
& DbGridControlOptions::Insert
))
1318 m_nOptions
|= DbGridControlOptions::Insert
;
1319 if ((m_nOptionMask
& DbGridControlOptions::Update
)
1320 && ((nPrivileges
& Privilege::UPDATE
) == Privilege::UPDATE
) && (nOpts
& DbGridControlOptions::Update
))
1321 m_nOptions
|= DbGridControlOptions::Update
;
1322 if ((m_nOptionMask
& DbGridControlOptions::Delete
)
1323 && ((nPrivileges
& Privilege::DELETE
) == Privilege::DELETE
) && (nOpts
& DbGridControlOptions::Delete
))
1324 m_nOptions
|= DbGridControlOptions::Delete
;
1328 catch( const Exception
& )
1330 DBG_UNHANDLED_EXCEPTION("svx");
1333 bool bPermanentCursor
= IsPermanentCursorEnabled();
1334 m_nMode
= DEFAULT_BROWSE_MODE
;
1336 if ( bPermanentCursor
)
1338 m_nMode
|= BrowserMode::CURSOR_WO_FOCUS
;
1339 m_nMode
&= ~BrowserMode::HIDECURSOR
;
1343 // updates are allowed -> no focus rectangle
1344 if ( m_nOptions
& DbGridControlOptions::Update
)
1345 m_nMode
|= BrowserMode::HIDECURSOR
;
1348 m_nMode
|= BrowserMode::MULTISELECTION
;
1350 adjustModeForScrollbars( m_nMode
, m_bNavigationBar
, m_bHideScrollbars
);
1352 Reference
< XColumnsSupplier
> xSupplyColumns(_xCursor
, UNO_QUERY
);
1353 if (xSupplyColumns
.is())
1354 InitColumnsByFields(Reference
< XIndexAccess
> (xSupplyColumns
->getColumns(), UNO_QUERY
));
1359 sal_uInt32
nRecordCount(0);
1363 Reference
< XPropertySet
> xSet
= m_pDataCursor
->getPropertySet();
1364 xSet
->getPropertyValue(FM_PROP_ROWCOUNT
) >>= nRecordCount
;
1365 m_bRecordCountFinal
= ::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ROWCOUNTFINAL
));
1367 m_xRowSetListener
= new RowSetEventListener(this);
1368 Reference
< XRowsChangeBroadcaster
> xChangeBroad(xSet
,UNO_QUERY
);
1369 if ( xChangeBroad
.is( ) )
1370 xChangeBroad
->addRowsChangeListener(m_xRowSetListener
);
1373 // insert the currently known rows
1374 // and one row if we are able to insert rows
1375 if (m_nOptions
& DbGridControlOptions::Insert
)
1377 // insert the empty row for insertion
1378 m_xEmptyRow
= new DbGridRow();
1383 m_xPaintRow
= m_xSeekRow
= new DbGridRow(m_pSeekCursor
.get(), true);
1384 m_xDataRow
= new DbGridRow(m_pDataCursor
.get(), false);
1385 RowInserted(0, nRecordCount
, false);
1387 if (m_xSeekRow
->IsValid())
1390 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
1392 catch( const Exception
& )
1394 DBG_UNHANDLED_EXCEPTION("svx");
1400 // no rows so we don't need a seekcursor
1401 m_pSeekCursor
.reset();
1405 // go to the old column
1406 if (nCurPos
== BROWSER_INVALIDID
|| nCurPos
>= ColCount())
1409 // Column zero is a valid choice and guaranteed to exist,
1410 // but invisible to the user; if we have at least one
1411 // user-visible column, go to that one.
1412 if (nCurPos
== 0 && ColCount() > 1)
1415 // there are rows so go to the selected current column
1417 GoToRowColumnId(0, GetColumnId(nCurPos
));
1418 // else stop the editing if necessary
1419 else if (IsEditing())
1422 // now reset the mode
1423 if (m_nMode
!= nOldMode
)
1426 // RecalcRows was already called while resizing
1427 if (!IsResizing() && GetRowCount())
1428 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1430 m_aBar
->InvalidateAll(m_nCurrentPos
, true);
1431 SetUpdateMode(true);
1433 // start listening on the seek cursor
1435 m_pCursorDisposeListener
.reset(new DisposeListenerGridBridge(*this, Reference
< XComponent
> (Reference
< XInterface
>(*m_pSeekCursor
), UNO_QUERY
)));
1438 void DbGridControl::RemoveColumns()
1445 EditBrowseBox::RemoveColumns();
1448 std::unique_ptr
<DbGridColumn
> DbGridControl::CreateColumn(sal_uInt16 nId
)
1450 return std::unique_ptr
<DbGridColumn
>(new DbGridColumn(nId
, *this));
1453 sal_uInt16
DbGridControl::AppendColumn(const OUString
& rName
, sal_uInt16 nWidth
, sal_uInt16 nModelPos
, sal_uInt16 nId
)
1455 DBG_ASSERT(nId
== BROWSER_INVALIDID
, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1456 sal_uInt16 nRealPos
= nModelPos
;
1457 if (nModelPos
!= HEADERBAR_APPEND
)
1459 // calc the view pos. we can't use our converting functions because the new column
1460 // has no VCL-representation, yet.
1461 sal_Int16 nViewPos
= nModelPos
;
1464 if ( m_aColumns
[ nModelPos
]->IsHidden() )
1467 // restore nModelPos, we need it later
1468 nModelPos
= nRealPos
;
1469 // the position the base class gets is the view pos + 1 (because of the handle column)
1470 nRealPos
= nViewPos
+ 1;
1473 // calculate the new id
1474 for (nId
=1; (GetModelColumnPos(nId
) != GRID_COLUMN_NOT_FOUND
) && size_t(nId
) <= m_aColumns
.size(); ++nId
)
1476 DBG_ASSERT(GetViewColumnPos(nId
) == GRID_COLUMN_NOT_FOUND
, "DbGridControl::AppendColumn : inconsistent internal state !");
1477 // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
1479 EditBrowseBox::AppendColumn(rName
, nWidth
, nRealPos
, nId
);
1480 if (nModelPos
== HEADERBAR_APPEND
)
1481 m_aColumns
.push_back( CreateColumn(nId
) );
1483 m_aColumns
.insert( m_aColumns
.begin() + nModelPos
, CreateColumn(nId
) );
1488 void DbGridControl::RemoveColumn(sal_uInt16 nId
)
1490 EditBrowseBox::RemoveColumn(nId
);
1492 const sal_uInt16 nIndex
= GetModelColumnPos(nId
);
1493 if(nIndex
!= GRID_COLUMN_NOT_FOUND
)
1495 m_aColumns
.erase( m_aColumns
.begin()+nIndex
);
1499 void DbGridControl::ColumnMoved(sal_uInt16 nId
)
1501 EditBrowseBox::ColumnMoved(nId
);
1503 // remove the col from the model
1504 sal_uInt16 nOldModelPos
= GetModelColumnPos(nId
);
1506 DbGridColumn
* pCol
= m_aColumns
[ nOldModelPos
].get();
1507 DBG_ASSERT(!pCol
->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
1510 // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
1511 // so the method won't work (in fact it would return the old model pos)
1513 // the new view pos is calculated easily
1514 sal_uInt16 nNewViewPos
= GetViewColumnPos(nId
);
1516 // from that we can compute the new model pos
1517 size_t nNewModelPos
;
1518 for (nNewModelPos
= 0; nNewModelPos
< m_aColumns
.size(); ++nNewModelPos
)
1520 if (!m_aColumns
[ nNewModelPos
]->IsHidden())
1528 DBG_ASSERT( nNewModelPos
< m_aColumns
.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
1530 // this will work. of course the model isn't fully consistent with our view right now, but let's
1531 // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
1532 // other case we can use analogue arguments).
1533 // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
1534 // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
1535 // within this range is constant, so we may calculate the view pos from the model pos in the above way.
1537 // for instance, let's look at a grid with six columns where the third one is hidden. this will
1538 // initially look like this :
1540 // +---+---+---+---+---+---+
1541 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1542 // +---+---+---+---+---+---+
1543 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1544 // +---+---+---+---+---+---+
1545 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1546 // +---+---+---+---+---+---+
1548 // if we move the column at (view) pos 1 to (view) pos 3 we have :
1550 // +---+---+---+---+---+---+
1551 // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
1552 // +---+---+---+---+---+---+
1553 // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
1554 // +---+---+---+---+---+---+
1555 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1556 // +---+---+---+---+---+---+
1558 // or, sorted by the out-of-date model positions :
1560 // +---+---+---+---+---+---+
1561 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1562 // +---+---+---+---+---+---+
1563 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1564 // +---+---+---+---+---+---+
1565 // view pos | 0 | 3 | - | 1 | 2 | 4 |
1566 // +---+---+---+---+---+---+
1568 // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
1569 // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
1570 // exactly the pos where we have to re-insert our column's model, so it looks ike this :
1572 // +---+---+---+---+---+---+
1573 // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
1574 // +---+---+---+---+---+---+
1575 // ID | 1 | 3 | 4 | 5 | 2 | 6 |
1576 // +---+---+---+---+---+---+
1577 // view pos | 0 | - | 1 | 2 | 3 | 4 |
1578 // +---+---+---+---+---+---+
1580 // Now, all is consistent again.
1581 // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
1582 // the user expected the latter but there really is no good argument against our method ;) ...)
1584 // And no, this large explanation isn't just because I wanted to play a board game or something like
1585 // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
1586 // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
1588 auto temp
= std::move(m_aColumns
[ nOldModelPos
]);
1589 m_aColumns
.erase( m_aColumns
.begin() + nOldModelPos
);
1590 m_aColumns
.insert( m_aColumns
.begin() + nNewModelPos
, std::move(temp
) );
1593 bool DbGridControl::SeekRow(sal_Int32 nRow
)
1595 // in filter mode or in insert only mode we don't have any cursor!
1596 if ( !SeekCursor( nRow
) )
1599 if ( IsFilterMode() )
1601 DBG_ASSERT( IsFilterRow( nRow
), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1602 m_xPaintRow
= m_xEmptyRow
;
1606 // on the current position we have to take the current row for display as we want
1607 // to have the most recent values for display
1608 if ( ( nRow
== m_nCurrentPos
) && getDisplaySynchron() )
1609 m_xPaintRow
= m_xCurrentRow
;
1610 // seek to the empty insert row
1611 else if ( IsInsertionRow( nRow
) )
1612 m_xPaintRow
= m_xEmptyRow
;
1615 m_xSeekRow
->SetState( m_pSeekCursor
.get(), true );
1616 m_xPaintRow
= m_xSeekRow
;
1620 EditBrowseBox::SeekRow(nRow
);
1622 return m_nSeekPos
>= 0;
1625 // Is called whenever the visible amount of data changes
1626 void DbGridControl::VisibleRowsChanged( sal_Int32 nNewTopRow
, sal_uInt16 nLinesOnScreen
)
1628 RecalcRows(nNewTopRow
, nLinesOnScreen
, false);
1631 void DbGridControl::RecalcRows(sal_Int32 nNewTopRow
, sal_uInt16 nLinesOnScreen
, bool bUpdateCursor
)
1633 // If no cursor -> no rows in the browser.
1636 DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there");
1640 // ignore any implicitly made updates
1641 bool bDisablePaint
= !bUpdateCursor
&& IsPaintEnabled();
1645 // adjust cache to the visible area
1646 Reference
< XPropertySet
> xSet
= m_pSeekCursor
->getPropertySet();
1647 sal_Int32 nCacheSize
= 0;
1648 xSet
->getPropertyValue(FM_PROP_FETCHSIZE
) >>= nCacheSize
;
1649 bool bCacheAligned
= false;
1650 // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
1651 // positioned on the first sentence
1652 tools::Long nDelta
= nNewTopRow
- GetTopRow();
1653 // limit for relative positioning
1654 tools::Long nLimit
= nCacheSize
? nCacheSize
/ 2 : 0;
1656 // more lines on screen than in cache
1657 if (nLimit
< nLinesOnScreen
)
1660 aCacheSize
<<= sal_Int32(nLinesOnScreen
*2);
1661 xSet
->setPropertyValue(FM_PROP_FETCHSIZE
, aCacheSize
);
1662 // here we need to update the cursor for sure
1663 bUpdateCursor
= true;
1664 bCacheAligned
= true;
1665 nLimit
= nLinesOnScreen
;
1668 // In the following, all positionings are done as it is
1669 // ensured that there are enough lines in the data cache
1671 // window goes downwards with less than two windows difference or
1672 // the cache was updated and no rowcount yet
1673 if (nDelta
< nLimit
&& (nDelta
> 0
1674 || (bCacheAligned
&& m_nTotalCount
< 0)) )
1675 SeekCursor(nNewTopRow
+ nLinesOnScreen
- 1);
1676 else if (nDelta
< 0 && std::abs(nDelta
) < nLimit
)
1677 SeekCursor(nNewTopRow
);
1678 else if (nDelta
!= 0 || bUpdateCursor
)
1679 SeekCursor(nNewTopRow
, true);
1683 // ignore any updates implicit made
1687 void DbGridControl::RowInserted(sal_Int32 nRow
, sal_Int32 nNumRows
, bool bDoPaint
)
1692 if (m_bRecordCountFinal
&& m_nTotalCount
< 0)
1694 // if we have an insert row we have to reduce to count by 1
1695 // as the total count reflects only the existing rows in database
1696 m_nTotalCount
= GetRowCount() + nNumRows
;
1697 if (m_xEmptyRow
.is())
1700 else if (m_nTotalCount
>= 0)
1701 m_nTotalCount
+= nNumRows
;
1703 EditBrowseBox::RowInserted(nRow
, nNumRows
, bDoPaint
);
1704 m_aBar
->InvalidateState(DbGridControlNavigationBarState::Count
);
1707 void DbGridControl::RowRemoved(sal_Int32 nRow
, sal_Int32 nNumRows
, bool bDoPaint
)
1712 if (m_bRecordCountFinal
&& m_nTotalCount
< 0)
1714 m_nTotalCount
= GetRowCount() - nNumRows
;
1715 // if we have an insert row reduce by 1
1716 if (m_xEmptyRow
.is())
1719 else if (m_nTotalCount
>= 0)
1720 m_nTotalCount
-= nNumRows
;
1722 EditBrowseBox::RowRemoved(nRow
, nNumRows
, bDoPaint
);
1723 m_aBar
->InvalidateState(DbGridControlNavigationBarState::Count
);
1726 void DbGridControl::AdjustRows()
1731 Reference
< XPropertySet
> xSet
= m_pDataCursor
->getPropertySet();
1733 // refresh RecordCount
1734 sal_Int32 nRecordCount
= 0;
1735 xSet
->getPropertyValue(FM_PROP_ROWCOUNT
) >>= nRecordCount
;
1736 if (!m_bRecordCountFinal
)
1737 m_bRecordCountFinal
= ::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ROWCOUNTFINAL
));
1739 // Did the number of rows change?
1740 // Here we need to consider that there might be an additional row for adding new data sets
1742 // add additional AppendRow for insertion
1743 if (m_nOptions
& DbGridControlOptions::Insert
)
1746 // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
1747 if (!IsUpdating() && m_bRecordCountFinal
&& IsModified() && m_xCurrentRow
!= m_xEmptyRow
&&
1748 m_xCurrentRow
->IsNew())
1750 // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
1751 // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
1752 // and a second time here (60787 - FS)
1754 if (nRecordCount
!= GetRowCount())
1756 tools::Long nDelta
= GetRowCount() - static_cast<tools::Long
>(nRecordCount
);
1757 if (nDelta
> 0) // too many
1759 RowRemoved(GetRowCount() - nDelta
, nDelta
, false);
1760 // some rows are gone, thus, repaint starting at the current position
1763 sal_Int32 nNewPos
= AlignSeekCursor();
1764 if (m_bSynchDisplay
)
1765 EditBrowseBox::GoToRow(nNewPos
);
1767 SetCurrent(nNewPos
);
1768 // there are rows so go to the selected current column
1770 GoToRowColumnId(nNewPos
, GetColumnId(GetCurColumnId()));
1771 if (!IsResizing() && GetRowCount())
1772 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1773 m_aBar
->InvalidateAll(m_nCurrentPos
, true);
1776 RowInserted(GetRowCount(), -nDelta
);
1779 if (m_bRecordCountFinal
&& m_nTotalCount
< 0)
1781 if (m_nOptions
& DbGridControlOptions::Insert
)
1782 m_nTotalCount
= GetRowCount() - 1;
1784 m_nTotalCount
= GetRowCount();
1786 m_aBar
->InvalidateState(DbGridControlNavigationBarState::Count
);
1789 svt::EditBrowseBox::RowStatus
DbGridControl::GetRowStatus(sal_Int32 nRow
) const
1791 if (IsFilterRow(nRow
))
1792 return EditBrowseBox::FILTER
;
1793 else if (m_nCurrentPos
>= 0 && nRow
== m_nCurrentPos
)
1796 if (!IsValid(m_xCurrentRow
))
1797 return EditBrowseBox::DELETED
;
1798 else if (IsModified())
1799 return EditBrowseBox::MODIFIED
;
1800 else if (m_xCurrentRow
->IsNew())
1801 return EditBrowseBox::CURRENTNEW
;
1803 return EditBrowseBox::CURRENT
;
1805 else if (IsInsertionRow(nRow
))
1806 return EditBrowseBox::NEW
;
1807 else if (!IsValid(m_xSeekRow
))
1808 return EditBrowseBox::DELETED
;
1810 return EditBrowseBox::CLEAN
;
1813 void DbGridControl::PaintCell(OutputDevice
& rDev
, const tools::Rectangle
& rRect
, sal_uInt16 nColumnId
) const
1815 if (!IsValid(m_xPaintRow
))
1818 size_t Location
= GetModelColumnPos(nColumnId
);
1819 DbGridColumn
* pColumn
= (Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
1822 tools::Rectangle
aArea(rRect
);
1823 if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS
) == BrowserMode::CURSOR_WO_FOCUS
)
1825 aArea
.AdjustTop(1 );
1826 aArea
.AdjustBottom( -1 );
1828 pColumn
->Paint(rDev
, aArea
, m_xPaintRow
.get(), getNumberFormatter());
1832 bool DbGridControl::CursorMoving(sal_Int32 nNewRow
, sal_uInt16 nNewCol
)
1835 DeactivateCell( false );
1838 && ( m_nCurrentPos
!= nNewRow
)
1839 && !SetCurrent( nNewRow
)
1846 return EditBrowseBox::CursorMoving( nNewRow
, nNewCol
);
1849 bool DbGridControl::SetCurrent(sal_Int32 nNewRow
)
1851 // Each movement of the datacursor must start with BeginCursorAction and end with
1852 // EndCursorAction to block all notifications during the movement
1853 BeginCursorAction();
1857 // compare positions
1858 if (SeekCursor(nNewRow
))
1860 if (IsFilterRow(nNewRow
)) // special mode for filtering
1862 m_xCurrentRow
= m_xDataRow
= m_xPaintRow
= m_xEmptyRow
;
1863 m_nCurrentPos
= nNewRow
;
1867 bool bNewRowInserted
= false;
1868 // Should we go to the insertrow ?
1869 if (IsInsertionRow(nNewRow
))
1871 // to we need to move the cursor to the insert row?
1872 // we need to insert the if the current row isn't the insert row or if the
1873 // cursor triggered the move by itself and we need a reinitialization of the row
1874 Reference
< XPropertySet
> xCursorProps
= m_pDataCursor
->getPropertySet();
1875 if ( !::comphelper::getBOOL(xCursorProps
->getPropertyValue(FM_PROP_ISNEW
)) )
1877 Reference
< XResultSetUpdate
> xUpdateCursor(Reference
< XInterface
>(*m_pDataCursor
), UNO_QUERY
);
1878 xUpdateCursor
->moveToInsertRow();
1880 bNewRowInserted
= true;
1885 if ( !m_pSeekCursor
->isBeforeFirst() && !m_pSeekCursor
->isAfterLast() )
1887 Any aBookmark
= m_pSeekCursor
->getBookmark();
1888 if (!m_xCurrentRow
.is() || m_xCurrentRow
->IsNew() || !CompareBookmark(aBookmark
, m_pDataCursor
->getBookmark()))
1890 // adjust the cursor to the new desired row
1891 if (!m_pDataCursor
->moveToBookmark(aBookmark
))
1899 m_xDataRow
->SetState(m_pDataCursor
.get(), false);
1900 m_xCurrentRow
= m_xDataRow
;
1902 tools::Long nPaintPos
= -1;
1903 // do we have to repaint the last regular row in case of setting defaults or autovalues
1904 if (m_nCurrentPos
>= 0 && m_nCurrentPos
>= (GetRowCount() - 2))
1905 nPaintPos
= m_nCurrentPos
;
1907 m_nCurrentPos
= nNewRow
;
1909 // repaint the new row to display all defaults
1910 if (bNewRowInserted
)
1911 RowModified(m_nCurrentPos
);
1913 RowModified(nPaintPos
);
1918 OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
1923 catch ( const Exception
& )
1925 DBG_UNHANDLED_EXCEPTION("svx");
1934 void DbGridControl::CursorMoved()
1937 // cursor movement due to deletion or insertion of rows
1938 if (m_pDataCursor
&& m_nCurrentPos
!= GetCurRow())
1941 SetCurrent(GetCurRow());
1944 EditBrowseBox::CursorMoved();
1945 m_aBar
->InvalidateAll(m_nCurrentPos
);
1947 // select the new column when they moved
1948 if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
1950 SelectColumnId( GetCurColumnId() );
1953 if ( m_nLastColId
!= GetCurColumnId() )
1955 m_nLastColId
= GetCurColumnId();
1957 if ( m_nLastRowId
!= GetCurRow() )
1959 m_nLastRowId
= GetCurRow();
1962 void DbGridControl::onRowChange()
1964 // not interested in
1967 void DbGridControl::onColumnChange()
1969 if ( m_pGridListener
)
1970 m_pGridListener
->columnChanged();
1973 void DbGridControl::setDisplaySynchron(bool bSync
)
1975 if (bSync
!= m_bSynchDisplay
)
1977 m_bSynchDisplay
= bSync
;
1978 if (m_bSynchDisplay
)
1983 void DbGridControl::AdjustDataSource(bool bFull
)
1985 SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
1986 SolarMutexGuard aGuard
;
1987 // If the current row is recalculated at the moment, do not adjust
1990 m_xCurrentRow
= nullptr;
1991 // if we are on the same row only repaint
1992 // but this is only possible for rows which are not inserted, in that case the comparison result
1993 // may not be correct
1995 if ( m_xCurrentRow
.is()
1996 && !m_xCurrentRow
->IsNew()
1997 && !m_pDataCursor
->isBeforeFirst()
1998 && !m_pDataCursor
->isAfterLast()
1999 && !m_pDataCursor
->rowDeleted()
2002 bool bEqualBookmarks
= CompareBookmark( m_xCurrentRow
->GetBookmark(), m_pDataCursor
->getBookmark() );
2004 bool bDataCursorIsOnNew
= false;
2005 m_pDataCursor
->getPropertySet()->getPropertyValue( FM_PROP_ISNEW
) >>= bDataCursorIsOnNew
;
2007 if ( bEqualBookmarks
&& !bDataCursorIsOnNew
)
2009 // position of my data cursor is the same as the position our current row points tpo
2010 // sync the status, repaint, done
2011 DBG_ASSERT(m_xDataRow
== m_xCurrentRow
, "Errors in the data row");
2012 SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow
));
2013 RowModified(m_nCurrentPos
);
2018 // away from the data cursor's row
2019 if (m_xPaintRow
== m_xCurrentRow
)
2020 m_xPaintRow
= m_xSeekRow
;
2022 // not up-to-date row, thus, adjust completely
2023 if (!m_xCurrentRow
.is())
2026 sal_Int32 nNewPos
= AlignSeekCursor();
2027 if (nNewPos
< 0)// could not find any position
2030 if (nNewPos
!= m_nCurrentPos
)
2032 if (m_bSynchDisplay
)
2033 EditBrowseBox::GoToRow(nNewPos
);
2035 if (!m_xCurrentRow
.is())
2036 // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
2037 // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
2038 // CurrentRow is corrected to point two rows down, so that GoToRow will point into
2039 // emptiness (since we are - purportedly - at the correct position)
2040 SetCurrent(nNewPos
);
2044 SetCurrent(nNewPos
);
2045 RowModified(nNewPos
);
2048 // if the data cursor was moved from outside, this section is voided
2050 m_aBar
->InvalidateAll(m_nCurrentPos
, m_xCurrentRow
.is());
2053 sal_Int32
DbGridControl::AlignSeekCursor()
2055 // position SeekCursor onto the data cursor, no data transmission
2060 Reference
< XPropertySet
> xSet
= m_pDataCursor
->getPropertySet();
2062 // now align the seek cursor and the data cursor
2063 if (::comphelper::getBOOL(xSet
->getPropertyValue(FM_PROP_ISNEW
)))
2064 m_nSeekPos
= GetRowCount() - 1;
2069 if ( m_pDataCursor
->isBeforeFirst() )
2071 // this is somewhat strange, but can nevertheless happen
2072 SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
2073 m_pSeekCursor
->first();
2074 m_pSeekCursor
->previous();
2077 else if ( m_pDataCursor
->isAfterLast() )
2079 SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2080 m_pSeekCursor
->last();
2081 m_pSeekCursor
->next();
2086 m_pSeekCursor
->moveToBookmark(m_pDataCursor
->getBookmark());
2087 if (!CompareBookmark(m_pDataCursor
->getBookmark(), m_pSeekCursor
->getBookmark()))
2088 // unfortunately, moveToBookmark might lead to a re-positioning of the seek
2089 // cursor (if the complex moveToBookmark with all its events fires an update
2090 // somewhere) -> retry
2091 m_pSeekCursor
->moveToBookmark(m_pDataCursor
->getBookmark());
2092 // Now there is still the chance of a failure but it is less likely.
2093 // The alternative would be a loop until everything is fine - no good solution...
2094 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2104 bool DbGridControl::SeekCursor(sal_Int32 nRow
, bool bAbsolute
)
2106 // position SeekCursor onto the data cursor, no data transmission
2108 // additions for the filtermode
2109 if (IsFilterRow(nRow
))
2118 // is this an insertion?
2119 if (IsValid(m_xCurrentRow
) && m_xCurrentRow
->IsNew() &&
2120 nRow
>= m_nCurrentPos
)
2122 // if so, scrolling down must be prevented as this is already the last data set!
2123 if (nRow
== m_nCurrentPos
)
2125 // no adjustment necessary
2128 else if (IsInsertionRow(nRow
)) // blank row for data insertion
2131 else if (IsInsertionRow(nRow
)) // blank row for data insertion
2133 else if ((-1 == nRow
) && (GetRowCount() == ((m_nOptions
& DbGridControlOptions::Insert
) ? 1 : 0)) && m_pSeekCursor
->isAfterLast())
2137 bool bSuccess
= false;
2138 tools::Long nSteps
= 0;
2141 if ( m_pSeekCursor
->rowDeleted() )
2143 // somebody deleted the current row of the seek cursor. Move it away from this row.
2144 m_pSeekCursor
->next();
2145 if ( m_pSeekCursor
->isAfterLast() || m_pSeekCursor
->isBeforeFirst() )
2151 DBG_ASSERT( !m_pSeekCursor
->isAfterLast() && !m_pSeekCursor
->isBeforeFirst(),
2152 "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2153 nSteps
= nRow
- (m_pSeekCursor
->getRow() - 1);
2154 bAbsolute
= std::abs(nSteps
) > 100;
2159 bSuccess
= m_pSeekCursor
->absolute(nRow
+ 1);
2165 if (nSteps
> 0) // position onto the last needed data set
2167 if (m_pSeekCursor
->isAfterLast())
2169 else if (m_pSeekCursor
->isBeforeFirst())
2170 bSuccess
= m_pSeekCursor
->absolute(nSteps
);
2172 bSuccess
= m_pSeekCursor
->relative(nSteps
);
2174 else if (nSteps
< 0)
2176 if (m_pSeekCursor
->isBeforeFirst())
2178 else if (m_pSeekCursor
->isAfterLast())
2179 bSuccess
= m_pSeekCursor
->absolute(nSteps
);
2181 bSuccess
= m_pSeekCursor
->relative(nSteps
);
2192 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2199 if (bAbsolute
|| nSteps
> 0)
2201 if (m_pSeekCursor
->isLast())
2204 bSuccess
= m_pSeekCursor
->last();
2208 if (m_pSeekCursor
->isFirst())
2211 bSuccess
= m_pSeekCursor
->first();
2216 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2222 DBG_UNHANDLED_EXCEPTION("svx");
2223 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2224 m_nSeekPos
= -1; // no further data set available
2227 return m_nSeekPos
== nRow
;
2230 void DbGridControl::MoveToFirst()
2232 if (m_pSeekCursor
&& (GetCurRow() != 0))
2236 void DbGridControl::MoveToLast()
2241 if (m_nTotalCount
< 0) // no RecordCount, yet
2245 bool bRes
= m_pSeekCursor
->last();
2249 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2258 // position onto the last data set not on a blank row
2259 if (m_nOptions
& DbGridControlOptions::Insert
)
2261 if ((GetRowCount() - 1) > 0)
2262 MoveToPosition(GetRowCount() - 2);
2264 else if (GetRowCount())
2265 MoveToPosition(GetRowCount() - 1);
2268 void DbGridControl::MoveToPrev()
2270 sal_Int32 nNewRow
= std::max(GetCurRow() - 1, sal_Int32(0));
2271 if (GetCurRow() != nNewRow
)
2272 MoveToPosition(nNewRow
);
2275 void DbGridControl::MoveToNext()
2280 if (m_nTotalCount
> 0)
2282 // move the data cursor to the right position
2283 tools::Long nNewRow
= std::min(GetRowCount() - 1, GetCurRow() + 1);
2284 if (GetCurRow() != nNewRow
)
2285 MoveToPosition(nNewRow
);
2292 // try to move to next row
2293 // when not possible our paint cursor is already on the last row
2294 // then we must be sure that the data cursor is on the position
2295 // we call ourself again
2296 bOk
= m_pSeekCursor
->next();
2299 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2300 MoveToPosition(GetCurRow() + 1);
2303 catch(SQLException
&)
2305 DBG_UNHANDLED_EXCEPTION("svx");
2311 if (m_nTotalCount
> 0) // only to avoid infinite recursion
2317 void DbGridControl::MoveToPosition(sal_uInt32 nPos
)
2322 if (m_nTotalCount
< 0 && static_cast<tools::Long
>(nPos
) >= GetRowCount())
2326 if (!m_pSeekCursor
->absolute(nPos
+ 1))
2333 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2342 EditBrowseBox::GoToRow(nPos
);
2343 m_aBar
->InvalidateAll(m_nCurrentPos
);
2346 void DbGridControl::AppendNew()
2348 if (!m_pSeekCursor
|| !(m_nOptions
& DbGridControlOptions::Insert
))
2351 if (m_nTotalCount
< 0) // no RecordCount, yet
2355 bool bRes
= m_pSeekCursor
->last();
2359 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2369 tools::Long nNewRow
= m_nTotalCount
+ 1;
2370 if (nNewRow
> 0 && GetCurRow() != nNewRow
)
2371 MoveToPosition(nNewRow
- 1);
2374 void DbGridControl::SetDesignMode(bool bMode
)
2376 if (IsDesignMode() == bMode
)
2379 // adjust Enable/Disable for design mode so that the headerbar remains configurable
2385 GetDataWindow().Disable();
2390 // disable completely
2391 if (!GetDataWindow().IsEnabled())
2395 m_bDesignMode
= bMode
;
2396 GetDataWindow().SetMouseTransparent(bMode
);
2397 SetMouseTransparent(bMode
);
2399 m_aBar
->InvalidateAll(m_nCurrentPos
, true);
2402 void DbGridControl::SetFilterMode(bool bMode
)
2404 if (IsFilterMode() == bMode
)
2407 m_bFilterMode
= bMode
;
2411 SetUpdateMode(false);
2413 // there is no cursor anymore
2418 m_xEmptyRow
= new DbGridRow();
2420 // setting the new filter controls
2421 for (auto const & pCurCol
: m_aColumns
)
2423 if (!pCurCol
->IsHidden())
2424 pCurCol
->UpdateControl();
2427 // one row for filtering
2429 SetUpdateMode(true);
2432 setDataSource(Reference
< XRowSet
> ());
2435 OUString
DbGridControl::GetCellText(sal_Int32 _nRow
, sal_uInt16 _nColId
) const
2437 size_t Location
= GetModelColumnPos( _nColId
);
2438 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
2440 if ( const_cast<DbGridControl
*>(this)->SeekRow(_nRow
) )
2441 sRet
= GetCurrentRowCellText(pColumn
, m_xPaintRow
);
2445 OUString
DbGridControl::GetCurrentRowCellText(DbGridColumn
const * pColumn
,const DbGridRowRef
& _rRow
) const
2447 // text output for a single row
2449 if ( pColumn
&& IsValid(_rRow
) )
2450 aText
= pColumn
->GetCellText(_rRow
.get(), m_xFormatter
);
2454 sal_uInt32
DbGridControl::GetTotalCellWidth(sal_Int32 nRow
, sal_uInt16 nColId
)
2458 size_t Location
= GetModelColumnPos( nColId
);
2459 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
2460 return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn
,m_xPaintRow
));
2463 return 30; // FIXME magic number for default cell width
2466 void DbGridControl::PreExecuteRowContextMenu(sal_uInt16
/*nRow*/, PopupMenu
& rMenu
)
2468 bool bDelete
= (m_nOptions
& DbGridControlOptions::Delete
) && GetSelectRowCount() && !IsCurrentAppending();
2469 // if only a blank row is selected then do not delete
2470 bDelete
= bDelete
&& !((m_nOptions
& DbGridControlOptions::Insert
) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2472 rMenu
.EnableItem(rMenu
.GetItemId("delete"), bDelete
);
2473 rMenu
.EnableItem(rMenu
.GetItemId("save"), IsModified());
2475 // the undo is more difficult
2476 bool bCanUndo
= IsModified();
2478 if (m_aMasterStateProvider
.IsSet())
2479 nState
= m_aMasterStateProvider
.Call(DbGridControlNavigationBarState::Undo
);
2480 bCanUndo
&= ( 0 != nState
);
2482 rMenu
.EnableItem(rMenu
.GetItemId("undo"), bCanUndo
);
2485 void DbGridControl::PostExecuteRowContextMenu(sal_uInt16
/*nRow*/, const PopupMenu
& rMenu
, sal_uInt16 nExecutionResult
)
2487 if (nExecutionResult
== rMenu
.GetItemId("delete"))
2489 // delete asynchronously
2491 Application::RemoveUserEvent(m_nDeleteEvent
);
2492 m_nDeleteEvent
= Application::PostUserEvent(LINK(this,DbGridControl
,OnDelete
), nullptr, true);
2494 else if (nExecutionResult
== rMenu
.GetItemId("undo"))
2496 else if (nExecutionResult
== rMenu
.GetItemId("save"))
2500 void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent
& evt
)
2502 SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
2503 SolarMutexGuard aGuard
;
2504 // prop "IsModified" changed ?
2505 // during update don't care about the modified state
2506 if (IsUpdating() || evt
.PropertyName
!= FM_PROP_ISMODIFIED
)
2509 Reference
< XPropertySet
> xSource(evt
.Source
, UNO_QUERY
);
2510 DBG_ASSERT( xSource
.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2511 bool bIsNew
= false;
2513 bIsNew
= ::comphelper::getBOOL(xSource
->getPropertyValue(FM_PROP_ISNEW
));
2515 if (bIsNew
&& m_xCurrentRow
.is())
2517 DBG_ASSERT(::comphelper::getBOOL(xSource
->getPropertyValue(FM_PROP_ROWCOUNTFINAL
)), "DbGridControl::DataSourcePropertyChanged : somebody moved the form to a new record before the row count was final !");
2518 sal_Int32 nRecordCount
= 0;
2519 xSource
->getPropertyValue(FM_PROP_ROWCOUNT
) >>= nRecordCount
;
2520 if (::comphelper::getBOOL(evt
.NewValue
))
2521 { // modified state changed from sal_False to sal_True and we're on an insert row
2522 // -> we've to add a new grid row
2523 if ((nRecordCount
== GetRowCount() - 1) && m_xCurrentRow
->IsNew())
2525 RowInserted(GetRowCount());
2526 InvalidateStatusCell(m_nCurrentPos
);
2527 m_aBar
->InvalidateAll(m_nCurrentPos
);
2531 { // modified state changed from sal_True to sal_False and we're on an insert row
2532 // we have two "new row"s at the moment : the one we're editing currently (where the current
2533 // column is the only dirty element) and a "new new" row which is completely clean. As the first
2534 // one is about to be cleaned, too, the second one is obsolete now.
2535 if (m_xCurrentRow
->IsNew() && nRecordCount
== (GetRowCount() - 2))
2537 RowRemoved(GetRowCount() - 1);
2538 InvalidateStatusCell(m_nCurrentPos
);
2539 m_aBar
->InvalidateAll(m_nCurrentPos
);
2543 if (m_xCurrentRow
.is())
2545 m_xCurrentRow
->SetStatus(::comphelper::getBOOL(evt
.NewValue
) ? GridRowStatus::Modified
: GridRowStatus::Clean
);
2546 m_xCurrentRow
->SetNew( bIsNew
);
2547 InvalidateStatusCell(m_nCurrentPos
);
2548 SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow
));
2552 void DbGridControl::StartDrag( sal_Int8
/*nAction*/, const Point
& rPosPixel
)
2554 if (!m_pSeekCursor
|| IsResizing())
2557 sal_uInt16 nColId
= GetColumnId(GetColumnAtXPosPixel(rPosPixel
.X()));
2558 tools::Long nRow
= GetRowAtYPosPixel(rPosPixel
.Y());
2559 if (nColId
!= HandleColumnId
&& nRow
>= 0)
2561 if (GetDataWindow().IsMouseCaptured())
2562 GetDataWindow().ReleaseMouse();
2564 size_t Location
= GetModelColumnPos( nColId
);
2565 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
2566 rtl::Reference
<OStringTransferable
> pTransferable
= new OStringTransferable(GetCurrentRowCellText(pColumn
,m_xPaintRow
));
2567 pTransferable
->StartDrag(this, DND_ACTION_COPY
);
2571 bool DbGridControl::canCopyCellText(sal_Int32 _nRow
, sal_uInt16 _nColId
)
2574 && (_nRow
< GetRowCount())
2575 && (_nColId
!= HandleColumnId
)
2576 && (GetModelColumnPos(_nColId
) != GRID_COLUMN_NOT_FOUND
);
2579 void DbGridControl::copyCellText(sal_Int32 _nRow
, sal_uInt16 _nColId
)
2581 DBG_ASSERT(canCopyCellText(_nRow
, _nColId
), "DbGridControl::copyCellText: invalid call!");
2582 DbGridColumn
* pColumn
= m_aColumns
[ GetModelColumnPos(_nColId
) ].get();
2584 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn
,m_xPaintRow
), this );
2587 void DbGridControl::executeRowContextMenu( sal_Int32 _nRow
, const Point
& _rPreferredPos
)
2589 VclBuilder
aBuilder(nullptr, AllSettings::GetUIRootDir(), "svx/ui/rowsmenu.ui", "");
2590 VclPtr
<PopupMenu
> aContextMenu(aBuilder
.get_menu("menu"));
2592 PreExecuteRowContextMenu( static_cast<sal_uInt16
>(_nRow
), *aContextMenu
);
2593 aContextMenu
->RemoveDisabledEntries( true, true );
2594 PostExecuteRowContextMenu( static_cast<sal_uInt16
>(_nRow
), *aContextMenu
, aContextMenu
->Execute( this, _rPreferredPos
) );
2597 void DbGridControl::Command(const CommandEvent
& rEvt
)
2599 switch (rEvt
.GetCommand())
2601 case CommandEventId::ContextMenu
:
2603 if ( !m_pSeekCursor
)
2605 EditBrowseBox::Command(rEvt
);
2609 if ( !rEvt
.IsMouseEvent() )
2610 { // context menu requested by keyboard
2611 if ( GetSelectRowCount() )
2613 tools::Long nRow
= FirstSelectedRow( );
2615 ::tools::Rectangle
aRowRect( GetRowRectPixel( nRow
) );
2616 executeRowContextMenu( nRow
, aRowRect
.LeftCenter() );
2623 sal_uInt16 nColId
= GetColumnId(GetColumnAtXPosPixel(rEvt
.GetMousePosPixel().X()));
2624 tools::Long nRow
= GetRowAtYPosPixel(rEvt
.GetMousePosPixel().Y());
2626 if (nColId
== HandleColumnId
)
2628 executeRowContextMenu( nRow
, rEvt
.GetMousePosPixel() );
2630 else if (canCopyCellText(nRow
, nColId
))
2632 VclBuilder
aBuilder(nullptr, AllSettings::GetUIRootDir(), "svx/ui/cellmenu.ui", "");
2633 VclPtr
<PopupMenu
> aContextMenu(aBuilder
.get_menu("menu"));
2634 if (aContextMenu
->Execute(this, rEvt
.GetMousePosPixel()))
2635 copyCellText(nRow
, nColId
);
2639 EditBrowseBox::Command(rEvt
);
2646 EditBrowseBox::Command(rEvt
);
2650 IMPL_LINK_NOARG(DbGridControl
, OnDelete
, void*, void)
2652 m_nDeleteEvent
= nullptr;
2653 DeleteSelectedRows();
2656 void DbGridControl::DeleteSelectedRows()
2658 DBG_ASSERT(GetSelection(), "no selection!!!");
2664 CellController
* DbGridControl::GetController(sal_Int32
/*nRow*/, sal_uInt16 nColumnId
)
2666 if (!IsValid(m_xCurrentRow
) || !IsEnabled())
2669 size_t Location
= GetModelColumnPos(nColumnId
);
2670 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
2674 CellController
* pReturn
= nullptr;
2676 pReturn
= pColumn
->GetController().get();
2679 if (::comphelper::hasProperty(FM_PROP_ENABLED
, pColumn
->getModel()))
2681 if (!::comphelper::getBOOL(pColumn
->getModel()->getPropertyValue(FM_PROP_ENABLED
)))
2685 bool bInsert
= (m_xCurrentRow
->IsNew() && (m_nOptions
& DbGridControlOptions::Insert
));
2686 bool bUpdate
= (!m_xCurrentRow
->IsNew() && (m_nOptions
& DbGridControlOptions::Update
));
2688 if ((bInsert
&& !pColumn
->IsAutoValue()) || bUpdate
)
2690 pReturn
= pColumn
->GetController().get();
2696 void DbGridControl::CellModified()
2698 SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
2701 ::osl::MutexGuard
aGuard(m_aAdjustSafety
);
2702 if (m_nAsynAdjustEvent
)
2704 SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows
? "AdjustRows" : "AdustDataSource"));
2705 RemoveUserEvent(m_nAsynAdjustEvent
);
2706 m_nAsynAdjustEvent
= nullptr;
2708 // force the call : this should be no problem as we're probably running in the solar thread here
2709 // (cell modified is triggered by user actions)
2710 if (m_bPendingAdjustRows
)
2717 if (IsFilterMode() || !IsValid(m_xCurrentRow
) || m_xCurrentRow
->IsModified())
2721 // a data set should be inserted
2722 if (m_xCurrentRow
->IsNew())
2724 m_xCurrentRow
->SetStatus(GridRowStatus::Modified
);
2725 SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
2726 // if no row was added yet, do it now
2727 if (m_nCurrentPos
== GetRowCount() - 1)
2729 // increment RowCount
2730 RowInserted(GetRowCount());
2731 InvalidateStatusCell(m_nCurrentPos
);
2732 m_aBar
->InvalidateAll(m_nCurrentPos
);
2735 else if (m_xCurrentRow
->GetStatus() != GridRowStatus::Modified
)
2737 m_xCurrentRow
->SetState(m_pDataCursor
.get(), false);
2738 SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow
));
2739 m_xCurrentRow
->SetStatus(GridRowStatus::Modified
);
2740 SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
2741 InvalidateStatusCell(m_nCurrentPos
);
2745 void DbGridControl::Dispatch(sal_uInt16 nId
)
2747 if (nId
== BROWSER_CURSORENDOFFILE
)
2749 if (m_nOptions
& DbGridControlOptions::Insert
)
2755 EditBrowseBox::Dispatch(nId
);
2758 void DbGridControl::Undo()
2760 if (IsFilterMode() || !IsValid(m_xCurrentRow
) || !IsModified())
2763 // check if we have somebody doin' the UNDO for us
2765 if (m_aMasterStateProvider
.IsSet())
2766 nState
= m_aMasterStateProvider
.Call(DbGridControlNavigationBarState::Undo
);
2768 { // yes, we have, and the slot is enabled
2769 DBG_ASSERT(m_aMasterSlotExecutor
.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
2770 bool lResult
= m_aMasterSlotExecutor
.Call(DbGridControlNavigationBarState::Undo
);
2775 else if (nState
== 0)
2776 // yes, we have, and the slot is disabled
2779 BeginCursorAction();
2781 bool bAppending
= m_xCurrentRow
->IsNew();
2782 bool bDirty
= m_xCurrentRow
->IsModified();
2787 Reference
< XResultSetUpdate
> xUpdateCursor(Reference
< XInterface
>(*m_pDataCursor
), UNO_QUERY
);
2788 // no effects if we're not updating currently
2790 // just refresh the row
2791 xUpdateCursor
->moveToInsertRow();
2793 xUpdateCursor
->cancelRowUpdates();
2798 DBG_UNHANDLED_EXCEPTION("svx");
2803 m_xDataRow
->SetState(m_pDataCursor
.get(), false);
2804 if (m_xPaintRow
== m_xCurrentRow
)
2805 m_xPaintRow
= m_xCurrentRow
= m_xDataRow
;
2807 m_xCurrentRow
= m_xDataRow
;
2809 if (bAppending
&& (EditBrowseBox::IsModified() || bDirty
))
2811 if (m_nCurrentPos
== GetRowCount() - 2)
2812 { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
2813 // caused our data source form to be reset - which should be the usual case...)
2814 RowRemoved(GetRowCount() - 1);
2815 m_aBar
->InvalidateAll(m_nCurrentPos
);
2818 RowModified(m_nCurrentPos
);
2821 void DbGridControl::resetCurrentRow()
2825 // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
2826 // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
2827 // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
2828 // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
2829 // would never delete the obsolete "second insert row". Thus in this special case this method here
2830 // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
2831 // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
2832 Reference
< XPropertySet
> xDataSource
= getDataSource()->getPropertySet();
2833 if (xDataSource
.is() && !::comphelper::getBOOL(xDataSource
->getPropertyValue(FM_PROP_ISMODIFIED
)))
2835 // are we on a new row currently ?
2836 if (m_xCurrentRow
->IsNew())
2838 if (m_nCurrentPos
== GetRowCount() - 2)
2840 RowRemoved(GetRowCount() - 1);
2841 m_aBar
->InvalidateAll(m_nCurrentPos
);
2847 m_xDataRow
->SetState(m_pDataCursor
.get(), false);
2848 if (m_xPaintRow
== m_xCurrentRow
)
2849 m_xPaintRow
= m_xCurrentRow
= m_xDataRow
;
2851 m_xCurrentRow
= m_xDataRow
;
2854 RowModified(GetCurRow()); // will update the current controller if affected
2857 void DbGridControl::RowModified( sal_Int32 nRow
)
2859 if (nRow
== m_nCurrentPos
&& IsEditing())
2861 CellControllerRef aTmpRef
= Controller();
2862 aTmpRef
->SaveValue();
2863 InitController(aTmpRef
, m_nCurrentPos
, GetCurColumnId());
2865 EditBrowseBox::RowModified(nRow
);
2868 bool DbGridControl::IsModified() const
2870 return !IsFilterMode() && IsValid(m_xCurrentRow
) && (m_xCurrentRow
->IsModified() || EditBrowseBox::IsModified());
2873 bool DbGridControl::IsCurrentAppending() const
2875 return m_xCurrentRow
.is() && m_xCurrentRow
->IsNew();
2878 bool DbGridControl::IsInsertionRow(sal_Int32 nRow
) const
2880 return (m_nOptions
& DbGridControlOptions::Insert
) && m_nTotalCount
>= 0 && (nRow
== GetRowCount() - 1);
2883 bool DbGridControl::SaveModified()
2885 SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
2886 DBG_ASSERT(IsValid(m_xCurrentRow
), "GridControl:: Invalid row");
2887 if (!IsValid(m_xCurrentRow
))
2890 // accept input for this field
2891 // Where there changes at the current input field?
2892 if (!EditBrowseBox::IsModified())
2895 size_t Location
= GetModelColumnPos( GetCurColumnId() );
2896 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
2897 bool bOK
= pColumn
&& pColumn
->Commit();
2898 DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
2899 if ( !Controller().is() )
2900 // this might happen if the callbacks implicitly triggered by Commit
2901 // fiddled with the form or the control ...
2902 // (Note that this here is a workaround, at most. We need a general concept how
2903 // to treat this, you can imagine an arbitrary number of scenarios where a callback
2904 // triggers something which leaves us in an expected state.)
2905 // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
2910 Controller()->SaveValue();
2912 if ( IsValid(m_xCurrentRow
) )
2914 m_xCurrentRow
->SetState(m_pDataCursor
.get(), false);
2915 SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow
));
2916 InvalidateStatusCell( m_nCurrentPos
);
2920 SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow
));
2927 bool DbGridControl::SaveRow()
2929 SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
2931 if (!IsValid(m_xCurrentRow
) || !IsModified())
2933 // value of the controller was not saved, yet
2934 else if (Controller().is() && Controller()->IsValueChangedFromSaved())
2936 if (!SaveModified())
2941 BeginCursorAction();
2942 bool bAppending
= m_xCurrentRow
->IsNew();
2943 bool bSuccess
= false;
2946 Reference
< XResultSetUpdate
> xUpdateCursor(Reference
< XInterface
>(*m_pDataCursor
), UNO_QUERY
);
2948 xUpdateCursor
->insertRow();
2950 xUpdateCursor
->updateRow();
2953 catch(SQLException
&)
2956 m_bUpdating
= false;
2964 // if we are appending we still sit on the insert row
2965 // we don't move just clear the flags not to move on the current row
2966 m_xCurrentRow
->SetState(m_pDataCursor
.get(), false);
2967 SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow
));
2968 m_xCurrentRow
->SetNew(false);
2970 // adjust the seekcursor if it is on the same position as the datacursor
2971 if (m_nSeekPos
== m_nCurrentPos
|| bAppending
)
2973 // get the bookmark to refetch the data
2974 // in insert mode we take the new bookmark of the data cursor
2975 Any aBookmark
= bAppending
? m_pDataCursor
->getBookmark() : m_pSeekCursor
->getBookmark();
2976 m_pSeekCursor
->moveToBookmark(aBookmark
);
2978 m_xSeekRow
->SetState(m_pSeekCursor
.get(), true);
2979 m_nSeekPos
= m_pSeekCursor
->getRow() - 1;
2982 // and repaint the row
2983 RowModified(m_nCurrentPos
);
2989 m_bUpdating
= false;
2992 // The old code returned (nRecords != 0) here.
2993 // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
2994 // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
2995 // is zero, this simply means all fields had their original values.
2996 // FS - 06.12.99 - 70502
3000 bool DbGridControl::PreNotify(NotifyEvent
& rEvt
)
3002 // do not handle events of the Navbar
3003 if (m_aBar
->IsWindowOrChild(rEvt
.GetWindow()))
3004 return BrowseBox::PreNotify(rEvt
);
3006 switch (rEvt
.GetType())
3008 case MouseNotifyEvent::KEYINPUT
:
3010 const KeyEvent
* pKeyEvent
= rEvt
.GetKeyEvent();
3012 sal_uInt16 nCode
= pKeyEvent
->GetKeyCode().GetCode();
3013 bool bShift
= pKeyEvent
->GetKeyCode().IsShift();
3014 bool bCtrl
= pKeyEvent
->GetKeyCode().IsMod1();
3015 bool bAlt
= pKeyEvent
->GetKeyCode().IsMod2();
3016 if ( ( KEY_TAB
== nCode
) && bCtrl
&& !bAlt
)
3018 // Ctrl-Tab is used to step out of the control, without traveling to the
3019 // remaining cells first
3020 // -> build a new key event without the Ctrl-key, and let the very base class handle it
3021 vcl::KeyCode
aNewCode( KEY_TAB
, bShift
, false, false, false );
3022 KeyEvent
aNewEvent( pKeyEvent
->GetCharCode(), aNewCode
);
3024 // call the Control - our direct base class will interpret this in a way we do not want (and do
3025 // a cell traveling)
3026 Control::KeyInput( aNewEvent
);
3030 if ( !bShift
&& !bCtrl
&& ( KEY_ESCAPE
== nCode
) )
3038 else if ( ( KEY_DELETE
== nCode
) && !bShift
&& !bCtrl
) // delete rows
3040 if ((m_nOptions
& DbGridControlOptions::Delete
) && GetSelectRowCount())
3042 // delete asynchronously
3044 Application::RemoveUserEvent(m_nDeleteEvent
);
3045 m_nDeleteEvent
= Application::PostUserEvent(LINK(this,DbGridControl
,OnDelete
), nullptr, true);
3053 return EditBrowseBox::PreNotify(rEvt
);
3057 bool DbGridControl::IsTabAllowed(bool bRight
) const
3060 // Tab only if not on the _last_ row
3061 return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal
||
3062 GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3065 // Tab only if not on the _first_ row
3066 return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3070 void DbGridControl::KeyInput( const KeyEvent
& rEvt
)
3072 if (rEvt
.GetKeyCode().GetFunction() == KeyFuncType::COPY
)
3074 tools::Long nRow
= GetCurRow();
3075 sal_uInt16 nColId
= GetCurColumnId();
3076 if (nRow
>= 0 && nRow
< GetRowCount() && nColId
< ColCount())
3078 size_t Location
= GetModelColumnPos( nColId
);
3079 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
3080 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn
, m_xCurrentRow
), this );
3084 EditBrowseBox::KeyInput(rEvt
);
3087 void DbGridControl::HideColumn(sal_uInt16 nId
)
3091 // determine the col for the focus to set to after removal
3092 sal_uInt16 nPos
= GetViewColumnPos(nId
);
3093 sal_uInt16 nNewColId
= nPos
== (ColCount()-1)
3094 ? GetColumnIdFromViewPos(nPos
-1) // last col is to be removed -> take the previous
3095 : GetColumnIdFromViewPos(nPos
+1); // take the next
3097 tools::Long lCurrentWidth
= GetColumnWidth(nId
);
3098 EditBrowseBox::RemoveColumn(nId
);
3099 // don't use my own RemoveColumn, this would remove it from m_aColumns, too
3102 size_t Location
= GetModelColumnPos( nId
);
3103 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
3104 DBG_ASSERT(pColumn
, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3107 pColumn
->m_bHidden
= true;
3108 pColumn
->m_nLastVisibleWidth
= CalcReverseZoom(lCurrentWidth
);
3111 // and reset the focus
3112 if ( nId
== GetCurColumnId() )
3113 GoToColumnId( nNewColId
);
3116 void DbGridControl::ShowColumn(sal_uInt16 nId
)
3118 sal_uInt16 nPos
= GetModelColumnPos(nId
);
3119 DBG_ASSERT(nPos
!= GRID_COLUMN_NOT_FOUND
, "DbGridControl::ShowColumn : invalid argument !");
3120 if (nPos
== GRID_COLUMN_NOT_FOUND
)
3123 DbGridColumn
* pColumn
= m_aColumns
[ nPos
].get();
3124 if (!pColumn
->IsHidden())
3126 DBG_ASSERT(GetViewColumnPos(nId
) != GRID_COLUMN_NOT_FOUND
, "DbGridControl::ShowColumn : inconsistent internal state !");
3127 // if the column isn't marked as hidden, it should be visible, shouldn't it ?
3130 DBG_ASSERT(GetViewColumnPos(nId
) == GRID_COLUMN_NOT_FOUND
, "DbGridControl::ShowColumn : inconsistent internal state !");
3131 // the opposite situation ...
3133 // to determine the new view position we need an adjacent non-hidden column
3134 sal_uInt16 nNextNonHidden
= BROWSER_INVALIDID
;
3135 // first search the cols to the right
3136 for ( size_t i
= nPos
+ 1; i
< m_aColumns
.size(); ++i
)
3138 DbGridColumn
* pCurCol
= m_aColumns
[ i
].get();
3139 if (!pCurCol
->IsHidden())
3145 if ((nNextNonHidden
== BROWSER_INVALIDID
) && (nPos
> 0))
3148 for ( size_t i
= nPos
; i
> 0; --i
)
3150 DbGridColumn
* pCurCol
= m_aColumns
[ i
-1 ].get();
3151 if (!pCurCol
->IsHidden())
3153 nNextNonHidden
= i
-1;
3158 sal_uInt16 nNewViewPos
= (nNextNonHidden
== BROWSER_INVALIDID
)
3159 ? 1 // there is no visible column -> insert behind the handle col
3160 : GetViewColumnPos( m_aColumns
[ nNextNonHidden
]->GetId() ) + 1;
3161 // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
3162 // a position 1 for the first non-handle col -> +1
3163 DBG_ASSERT(nNewViewPos
!= GRID_COLUMN_NOT_FOUND
, "DbGridControl::ShowColumn : inconsistent internal state !");
3164 // we found a col marked as visible but got no view pos for it ...
3166 if ((nNextNonHidden
<nPos
) && (nNextNonHidden
!= BROWSER_INVALIDID
))
3167 // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
3173 pColumn
->getModel()->getPropertyValue(FM_PROP_LABEL
) >>= aName
;
3174 InsertDataColumn(nId
, aName
, CalcZoom(pColumn
->m_nLastVisibleWidth
), HeaderBarItemBits::CENTER
| HeaderBarItemBits::CLICKABLE
, nNewViewPos
);
3175 pColumn
->m_bHidden
= false;
3181 sal_uInt16
DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos
) const
3183 if (nPos
>= m_aColumns
.size())
3185 OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3186 return GRID_COLUMN_NOT_FOUND
;
3189 DbGridColumn
* pCol
= m_aColumns
[ nPos
].get();
3190 #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
3191 // in the debug version, we convert the ModelPos into a ViewPos and compare this with the
3192 // value we will return (nId at the corresponding Col in m_aColumns)
3194 if (!pCol
->IsHidden())
3195 { // makes sense only if the column is visible
3196 sal_uInt16 nViewPos
= nPos
;
3197 for ( size_t i
= 0; i
< m_aColumns
.size() && i
< nPos
; ++i
)
3198 if ( m_aColumns
[ i
]->IsHidden())
3201 DBG_ASSERT(pCol
&& GetColumnIdFromViewPos(nViewPos
) == pCol
->GetId(),
3202 "DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?");
3205 return pCol
->GetId();
3208 sal_uInt16
DbGridControl::GetModelColumnPos( sal_uInt16 nId
) const
3210 for ( size_t i
= 0; i
< m_aColumns
.size(); ++i
)
3211 if ( m_aColumns
[ i
]->GetId() == nId
)
3214 return GRID_COLUMN_NOT_FOUND
;
3217 void DbGridControl::implAdjustInSolarThread(bool _bRows
)
3219 SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
3220 ::osl::MutexGuard
aGuard(m_aAdjustSafety
);
3221 if (!Application::IsMainThread())
3223 m_nAsynAdjustEvent
= PostUserEvent(LINK(this, DbGridControl
, OnAsyncAdjust
), reinterpret_cast< void* >( _bRows
), true);
3224 m_bPendingAdjustRows
= _bRows
;
3226 SAL_INFO("svx.fmcomp", "posting an AdjustRows");
3228 SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
3233 SAL_INFO("svx.fmcomp", "doing an AdjustRows");
3235 SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
3236 // always adjust the rows before adjusting the data source
3237 // If this is not necessary (because the row count did not change), nothing is done
3238 // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
3239 // to a position behind row count know 'til now, the cursorMoved notification may come before the
3240 // RowCountChanged notification
3241 // 94093 - 02.11.2001 - frank.schoenheit@sun.com
3249 IMPL_LINK(DbGridControl
, OnAsyncAdjust
, void*, pAdjustWhat
, void)
3251 m_nAsynAdjustEvent
= nullptr;
3254 // see implAdjustInSolarThread for a comment why we do this every time
3260 void DbGridControl::BeginCursorAction()
3262 if (m_pFieldListeners
)
3264 ColumnFieldValueListeners
* pListeners
= static_cast<ColumnFieldValueListeners
*>(m_pFieldListeners
);
3265 for (const auto& rListener
: *pListeners
)
3267 GridFieldValueListener
* pCurrent
= rListener
.second
;
3269 pCurrent
->suspend();
3273 if (m_pDataSourcePropListener
)
3274 m_pDataSourcePropListener
->suspend();
3277 void DbGridControl::EndCursorAction()
3279 if (m_pFieldListeners
)
3281 ColumnFieldValueListeners
* pListeners
= static_cast<ColumnFieldValueListeners
*>(m_pFieldListeners
);
3282 for (const auto& rListener
: *pListeners
)
3284 GridFieldValueListener
* pCurrent
= rListener
.second
;
3290 if (m_pDataSourcePropListener
)
3291 m_pDataSourcePropListener
->resume();
3294 void DbGridControl::ConnectToFields()
3296 ColumnFieldValueListeners
* pListeners
= static_cast<ColumnFieldValueListeners
*>(m_pFieldListeners
);
3297 DBG_ASSERT(!pListeners
|| pListeners
->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3301 pListeners
= new ColumnFieldValueListeners
;
3302 m_pFieldListeners
= pListeners
;
3305 for (auto const & pCurrent
: m_aColumns
)
3307 sal_uInt16 nViewPos
= pCurrent
? GetViewColumnPos(pCurrent
->GetId()) : GRID_COLUMN_NOT_FOUND
;
3308 if (GRID_COLUMN_NOT_FOUND
== nViewPos
)
3311 Reference
< XPropertySet
> xField
= pCurrent
->GetField();
3315 // column is visible and bound here
3316 GridFieldValueListener
*& rpListener
= (*pListeners
)[pCurrent
->GetId()];
3317 DBG_ASSERT(!rpListener
, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3318 rpListener
= new GridFieldValueListener(*this, xField
, pCurrent
->GetId());
3322 void DbGridControl::DisconnectFromFields()
3324 if (!m_pFieldListeners
)
3327 ColumnFieldValueListeners
* pListeners
= static_cast<ColumnFieldValueListeners
*>(m_pFieldListeners
);
3328 while (!pListeners
->empty())
3330 sal_Int32 nOldSize
= pListeners
->size();
3331 pListeners
->begin()->second
->dispose();
3332 DBG_ASSERT(nOldSize
> static_cast<sal_Int32
>(pListeners
->size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
3336 m_pFieldListeners
= nullptr;
3339 void DbGridControl::FieldValueChanged(sal_uInt16 _nId
)
3341 osl::MutexGuard
aPreventDestruction(m_aDestructionSafety
);
3342 // needed as this may run in a thread other than the main one
3343 if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED
)
3344 // all other cases are handled elsewhere
3347 size_t Location
= GetModelColumnPos( _nId
);
3348 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
3352 std::unique_ptr
<vcl::SolarMutexTryAndBuyGuard
> pGuard
;
3353 while (!m_bWantDestruction
&& (!pGuard
|| !pGuard
->isAcquired()))
3354 pGuard
.reset(new vcl::SolarMutexTryAndBuyGuard
);
3356 if (m_bWantDestruction
)
3357 { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
3358 // => don't do anything
3359 // 73365 - 23.02.00 - FS
3363 // and finally do the update ...
3364 pColumn
->UpdateFromField(m_xCurrentRow
.get(), m_xFormatter
);
3365 RowModified(GetCurRow());
3368 void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId
)
3370 ColumnFieldValueListeners
* pListeners
= static_cast<ColumnFieldValueListeners
*>(m_pFieldListeners
);
3373 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3377 ColumnFieldValueListeners::const_iterator aPos
= pListeners
->find(_nId
);
3378 if (aPos
== pListeners
->end())
3380 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3384 delete aPos
->second
;
3386 pListeners
->erase(aPos
);
3389 void DbGridControl::disposing(sal_uInt16 _nId
)
3392 { // the seek cursor is being disposed
3393 ::osl::MutexGuard
aGuard(m_aAdjustSafety
);
3394 setDataSource(nullptr, DbGridControlOptions::Readonly
); // our clone was disposed so we set our datasource to null to avoid later access to it
3395 if (m_nAsynAdjustEvent
)
3397 RemoveUserEvent(m_nAsynAdjustEvent
);
3398 m_nAsynAdjustEvent
= nullptr;
3403 sal_Int32
DbGridControl::GetAccessibleControlCount() const
3405 return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control
3408 Reference
<XAccessible
> DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex
)
3410 Reference
<XAccessible
> xRet
;
3411 if ( _nIndex
== EditBrowseBox::GetAccessibleControlCount() )
3413 xRet
= m_aBar
->GetAccessible();
3416 xRet
= EditBrowseBox::CreateAccessibleControl( _nIndex
);
3420 Reference
< XAccessible
> DbGridControl::CreateAccessibleCell( sal_Int32 _nRow
, sal_uInt16 _nColumnPos
)
3422 sal_uInt16 nColumnId
= GetColumnId( _nColumnPos
);
3423 size_t Location
= GetModelColumnPos(nColumnId
);
3424 DbGridColumn
* pColumn
= ( Location
< m_aColumns
.size() ) ? m_aColumns
[ Location
].get() : nullptr;
3427 Reference
< css::awt::XControl
> xInt(pColumn
->GetCell());
3428 Reference
< css::awt::XCheckBox
> xBox(xInt
,UNO_QUERY
);
3431 TriState eValue
= TRISTATE_FALSE
;
3432 switch( xBox
->getState() )
3435 eValue
= TRISTATE_FALSE
;
3438 eValue
= TRISTATE_TRUE
;
3441 eValue
= TRISTATE_INDET
;
3444 return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow
, _nColumnPos
,eValue
);
3447 return EditBrowseBox::CreateAccessibleCell( _nRow
, _nColumnPos
);
3450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */