Bump version to 6.4-15
[LibreOffice.git] / svx / source / fmcomp / gridctrl.cxx
blob8b74b76a3ca75c065a0a8b07c4cde13e0839099a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/macros.h>
21 #include <sal/log.hxx>
22 #include <helpids.h>
23 #include <svx/gridctrl.hxx>
24 #include <gridcell.hxx>
25 #include <svx/fmtools.hxx>
26 #include <svtools/stringtransfer.hxx>
27 #include <connectivity/dbtools.hxx>
28 #include <connectivity/dbconversion.hxx>
30 #include <fmprop.hxx>
31 #include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
32 #include <com/sun/star/accessibility/XAccessible.hpp>
33 #include <com/sun/star/sdb/XResultSetAccess.hpp>
34 #include <com/sun/star/sdb/RowChangeAction.hpp>
35 #include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
36 #include <com/sun/star/sdbc/SQLException.hpp>
37 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
38 #include <com/sun/star/sdbcx/Privilege.hpp>
39 #include <com/sun/star/container/XChild.hpp>
40 #include <com/sun/star/util/NumberFormatter.hpp>
41 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
42 #include <com/sun/star/util/XCloneable.hpp>
43 #include <com/sun/star/beans/XPropertySet.hpp>
44 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
45 #include <com/sun/star/container/XIndexAccess.hpp>
46 #include <tools/diagnose_ex.h>
47 #include <tools/debug.hxx>
48 #include <tools/fract.hxx>
49 #include <vcl/builder.hxx>
50 #include <vcl/menu.hxx>
51 #include <vcl/settings.hxx>
52 #include <vcl/commandevent.hxx>
53 #include <vcl/svapp.hxx>
55 #include <svx/strings.hrc>
57 #include <svx/svxids.hrc>
58 #include <svx/dialmgr.hxx>
59 #include <fmservs.hxx>
60 #include <sdbdatacolumn.hxx>
62 #include <comphelper/property.hxx>
63 #include <comphelper/types.hxx>
64 #include <cppuhelper/implbase.hxx>
66 #include <algorithm>
67 #include <cstdlib>
68 #include <map>
69 #include <memory>
71 using namespace ::dbtools;
72 using namespace ::dbtools::DBTypeConversion;
73 using namespace ::svxform;
74 using namespace ::svt;
75 using namespace ::com::sun::star::beans;
76 using namespace ::com::sun::star::lang;
77 using namespace ::com::sun::star::uno;
78 using namespace ::com::sun::star::sdbc;
79 using namespace ::com::sun::star::sdbcx;
80 using namespace ::com::sun::star::sdb;
81 using namespace ::com::sun::star::datatransfer;
82 using namespace ::com::sun::star::container;
83 using namespace com::sun::star::accessibility;
85 #define ROWSTATUS(row) (!row.is() ? "NULL" : row->GetStatus() == GridRowStatus::Clean ? "CLEAN" : row->GetStatus() == GridRowStatus::Modified ? "MODIFIED" : row->GetStatus() == GridRowStatus::Deleted ? "DELETED" : "INVALID")
87 static constexpr auto DEFAULT_BROWSE_MODE =
88 BrowserMode::COLUMNSELECTION
89 | BrowserMode::MULTISELECTION
90 | BrowserMode::KEEPHIGHLIGHT
91 | BrowserMode::TRACKING_TIPS
92 | BrowserMode::HLINES
93 | BrowserMode::VLINES
94 | BrowserMode::HEADERBAR_NEW;
96 class RowSetEventListener : public ::cppu::WeakImplHelper<XRowsChangeListener>
98 VclPtr<DbGridControl> m_pControl;
99 public:
100 explicit RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
104 private:
105 // XEventListener
106 virtual void SAL_CALL disposing(const css::lang::EventObject& /*i_aEvt*/) override
109 virtual void SAL_CALL rowsChanged(const css::sdb::RowsChangeEvent& i_aEvt) override
111 if ( i_aEvt.Action == RowChangeAction::UPDATE )
113 ::DbGridControl::GrantControlAccess aAccess;
114 CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
115 const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
116 for(const Any& rBookmark : i_aEvt.Bookmarks)
118 pSeek->moveToBookmark(rBookmark);
119 // get the data
120 rSeekRow->SetState(pSeek, true);
121 sal_Int32 nSeekPos = pSeek->getRow() - 1;
122 m_pControl->SetSeekPos(nSeekPos,aAccess);
123 m_pControl->RowModified(nSeekPos);
129 class GridFieldValueListener;
130 typedef std::map<sal_uInt16, GridFieldValueListener*> ColumnFieldValueListeners;
132 class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
134 osl::Mutex m_aMutex;
135 DbGridControl& m_rParent;
136 rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_pRealListener;
137 sal_uInt16 const m_nId;
138 sal_Int16 m_nSuspended;
139 bool m_bDisposed : 1;
141 public:
142 GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
143 virtual ~GridFieldValueListener() override;
145 virtual void _propertyChanged(const PropertyChangeEvent& evt) override;
147 void suspend() { ++m_nSuspended; }
148 void resume() { --m_nSuspended; }
150 void dispose();
153 GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
154 :OPropertyChangeListener(m_aMutex)
155 ,m_rParent(_rParent)
156 ,m_nId(_nId)
157 ,m_nSuspended(0)
158 ,m_bDisposed(false)
160 if (_rField.is())
162 m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
163 m_pRealListener->addProperty(FM_PROP_VALUE);
167 GridFieldValueListener::~GridFieldValueListener()
169 dispose();
172 void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& /*_evt*/)
174 DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
175 if (m_nSuspended <= 0)
176 m_rParent.FieldValueChanged(m_nId);
179 void GridFieldValueListener::dispose()
181 if (m_bDisposed)
183 DBG_ASSERT(m_pRealListener.get() == nullptr, "GridFieldValueListener::dispose : inconsistent !");
184 return;
187 if (m_pRealListener.is())
189 m_pRealListener->dispose();
190 m_pRealListener.clear();
193 m_bDisposed = true;
194 m_rParent.FieldListenerDisposing(m_nId);
197 class DisposeListenerGridBridge : public FmXDisposeListener
199 DbGridControl& m_rParent;
200 rtl::Reference<FmXDisposeMultiplexer> m_xRealListener;
202 public:
203 DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject);
204 virtual ~DisposeListenerGridBridge() override;
206 virtual void disposing(sal_Int16 _nId) override { m_rParent.disposing(_nId); }
209 DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject)
210 :FmXDisposeListener()
211 ,m_rParent(_rParent)
214 if (_rxObject.is())
216 m_xRealListener = new FmXDisposeMultiplexer(this, _rxObject);
220 DisposeListenerGridBridge::~DisposeListenerGridBridge()
222 if (m_xRealListener.is())
224 m_xRealListener->dispose();
228 static const DbGridControlNavigationBarState ControlMap[] =
230 DbGridControlNavigationBarState::Text,
231 DbGridControlNavigationBarState::Absolute,
232 DbGridControlNavigationBarState::Of,
233 DbGridControlNavigationBarState::Count,
234 DbGridControlNavigationBarState::First,
235 DbGridControlNavigationBarState::Next,
236 DbGridControlNavigationBarState::Prev,
237 DbGridControlNavigationBarState::Last,
238 DbGridControlNavigationBarState::New,
239 DbGridControlNavigationBarState::NONE
242 bool CompareBookmark(const Any& aLeft, const Any& aRight)
244 return aLeft == aRight;
247 class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
249 VclPtr<DbGridControl> m_pParent;
251 // a DbGridControl has no mutex, so we use our own as the base class expects one
252 osl::Mutex m_aMutex;
253 sal_Int16 m_nSuspended;
255 public:
256 explicit FmXGridSourcePropListener(DbGridControl* _pParent);
258 void suspend() { ++m_nSuspended; }
259 void resume() { --m_nSuspended; }
261 virtual void _propertyChanged(const PropertyChangeEvent& evt) override;
264 FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
265 :OPropertyChangeListener(m_aMutex)
266 ,m_pParent(_pParent)
267 ,m_nSuspended(0)
269 DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
272 void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt)
274 DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
275 if (m_nSuspended <= 0)
276 m_pParent->DataSourcePropertyChanged(evt);
279 DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(vcl::Window* pParent, WinBits nStyle)
280 :NumericField(pParent, nStyle)
282 SetMin(1);
283 SetFirst(1);
284 SetSpinSize(1);
286 SetDecimalDigits(0);
287 SetStrictFormat(true);
290 void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt)
292 if (rEvt.GetKeyCode() == KEY_RETURN && !GetText().isEmpty())
294 sal_Int64 nRecord = GetValue();
295 if (nRecord < GetMin() || nRecord > GetMax())
296 return;
297 else
298 static_cast<NavigationBar*>(GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
300 else if (rEvt.GetKeyCode() == KEY_TAB)
301 GetParent()->GetParent()->GrabFocus();
302 else
303 NumericField::KeyInput(rEvt);
306 void DbGridControl::NavigationBar::AbsolutePos::LoseFocus()
308 NumericField::LoseFocus();
309 sal_Int64 nRecord = GetValue();
310 if (nRecord < GetMin() || nRecord > GetMax())
311 return;
312 else
314 static_cast<NavigationBar*>(GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord));
315 static_cast<NavigationBar*>(GetParent())->InvalidateState(DbGridControlNavigationBarState::Absolute);
319 void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord)
321 if (m_bPositioning)
322 return;
323 // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
324 // so protect against this recursion
325 m_bPositioning = true;
326 static_cast<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1);
327 m_bPositioning = false;
330 DbGridControl::NavigationBar::NavigationBar(vcl::Window* pParent)
331 :Control(pParent, 0)
332 ,m_aRecordText(VclPtr<FixedText>::Create(this, WB_VCENTER))
333 ,m_aAbsolute(VclPtr<DbGridControl::NavigationBar::AbsolutePos>::Create(this, WB_CENTER | WB_VCENTER))
334 ,m_aRecordOf(VclPtr<FixedText>::Create(this, WB_VCENTER))
335 ,m_aRecordCount(VclPtr<FixedText>::Create(this, WB_VCENTER))
336 ,m_aFirstBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
337 ,m_aPrevBtn(VclPtr<ImageButton>::Create(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS))
338 ,m_aNextBtn(VclPtr<ImageButton>::Create(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS))
339 ,m_aLastBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
340 ,m_aNewBtn(VclPtr<ImageButton>::Create(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS))
341 ,m_nCurrentPos(-1)
342 ,m_bPositioning(false)
344 m_aFirstBtn->SetSymbol(SymbolType::FIRST);
345 m_aPrevBtn->SetSymbol(SymbolType::PREV);
346 m_aNextBtn->SetSymbol(SymbolType::NEXT);
347 m_aLastBtn->SetSymbol(SymbolType::LAST);
348 m_aNewBtn->SetModeImage(static_cast<DbGridControl*>(pParent)->GetImage(EditBrowseBox::NEW));
350 m_aFirstBtn->SetHelpId(HID_GRID_TRAVEL_FIRST);
351 m_aPrevBtn->SetHelpId(HID_GRID_TRAVEL_PREV);
352 m_aNextBtn->SetHelpId(HID_GRID_TRAVEL_NEXT);
353 m_aLastBtn->SetHelpId(HID_GRID_TRAVEL_LAST);
354 m_aNewBtn->SetHelpId(HID_GRID_TRAVEL_NEW);
355 m_aAbsolute->SetHelpId(HID_GRID_TRAVEL_ABSOLUTE);
356 m_aRecordCount->SetHelpId(HID_GRID_NUMBEROFRECORDS);
358 // set handlers for buttons
359 m_aFirstBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
360 m_aPrevBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
361 m_aNextBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
362 m_aLastBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
363 m_aNewBtn->SetClickHdl(LINK(this,NavigationBar,OnClick));
365 m_aRecordText->SetText(SvxResId(RID_STR_REC_TEXT));
366 m_aRecordOf->SetText(SvxResId(RID_STR_REC_FROM_TEXT));
367 m_aRecordCount->SetText(OUString('?'));
369 m_aFirstBtn->Disable();
370 m_aPrevBtn->Disable();
371 m_aNextBtn->Disable();
372 m_aLastBtn->Disable();
373 m_aNewBtn->Disable();
374 m_aRecordText->Disable();
375 m_aRecordOf->Disable();
376 m_aRecordCount->Disable();
377 m_aAbsolute->Disable();
379 AllSettings aSettings = m_aNextBtn->GetSettings();
380 MouseSettings aMouseSettings = aSettings.GetMouseSettings();
381 aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
382 aSettings.SetMouseSettings(aMouseSettings);
383 m_aNextBtn->SetSettings(aSettings, true);
384 m_aPrevBtn->SetSettings(aSettings, true);
386 m_aFirstBtn->Show();
387 m_aPrevBtn->Show();
388 m_aNextBtn->Show();
389 m_aLastBtn->Show();
390 m_aNewBtn->Show();
391 m_aRecordText->Show();
392 m_aRecordOf->Show();
393 m_aRecordCount->Show();
394 m_aAbsolute->Show();
398 DbGridControl::NavigationBar::~NavigationBar()
400 disposeOnce();
403 void DbGridControl::NavigationBar::dispose()
405 m_aRecordText.disposeAndClear();
406 m_aAbsolute.disposeAndClear();
407 m_aRecordOf.disposeAndClear();
408 m_aRecordCount.disposeAndClear();
409 m_aFirstBtn.disposeAndClear();
410 m_aPrevBtn.disposeAndClear();
411 m_aNextBtn.disposeAndClear();
412 m_aLastBtn.disposeAndClear();
413 m_aNewBtn.disposeAndClear();
414 Control::dispose();
417 namespace
419 void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize)
421 _rButton.SetPosPixel( _rPos );
422 _rButton.SetSizePixel( _rSize );
423 _rPos.AdjustX(static_cast<sal_uInt16>(_rSize.Width()) );
427 sal_uInt16 DbGridControl::NavigationBar::ArrangeControls()
429 // positioning of the controls
430 // calculate base size
431 tools::Rectangle aRect(static_cast<DbGridControl*>(GetParent())->GetControlArea());
432 long nH = aRect.GetSize().Height();
433 long nW = GetParent()->GetOutputSizePixel().Width();
434 Size aBorder = LogicToPixel(Size(2, 2), MapMode(MapUnit::MapAppFont));
435 aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height()));
436 sal_uInt16 nX = 1;
437 sal_uInt16 nY = 0;
440 vcl::Font aApplFont(GetSettings().GetStyleSettings().GetToolFont());
441 m_aAbsolute->SetControlFont( aApplFont );
442 aApplFont.SetTransparent( true );
443 m_aRecordText->SetControlFont( aApplFont );
444 m_aRecordOf->SetControlFont( aApplFont );
445 m_aRecordCount->SetControlFont( aApplFont );
448 // set size and position of the control
449 OUString aText = m_aRecordText->GetText();
450 long nTextWidth = m_aRecordText->GetTextWidth(aText);
451 m_aRecordText->SetPosPixel(Point(nX,nY));
452 m_aRecordText->SetSizePixel(Size(nTextWidth,nH));
453 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
455 // count an extra hairspace (U+200A) left and right
456 const OUString sevenDigits(m_aAbsolute->CreateFieldText(6000000));
457 const OUString hairSpace(u'\x200A');
458 OUString textPattern = hairSpace + sevenDigits + hairSpace;
459 nTextWidth = m_aAbsolute->GetTextWidth(textPattern);
460 m_aAbsolute->SetPosPixel(Point(nX,nY));
461 m_aAbsolute->SetSizePixel(Size(nTextWidth, nH));
462 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
464 aText = m_aRecordOf->GetText();
465 nTextWidth = m_aRecordOf->GetTextWidth(aText);
466 m_aRecordOf->SetPosPixel(Point(nX,nY));
467 m_aRecordOf->SetSizePixel(Size(nTextWidth,nH));
468 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
470 textPattern = sevenDigits + " * (" + sevenDigits + ")";
471 nTextWidth = m_aRecordCount->GetTextWidth(textPattern);
472 m_aRecordCount->SetPosPixel(Point(nX,nY));
473 m_aRecordCount->SetSizePixel(Size(nTextWidth,nH));
474 nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width());
476 Point aButtonPos(nX,nY);
477 const Size aButtonSize(nH,nH);
478 SetPosAndSize(*m_aFirstBtn.get(), aButtonPos, aButtonSize);
479 SetPosAndSize(*m_aPrevBtn.get(), aButtonPos, aButtonSize);
480 SetPosAndSize(*m_aNextBtn.get(), aButtonPos, aButtonSize);
481 SetPosAndSize(*m_aLastBtn.get(), aButtonPos, aButtonSize);
482 SetPosAndSize(*m_aNewBtn.get(), aButtonPos, aButtonSize);
484 nX = sal::static_int_cast< sal_uInt16 >(aButtonPos.X() + 1);
486 nW = std::max(nW - GetSettings().GetStyleSettings().GetScrollBarSize(), 0L);
488 if (nX > nW)
490 aButtonPos.setX( nW-nH );
491 m_aNewBtn->SetPosPixel(aButtonPos);
492 aButtonPos.AdjustX( -nH );
493 m_aLastBtn->SetPosPixel(aButtonPos);
494 aButtonPos.AdjustX( -nH );
495 m_aNextBtn->SetPosPixel(aButtonPos);
496 aButtonPos.AdjustX( -nH );
497 m_aPrevBtn->SetPosPixel(aButtonPos);
498 aButtonPos.AdjustX( -nH );
499 m_aFirstBtn->SetPosPixel(aButtonPos);
501 auto nDiff = nX - nW;
503 Size aSize = m_aAbsolute->GetSizePixel();
504 aSize.AdjustWidth( -(nDiff/3.0) );
505 m_aAbsolute->SetSizePixel(aSize);
507 aSize = m_aRecordCount->GetSizePixel();
508 aSize.AdjustWidth( -(nDiff/3.0*2) );
509 m_aRecordCount->SetSizePixel(aSize);
511 Point aPos = m_aRecordOf->GetPosPixel();
512 aPos.AdjustX( -(nDiff/3.0) );
513 m_aRecordOf->SetPosPixel(aPos);
515 aPos = m_aRecordCount->GetPosPixel();
516 aPos.AdjustX( -(nDiff/3.0) );
517 m_aRecordCount->SetPosPixel(aPos);
519 vcl::Window* pWindows[] =
521 m_aRecordText.get(),
522 m_aAbsolute.get(),
523 m_aRecordOf.get(),
524 m_aRecordCount.get(),
525 m_aFirstBtn.get(),
526 m_aPrevBtn.get(),
527 m_aNextBtn.get(),
528 m_aLastBtn.get(),
529 m_aNewBtn.get()
532 for (vcl::Window* pWindow : pWindows)
534 if (pWindow->GetPosPixel().X() < 0)
535 pWindow->SetSizePixel(Size(0, nH));
536 aSize = pWindow->GetSizePixel();
537 auto nExcess = (pWindow->GetPosPixel().X() + aSize.Width()) - nW;
538 if (nExcess > 0)
540 aSize.AdjustWidth( -nExcess );
541 pWindow->SetSizePixel(aSize);
545 nX = nW;
548 return nX;
551 IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton, void )
553 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
555 if (pParent->m_aMasterSlotExecutor.IsSet())
557 bool lResult = false;
558 if (pButton == m_aFirstBtn.get())
559 lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::First);
560 else if( pButton == m_aPrevBtn.get() )
561 lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Prev);
562 else if( pButton == m_aNextBtn.get() )
563 lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Next);
564 else if( pButton == m_aLastBtn.get() )
565 lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Last);
566 else if( pButton == m_aNewBtn.get() )
567 lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::New);
569 if (lResult)
570 // the link already handled it
571 return;
574 if (pButton == m_aFirstBtn.get())
575 pParent->MoveToFirst();
576 else if( pButton == m_aPrevBtn.get() )
577 pParent->MoveToPrev();
578 else if( pButton == m_aNextBtn.get() )
579 pParent->MoveToNext();
580 else if( pButton == m_aLastBtn.get() )
581 pParent->MoveToLast();
582 else if( pButton == m_aNewBtn.get() )
583 pParent->AppendNew();
586 void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll)
588 if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)
590 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
592 sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControlOptions::Insert) ? 2 : 1);
594 // check if everything needs to be invalidated
595 bAll = bAll || m_nCurrentPos <= 0;
596 bAll = bAll || nCurrentPos <= 0;
597 bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
598 bAll = bAll || nCurrentPos >= nAdjustedRowCount;
600 if ( bAll )
602 m_nCurrentPos = nCurrentPos;
603 int i = 0;
604 while (ControlMap[i] != DbGridControlNavigationBarState::NONE)
605 SetState(ControlMap[i++]);
607 else // is in the center
609 m_nCurrentPos = nCurrentPos;
610 SetState(DbGridControlNavigationBarState::Count);
611 SetState(DbGridControlNavigationBarState::Absolute);
616 bool DbGridControl::NavigationBar::GetState(DbGridControlNavigationBarState nWhich) const
618 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
620 if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
621 || pParent->IsFilterMode() )
622 return false;
623 else
625 // check if we have a master state provider
626 if (pParent->m_aMasterStateProvider.IsSet())
628 long nState = pParent->m_aMasterStateProvider.Call( nWhich );
629 if (nState>=0)
630 return (nState>0);
633 bool bAvailable = true;
635 switch (nWhich)
637 case DbGridControlNavigationBarState::First:
638 case DbGridControlNavigationBarState::Prev:
639 bAvailable = m_nCurrentPos > 0;
640 break;
641 case DbGridControlNavigationBarState::Next:
642 if(pParent->m_bRecordCountFinal)
644 bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
645 if (!bAvailable && pParent->GetOptions() & DbGridControlOptions::Insert)
646 bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
648 break;
649 case DbGridControlNavigationBarState::Last:
650 if(pParent->m_bRecordCountFinal)
652 if (pParent->GetOptions() & DbGridControlOptions::Insert)
653 bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
654 m_nCurrentPos != pParent->GetRowCount() - 2;
655 else
656 bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
658 break;
659 case DbGridControlNavigationBarState::New:
660 bAvailable = (pParent->GetOptions() & DbGridControlOptions::Insert) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
661 break;
662 case DbGridControlNavigationBarState::Absolute:
663 bAvailable = pParent->GetRowCount() > 0;
664 break;
665 default: break;
667 return bAvailable;
671 void DbGridControl::NavigationBar::SetState(DbGridControlNavigationBarState nWhich)
673 bool bAvailable = GetState(nWhich);
674 DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
675 vcl::Window* pWnd = nullptr;
676 switch (nWhich)
678 case DbGridControlNavigationBarState::First:
679 pWnd = m_aFirstBtn.get();
680 break;
681 case DbGridControlNavigationBarState::Prev:
682 pWnd = m_aPrevBtn.get();
683 break;
684 case DbGridControlNavigationBarState::Next:
685 pWnd = m_aNextBtn.get();
686 break;
687 case DbGridControlNavigationBarState::Last:
688 pWnd = m_aLastBtn.get();
689 break;
690 case DbGridControlNavigationBarState::New:
691 pWnd = m_aNewBtn.get();
692 break;
693 case DbGridControlNavigationBarState::Absolute:
694 pWnd = m_aAbsolute.get();
695 if (bAvailable)
697 if (pParent->m_nTotalCount >= 0)
699 if (pParent->IsCurrentAppending())
700 m_aAbsolute->SetMax(pParent->m_nTotalCount + 1);
701 else
702 m_aAbsolute->SetMax(pParent->m_nTotalCount);
704 else
705 m_aAbsolute->SetMax(LONG_MAX);
707 m_aAbsolute->SetValue(m_nCurrentPos + 1);
709 else
710 m_aAbsolute->SetText(OUString());
711 break;
712 case DbGridControlNavigationBarState::Text:
713 pWnd = m_aRecordText.get();
714 break;
715 case DbGridControlNavigationBarState::Of:
716 pWnd = m_aRecordOf.get();
717 break;
718 case DbGridControlNavigationBarState::Count:
720 pWnd = m_aRecordCount.get();
721 OUString aText;
722 if (bAvailable)
724 if (pParent->GetOptions() & DbGridControlOptions::Insert)
726 if (pParent->IsCurrentAppending() && !pParent->IsModified())
727 aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount());
728 else
729 aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount() - 1);
731 else
732 aText = m_aAbsolute->CreateFieldText(pParent->GetRowCount());
733 if(!pParent->m_bRecordCountFinal)
734 aText += " *";
736 else
737 aText.clear();
739 // add the number of selected rows, if applicable
740 if (pParent->GetSelectRowCount())
742 OUString aExtendedInfo = aText + " (" +
743 m_aAbsolute->CreateFieldText(pParent->GetSelectRowCount()) + ")";
744 pWnd->SetText(aExtendedInfo);
746 else
747 pWnd->SetText(aText);
749 pParent->SetRealRowCount(aText);
750 } break;
751 default: break;
753 DBG_ASSERT(pWnd, "no window");
754 if (pWnd && (pWnd->IsEnabled() != bAvailable))
755 // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
756 // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
757 // do this check.
758 // For further explanation see Bug 69900.
759 pWnd->Enable(bAvailable);
762 void DbGridControl::NavigationBar::Resize()
764 Control::Resize();
765 ArrangeControls();
768 void DbGridControl::NavigationBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
770 Control::Paint(rRenderContext, rRect);
771 Point aAbsolutePos = m_aAbsolute->GetPosPixel();
772 Size aAbsoluteSize = m_aAbsolute->GetSizePixel();
774 rRenderContext.DrawLine(Point(aAbsolutePos.X() - 1, 0 ),
775 Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
777 rRenderContext.DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ),
778 Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height()));
781 void DbGridControl::NavigationBar::StateChanged(StateChangedType nType)
783 Control::StateChanged(nType);
785 vcl::Window* pWindows[] =
787 m_aRecordText.get(),
788 m_aAbsolute.get(),
789 m_aRecordOf.get(),
790 m_aRecordCount.get(),
791 m_aFirstBtn.get(),
792 m_aPrevBtn.get(),
793 m_aNextBtn.get(),
794 m_aLastBtn.get(),
795 m_aNewBtn.get()
798 switch ( nType )
800 case StateChangedType::Mirroring:
802 bool bIsRTLEnabled = IsRTLEnabled();
803 for (vcl::Window* pWindow : pWindows)
804 pWindow->EnableRTL( bIsRTLEnabled );
806 break;
808 case StateChangedType::Zoom:
810 Fraction aZoom = GetZoom();
812 // not all of these controls need to know the new zoom, but to be sure ...
813 vcl::Font aFont(GetSettings().GetStyleSettings().GetToolFont());
814 if (IsControlFont())
815 aFont.Merge(GetControlFont());
817 for (vcl::Window* pWindow : pWindows)
819 pWindow->SetZoom(aZoom);
820 pWindow->SetZoomedPointFont(*pWindow, aFont);
823 SetZoomedPointFont(*this, aFont);
825 // rearrange the controls
826 ArrangeControls();
828 break;
829 default:;
833 DbGridRow::DbGridRow():m_eStatus(GridRowStatus::Clean), m_bIsNew(true)
836 DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor)
837 :m_bIsNew(false)
840 if (pCur && pCur->Is())
842 Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
843 for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
845 Reference< XPropertySet > xColSet(
846 xColumns->getByIndex(i), css::uno::UNO_QUERY);
847 m_aVariants.emplace_back( new DataColumn(xColSet) );
850 if (pCur->rowDeleted())
851 m_eStatus = GridRowStatus::Deleted;
852 else
854 if (bPaintCursor)
855 m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GridRowStatus::Invalid : GridRowStatus::Clean;
856 else
858 const Reference< XPropertySet >& xSet = pCur->getPropertySet();
859 if (xSet.is())
861 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
862 if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
863 m_eStatus = GridRowStatus::Invalid;
864 else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
865 m_eStatus = GridRowStatus::Modified;
866 else
867 m_eStatus = GridRowStatus::Clean;
869 else
870 m_eStatus = GridRowStatus::Invalid;
873 if (!m_bIsNew && IsValid())
874 m_aBookmark = pCur->getBookmark();
875 else
876 m_aBookmark = Any();
878 else
879 m_eStatus = GridRowStatus::Invalid;
882 DbGridRow::~DbGridRow()
886 void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor)
888 if (pCur && pCur->Is())
890 if (pCur->rowDeleted())
892 m_eStatus = GridRowStatus::Deleted;
893 m_bIsNew = false;
895 else
897 m_eStatus = GridRowStatus::Clean;
898 if (!bPaintCursor)
900 const Reference< XPropertySet >& xSet = pCur->getPropertySet();
901 DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
903 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
904 m_eStatus = GridRowStatus::Modified;
905 m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
907 else
908 m_bIsNew = false;
913 if (!m_bIsNew && IsValid())
914 m_aBookmark = pCur->getBookmark();
915 else
916 m_aBookmark = Any();
918 catch(SQLException&)
920 DBG_UNHANDLED_EXCEPTION("svx");
921 m_aBookmark = Any();
922 m_eStatus = GridRowStatus::Invalid;
923 m_bIsNew = false;
926 else
928 m_aBookmark = Any();
929 m_eStatus = GridRowStatus::Invalid;
930 m_bIsNew = false;
934 DbGridControl::DbGridControl(
935 Reference< XComponentContext > const & _rxContext,
936 vcl::Window* pParent,
937 WinBits nBits)
938 :EditBrowseBox(pParent, EditBrowseBoxFlags::NONE, nBits, DEFAULT_BROWSE_MODE )
939 ,m_xContext(_rxContext)
940 ,m_aBar(VclPtr<DbGridControl::NavigationBar>::Create(this))
941 ,m_nAsynAdjustEvent(nullptr)
942 ,m_pDataSourcePropListener(nullptr)
943 ,m_pFieldListeners(nullptr)
944 ,m_pGridListener(nullptr)
945 ,m_nSeekPos(-1)
946 ,m_nTotalCount(-1)
947 ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate())
948 ,m_nMode(DEFAULT_BROWSE_MODE)
949 ,m_nCurrentPos(-1)
950 ,m_nDeleteEvent(nullptr)
951 ,m_nOptions(DbGridControlOptions::Readonly)
952 ,m_nOptionMask(DbGridControlOptions::Insert | DbGridControlOptions::Update | DbGridControlOptions::Delete)
953 ,m_nLastColId(sal_uInt16(-1))
954 ,m_nLastRowId(-1)
955 ,m_bDesignMode(false)
956 ,m_bRecordCountFinal(false)
957 ,m_bNavigationBar(true)
958 ,m_bSynchDisplay(true)
959 ,m_bHandle(true)
960 ,m_bFilterMode(false)
961 ,m_bWantDestruction(false)
962 ,m_bPendingAdjustRows(false)
963 ,m_bHideScrollbars( false )
964 ,m_bUpdating(false)
967 OUString sName(SvxResId(RID_STR_NAVIGATIONBAR));
968 m_aBar->SetAccessibleName(sName);
969 m_aBar->Show();
970 ImplInitWindow( InitWindowFacet::All );
973 void DbGridControl::InsertHandleColumn()
975 // BrowseBox has problems when painting without a handleColumn (hide it here)
976 if (HasHandle())
977 BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
978 else
979 BrowseBox::InsertHandleColumn(0);
982 void DbGridControl::Init()
984 VclPtr<BrowserHeader> pNewHeader = CreateHeaderBar(this);
985 pHeader->SetMouseTransparent(false);
987 SetHeaderBar(pNewHeader);
988 SetMode(m_nMode);
989 SetCursorColor(Color(0xFF, 0, 0));
991 InsertHandleColumn();
994 DbGridControl::~DbGridControl()
996 disposeOnce();
999 void DbGridControl::dispose()
1001 if (!IsDisposed())
1003 RemoveColumns();
1005 m_bWantDestruction = true;
1006 osl::MutexGuard aGuard(m_aDestructionSafety);
1007 if (m_pFieldListeners)
1008 DisconnectFromFields();
1009 m_pCursorDisposeListener.reset();
1012 if (m_nDeleteEvent)
1013 Application::RemoveUserEvent(m_nDeleteEvent);
1015 if (m_pDataSourcePropMultiplexer.is())
1017 m_pDataSourcePropMultiplexer->dispose();
1018 m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer
1019 delete m_pDataSourcePropListener;
1020 m_pDataSourcePropListener = nullptr;
1022 m_xRowSetListener.clear();
1024 m_pDataCursor.reset();
1025 m_pSeekCursor.reset();
1027 m_aBar.disposeAndClear();
1029 EditBrowseBox::dispose();
1032 void DbGridControl::StateChanged( StateChangedType nType )
1034 EditBrowseBox::StateChanged( nType );
1036 switch (nType)
1038 case StateChangedType::Mirroring:
1039 ImplInitWindow( InitWindowFacet::WritingMode );
1040 Invalidate();
1041 break;
1043 case StateChangedType::Zoom:
1045 ImplInitWindow( InitWindowFacet::Font );
1047 // and give it a chance to rearrange
1048 Point aPoint = GetControlArea().TopLeft();
1049 sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
1050 ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y()));
1051 ReserveControlArea(nX);
1053 break;
1054 case StateChangedType::ControlFont:
1055 ImplInitWindow( InitWindowFacet::Font );
1056 Invalidate();
1057 break;
1058 case StateChangedType::ControlForeground:
1059 ImplInitWindow( InitWindowFacet::Foreground );
1060 Invalidate();
1061 break;
1062 case StateChangedType::ControlBackground:
1063 ImplInitWindow( InitWindowFacet::Background );
1064 Invalidate();
1065 break;
1066 default:;
1070 void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
1072 EditBrowseBox::DataChanged( rDCEvt );
1073 if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS ) &&
1074 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
1076 ImplInitWindow( InitWindowFacet::All );
1077 Invalidate();
1081 void DbGridControl::Select()
1083 EditBrowseBox::Select();
1085 // as the selected rows may have changed, update the according display in our navigation bar
1086 m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
1088 if (m_pGridListener)
1089 m_pGridListener->selectionChanged();
1092 void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
1094 for (auto const & pCol : m_aColumns)
1096 pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
1099 if ( _eInitWhat & InitWindowFacet::WritingMode )
1101 if ( m_bNavigationBar )
1103 m_aBar->EnableRTL( IsRTLEnabled() );
1107 if ( _eInitWhat & InitWindowFacet::Font )
1109 if ( m_bNavigationBar )
1111 vcl::Font aFont = m_aBar->GetSettings().GetStyleSettings().GetToolFont();
1112 if ( IsControlFont() )
1113 m_aBar->SetControlFont( GetControlFont() );
1114 else
1115 m_aBar->SetControlFont();
1117 m_aBar->SetZoom( GetZoom() );
1121 if ( _eInitWhat & InitWindowFacet::Background )
1123 if (IsControlBackground())
1125 GetDataWindow().SetBackground(GetControlBackground());
1126 GetDataWindow().SetControlBackground(GetControlBackground());
1127 GetDataWindow().SetFillColor(GetControlBackground());
1129 else
1131 GetDataWindow().SetControlBackground();
1132 GetDataWindow().SetFillColor(GetFillColor());
1137 void DbGridControl::RemoveRows(bool bNewCursor)
1139 // Did the data cursor change?
1140 if (!bNewCursor)
1142 m_pSeekCursor.reset();
1143 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr;
1144 m_nCurrentPos = m_nSeekPos = -1;
1145 m_nOptions = DbGridControlOptions::Readonly;
1147 RowRemoved(0, GetRowCount(), false);
1148 m_nTotalCount = -1;
1150 else
1152 RemoveRows();
1156 void DbGridControl::RemoveRows()
1158 // we're going to remove all columns and all row, so deactivate the current cell
1159 if (IsEditing())
1160 DeactivateCell();
1162 // de-initialize all columns
1163 // if there are columns, free all controllers
1164 for (auto const & pColumn : m_aColumns)
1165 pColumn->Clear();
1167 m_pSeekCursor.reset();
1168 m_pDataCursor.reset();
1170 m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr;
1171 m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
1172 m_nOptions = DbGridControlOptions::Readonly;
1174 // reset number of sentences to zero in the browser
1175 EditBrowseBox::RemoveRows();
1176 m_aBar->InvalidateAll(m_nCurrentPos, true);
1179 void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
1181 // positioning of the controls
1182 if (m_bNavigationBar)
1184 tools::Rectangle aRect(GetControlArea());
1185 m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(aRect.GetSize().Width(), aRect.GetSize().Height() - 1));
1186 nX = m_aBar->ArrangeControls();
1190 void DbGridControl::EnableHandle(bool bEnable)
1192 if (m_bHandle == bEnable)
1193 return;
1195 // HandleColumn is only hidden because there are a lot of problems while painting otherwise
1196 RemoveColumn( HandleColumnId );
1197 m_bHandle = bEnable;
1198 InsertHandleColumn();
1201 namespace
1203 bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars )
1205 BrowserMode nOldMode = _rMode;
1207 if ( !_bNavigationBar )
1209 _rMode &= ~BrowserMode::AUTO_HSCROLL;
1212 if ( _bHideScrollbars )
1214 _rMode |= BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL;
1215 _rMode &= ~BrowserMode( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL );
1217 else
1219 _rMode |= BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL;
1220 _rMode &= ~BrowserMode( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL );
1223 // note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular,
1224 // _bHideScrollbars is ignored then
1225 if ( _bNavigationBar )
1227 _rMode |= BrowserMode::AUTO_HSCROLL;
1228 _rMode &= ~BrowserMode::NO_HSCROLL;
1231 return nOldMode != _rMode;
1235 void DbGridControl::EnableNavigationBar(bool bEnable)
1237 if (m_bNavigationBar == bEnable)
1238 return;
1240 m_bNavigationBar = bEnable;
1242 if (bEnable)
1244 m_aBar->Show();
1245 m_aBar->Enable();
1246 m_aBar->InvalidateAll(m_nCurrentPos, true);
1248 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1249 SetMode( m_nMode );
1251 // get size of the reserved ControlArea
1252 Point aPoint = GetControlArea().TopLeft();
1253 sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
1255 ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y()));
1256 ReserveControlArea(nX);
1258 else
1260 m_aBar->Hide();
1261 m_aBar->Disable();
1263 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1264 SetMode( m_nMode );
1266 ReserveControlArea();
1270 DbGridControlOptions DbGridControl::SetOptions(DbGridControlOptions nOpt)
1272 DBG_ASSERT(!m_xCurrentRow.is() || !m_xCurrentRow->IsModified(),
1273 "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
1275 // for the next setDataSource (which is triggered by a refresh, for instance)
1276 m_nOptionMask = nOpt;
1278 // normalize the new options
1279 Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
1280 if (xDataSourceSet.is())
1282 // check what kind of options are available
1283 sal_Int32 nPrivileges = 0;
1284 xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1285 if ((nPrivileges & Privilege::INSERT) == 0)
1286 nOpt &= ~DbGridControlOptions::Insert;
1287 if ((nPrivileges & Privilege::UPDATE) == 0)
1288 nOpt &= ~DbGridControlOptions::Update;
1289 if ((nPrivileges & Privilege::DELETE) == 0)
1290 nOpt &= ~DbGridControlOptions::Delete;
1292 else
1293 nOpt = DbGridControlOptions::Readonly;
1295 // need to do something after that ?
1296 if (nOpt == m_nOptions)
1297 return m_nOptions;
1299 // the 'update' option only affects our BrowserMode (with or w/o focus rect)
1300 BrowserMode nNewMode = m_nMode;
1301 if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS))
1303 if (nOpt & DbGridControlOptions::Update)
1304 nNewMode |= BrowserMode::HIDECURSOR;
1305 else
1306 nNewMode &= ~BrowserMode::HIDECURSOR;
1308 else
1309 nNewMode &= ~BrowserMode::HIDECURSOR;
1310 // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
1312 if (nNewMode != m_nMode)
1314 SetMode(nNewMode);
1315 m_nMode = nNewMode;
1318 // _after_ setting the mode because this results in an ActivateCell
1319 DeactivateCell();
1321 bool bInsertChanged = (nOpt & DbGridControlOptions::Insert) != (m_nOptions & DbGridControlOptions::Insert);
1322 m_nOptions = nOpt;
1323 // we need to set this before the code below because it indirectly uses m_nOptions
1325 // the 'insert' option affects our empty row
1326 if (bInsertChanged)
1328 if (m_nOptions & DbGridControlOptions::Insert)
1329 { // the insert option is to be set
1330 m_xEmptyRow = new DbGridRow();
1331 RowInserted(GetRowCount());
1333 else
1334 { // the insert option is to be reset
1335 m_xEmptyRow = nullptr;
1336 if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
1337 GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
1338 RowRemoved(GetRowCount());
1342 // the 'delete' options has no immediate consequences
1344 ActivateCell();
1345 Invalidate();
1346 return m_nOptions;
1349 void DbGridControl::ForceHideScrollbars()
1351 if ( m_bHideScrollbars )
1352 return;
1354 m_bHideScrollbars = true;
1356 if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
1357 SetMode( m_nMode );
1360 void DbGridControl::EnablePermanentCursor(bool bEnable)
1362 if (IsPermanentCursorEnabled() == bEnable)
1363 return;
1365 if (bEnable)
1367 m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect
1368 m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
1370 else
1372 if (m_nOptions & DbGridControlOptions::Update)
1373 m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all
1374 else
1375 m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor
1377 m_nMode &= ~BrowserMode::CURSOR_WO_FOCUS;
1379 SetMode(m_nMode);
1381 bool bWasEditing = IsEditing();
1382 DeactivateCell();
1383 if (bWasEditing)
1384 ActivateCell();
1387 bool DbGridControl::IsPermanentCursorEnabled() const
1389 return (m_nMode & BrowserMode::CURSOR_WO_FOCUS) && !(m_nMode & BrowserMode::HIDECURSOR);
1392 void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
1394 if ((GetCurColumnId() == _nColId) && IsEditing())
1395 { // the controller which is currently active needs to be refreshed
1396 DeactivateCell();
1397 ActivateCell();
1401 void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, DbGridControlOptions nOpts)
1403 if (!_xCursor.is() && !m_pDataCursor)
1404 return;
1406 if (m_pDataSourcePropMultiplexer.is())
1408 m_pDataSourcePropMultiplexer->dispose();
1409 m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer
1410 delete m_pDataSourcePropListener;
1411 m_pDataSourcePropListener = nullptr;
1413 m_xRowSetListener.clear();
1415 // is the new cursor valid ?
1416 // the cursor is only valid if it contains some columns
1417 // if there is no cursor or the cursor is not valid we have to clean up and leave
1418 if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY_THROW)->getColumns()->hasElements())
1420 RemoveRows();
1421 return;
1424 // did the data cursor change?
1425 sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
1427 SetUpdateMode(false);
1428 RemoveRows();
1429 DisconnectFromFields();
1431 m_pCursorDisposeListener.reset();
1434 ::osl::MutexGuard aGuard(m_aAdjustSafety);
1435 if (m_nAsynAdjustEvent)
1437 // the adjust was thought to work with the old cursor which we don't have anymore
1438 RemoveUserEvent(m_nAsynAdjustEvent);
1439 m_nAsynAdjustEvent = nullptr;
1443 // get a new formatter and data cursor
1444 m_xFormatter = nullptr;
1445 Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true);
1446 if (xSupplier.is())
1448 m_xFormatter = css::util::NumberFormatter::create(m_xContext);
1449 m_xFormatter->attachNumberFormatsSupplier(xSupplier);
1451 // retrieve the datebase of the Numberformatter
1454 xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
1456 catch(Exception&)
1461 m_pDataCursor.reset(new CursorWrapper(_xCursor));
1463 // now create a cursor for painting rows
1464 // we need that cursor only if we are not in insert only mode
1465 Reference< XResultSet > xClone;
1466 Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
1469 xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
1471 catch(Exception&)
1474 if (xClone.is())
1475 m_pSeekCursor.reset(new CursorWrapper(xClone));
1477 // property listening on the data source
1478 // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
1479 // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
1480 // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
1481 // and forwards the property changes to our special method "DataSourcePropertyChanged".)
1482 if (m_pDataCursor)
1484 m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
1485 m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
1486 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
1487 m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
1490 BrowserMode nOldMode = m_nMode;
1491 if (m_pSeekCursor)
1495 Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
1496 if (xSet.is())
1498 // check what kind of options are available
1499 sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
1500 xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
1502 if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
1504 sal_Int32 nPrivileges = 0;
1505 xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
1507 // Insert Option should be set if insert only otherwise you won't see any rows
1508 // and no insertion is possible
1509 if ((m_nOptionMask & DbGridControlOptions::Insert)
1510 && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & DbGridControlOptions::Insert))
1511 m_nOptions |= DbGridControlOptions::Insert;
1512 if ((m_nOptionMask & DbGridControlOptions::Update)
1513 && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & DbGridControlOptions::Update))
1514 m_nOptions |= DbGridControlOptions::Update;
1515 if ((m_nOptionMask & DbGridControlOptions::Delete)
1516 && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & DbGridControlOptions::Delete))
1517 m_nOptions |= DbGridControlOptions::Delete;
1521 catch( const Exception& )
1523 DBG_UNHANDLED_EXCEPTION("svx");
1526 bool bPermanentCursor = IsPermanentCursorEnabled();
1527 m_nMode = DEFAULT_BROWSE_MODE;
1529 if ( bPermanentCursor )
1531 m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
1532 m_nMode &= ~BrowserMode::HIDECURSOR;
1534 else
1536 // updates are allowed -> no focus rectangle
1537 if ( m_nOptions & DbGridControlOptions::Update )
1538 m_nMode |= BrowserMode::HIDECURSOR;
1541 m_nMode |= BrowserMode::MULTISELECTION;
1543 adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
1545 Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
1546 if (xSupplyColumns.is())
1547 InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
1549 ConnectToFields();
1552 sal_uInt32 nRecordCount(0);
1554 if (m_pSeekCursor)
1556 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1557 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1558 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1560 m_xRowSetListener = new RowSetEventListener(this);
1561 Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
1562 if ( xChangeBroad.is( ) )
1563 xChangeBroad->addRowsChangeListener(m_xRowSetListener);
1566 // insert the currently known rows
1567 // and one row if we are able to insert rows
1568 if (m_nOptions & DbGridControlOptions::Insert)
1570 // insert the empty row for insertion
1571 m_xEmptyRow = new DbGridRow();
1572 ++nRecordCount;
1574 if (nRecordCount)
1576 m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor.get(), true);
1577 m_xDataRow = new DbGridRow(m_pDataCursor.get(), false);
1578 RowInserted(0, nRecordCount, false);
1580 if (m_xSeekRow->IsValid())
1583 m_nSeekPos = m_pSeekCursor->getRow() - 1;
1585 catch( const Exception& )
1587 DBG_UNHANDLED_EXCEPTION("svx");
1588 m_nSeekPos = -1;
1591 else
1593 // no rows so we don't need a seekcursor
1594 m_pSeekCursor.reset();
1598 // go to the old column
1599 if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
1600 nCurPos = 0;
1602 // Column zero is a valid choice and guaranteed to exist,
1603 // but invisible to the user; if we have at least one
1604 // user-visible column, go to that one.
1605 if (nCurPos == 0 && ColCount() > 1)
1606 nCurPos = 1;
1608 // there are rows so go to the selected current column
1609 if (nRecordCount)
1610 GoToRowColumnId(0, GetColumnId(nCurPos));
1611 // else stop the editing if necessary
1612 else if (IsEditing())
1613 DeactivateCell();
1615 // now reset the mode
1616 if (m_nMode != nOldMode)
1617 SetMode(m_nMode);
1619 // RecalcRows was already called while resizing
1620 if (!IsResizing() && GetRowCount())
1621 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1623 m_aBar->InvalidateAll(m_nCurrentPos, true);
1624 SetUpdateMode(true);
1626 // start listening on the seek cursor
1627 if (m_pSeekCursor)
1628 m_pCursorDisposeListener.reset(new DisposeListenerGridBridge(*this, Reference< XComponent > (Reference< XInterface >(*m_pSeekCursor), UNO_QUERY)));
1631 void DbGridControl::RemoveColumns()
1633 if ( IsEditing() )
1634 DeactivateCell();
1636 m_aColumns.clear();
1638 EditBrowseBox::RemoveColumns();
1641 std::unique_ptr<DbGridColumn> DbGridControl::CreateColumn(sal_uInt16 nId) const
1643 return std::unique_ptr<DbGridColumn>(new DbGridColumn(nId, *const_cast<DbGridControl*>(this)));
1646 sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
1648 DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
1649 sal_uInt16 nRealPos = nModelPos;
1650 if (nModelPos != HEADERBAR_APPEND)
1652 // calc the view pos. we can't use our converting functions because the new column
1653 // has no VCL-representation, yet.
1654 sal_Int16 nViewPos = nModelPos;
1655 while (nModelPos--)
1657 if ( m_aColumns[ nModelPos ]->IsHidden() )
1658 --nViewPos;
1660 // restore nModelPos, we need it later
1661 nModelPos = nRealPos;
1662 // the position the base class gets is the view pos + 1 (because of the handle column)
1663 nRealPos = nViewPos + 1;
1666 // calculate the new id
1667 for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && size_t(nId) <= m_aColumns.size(); ++nId)
1669 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !");
1670 // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
1672 EditBrowseBox::AppendColumn(rName, nWidth, nRealPos, nId);
1673 if (nModelPos == HEADERBAR_APPEND)
1674 m_aColumns.push_back( CreateColumn(nId) );
1675 else
1676 m_aColumns.insert( m_aColumns.begin() + nModelPos, CreateColumn(nId) );
1678 return nId;
1681 void DbGridControl::RemoveColumn(sal_uInt16 nId)
1683 EditBrowseBox::RemoveColumn(nId);
1685 const sal_uInt16 nIndex = GetModelColumnPos(nId);
1686 if(nIndex != GRID_COLUMN_NOT_FOUND)
1688 m_aColumns.erase( m_aColumns.begin()+nIndex );
1692 void DbGridControl::ColumnMoved(sal_uInt16 nId)
1694 EditBrowseBox::ColumnMoved(nId);
1696 // remove the col from the model
1697 sal_uInt16 nOldModelPos = GetModelColumnPos(nId);
1698 #ifdef DBG_UTIL
1699 DbGridColumn* pCol = m_aColumns[ nOldModelPos ].get();
1700 DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
1701 #endif
1703 // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
1704 // so the method won't work (in fact it would return the old model pos)
1706 // the new view pos is calculated easily
1707 sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
1709 // from that we can compute the new model pos
1710 size_t nNewModelPos;
1711 for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
1713 if (!m_aColumns[ nNewModelPos ]->IsHidden())
1715 if (!nNewViewPos)
1716 break;
1717 else
1718 --nNewViewPos;
1721 DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
1723 // this will work. of course the model isn't fully consistent with our view right now, but let's
1724 // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
1725 // other case we can use analogue arguments).
1726 // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
1727 // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
1728 // within this range is constant, so we may calculate the view pos from the model pos in the above way.
1730 // for instance, let's look at a grid with six columns where the third one is hidden. this will
1731 // initially look like this :
1733 // +---+---+---+---+---+---+
1734 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1735 // +---+---+---+---+---+---+
1736 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1737 // +---+---+---+---+---+---+
1738 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1739 // +---+---+---+---+---+---+
1741 // if we move the column at (view) pos 1 to (view) pos 3 we have :
1743 // +---+---+---+---+---+---+
1744 // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
1745 // +---+---+---+---+---+---+
1746 // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
1747 // +---+---+---+---+---+---+
1748 // view pos | 0 | 1 | - | 2 | 3 | 4 |
1749 // +---+---+---+---+---+---+
1751 // or, sorted by the out-of-date model positions :
1753 // +---+---+---+---+---+---+
1754 // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
1755 // +---+---+---+---+---+---+
1756 // ID | 1 | 2 | 3 | 4 | 5 | 6 |
1757 // +---+---+---+---+---+---+
1758 // view pos | 0 | 3 | - | 1 | 2 | 4 |
1759 // +---+---+---+---+---+---+
1761 // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
1762 // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
1763 // exactly the pos where we have to re-insert our column's model, so it looks ike this :
1765 // +---+---+---+---+---+---+
1766 // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
1767 // +---+---+---+---+---+---+
1768 // ID | 1 | 3 | 4 | 5 | 2 | 6 |
1769 // +---+---+---+---+---+---+
1770 // view pos | 0 | - | 1 | 2 | 3 | 4 |
1771 // +---+---+---+---+---+---+
1773 // Now, all is consistent again.
1774 // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
1775 // the user expected the latter but there really is no good argument against our method ;) ...)
1777 // And no, this large explanation isn't just because I wanted to play a board game or something like
1778 // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
1779 // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
1781 auto temp = std::move(m_aColumns[ nOldModelPos ]);
1782 m_aColumns.erase( m_aColumns.begin() + nOldModelPos );
1783 m_aColumns.insert( m_aColumns.begin() + nNewModelPos, std::move(temp) );
1786 bool DbGridControl::SeekRow(long nRow)
1788 // in filter mode or in insert only mode we don't have any cursor!
1789 if ( !SeekCursor( nRow ) )
1790 return false;
1792 if ( IsFilterMode() )
1794 DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
1795 m_xPaintRow = m_xEmptyRow;
1797 else
1799 // on the current position we have to take the current row for display as we want
1800 // to have the most recent values for display
1801 if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
1802 m_xPaintRow = m_xCurrentRow;
1803 // seek to the empty insert row
1804 else if ( IsInsertionRow( nRow ) )
1805 m_xPaintRow = m_xEmptyRow;
1806 else
1808 m_xSeekRow->SetState( m_pSeekCursor.get(), true );
1809 m_xPaintRow = m_xSeekRow;
1813 EditBrowseBox::SeekRow(nRow);
1815 return m_nSeekPos >= 0;
1818 // Is called whenever the visible amount of data changes
1819 void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen )
1821 RecalcRows(nNewTopRow, nLinesOnScreen, false);
1824 void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
1826 // If no cursor -> no rows in the browser.
1827 if (!m_pSeekCursor)
1829 DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there");
1830 return;
1833 // ignore any implicitly made updates
1834 bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
1835 if (bDisablePaint)
1836 EnablePaint(false);
1838 // adjust cache to the visible area
1839 Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
1840 sal_Int32 nCacheSize = 0;
1841 xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
1842 bool bCacheAligned = false;
1843 // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
1844 // positioned on the first sentence
1845 long nDelta = nNewTopRow - GetTopRow();
1846 // limit for relative positioning
1847 long nLimit = nCacheSize ? nCacheSize / 2 : 0;
1849 // more lines on screen than in cache
1850 if (nLimit < nLinesOnScreen)
1852 Any aCacheSize;
1853 aCacheSize <<= sal_Int32(nLinesOnScreen*2);
1854 xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
1855 // here we need to update the cursor for sure
1856 bUpdateCursor = true;
1857 bCacheAligned = true;
1858 nLimit = nLinesOnScreen;
1861 // In the following, all positionings are done as it is
1862 // ensured that there are enough lines in the data cache
1864 // window goes downwards with less than two windows difference or
1865 // the cache was updated and no rowcount yet
1866 if (nDelta < nLimit && (nDelta > 0
1867 || (bCacheAligned && m_nTotalCount < 0)) )
1868 SeekCursor(nNewTopRow + nLinesOnScreen - 1);
1869 else if (nDelta < 0 && std::abs(nDelta) < nLimit)
1870 SeekCursor(nNewTopRow);
1871 else if (nDelta != 0 || bUpdateCursor)
1872 SeekCursor(nNewTopRow, true);
1874 AdjustRows();
1876 // ignore any updates implicit made
1877 EnablePaint(true);
1880 void DbGridControl::RowInserted(long nRow, long nNumRows, bool bDoPaint)
1882 if (nNumRows)
1884 if (m_bRecordCountFinal && m_nTotalCount < 0)
1886 // if we have an insert row we have to reduce to count by 1
1887 // as the total count reflects only the existing rows in database
1888 m_nTotalCount = GetRowCount() + nNumRows;
1889 if (m_xEmptyRow.is())
1890 --m_nTotalCount;
1892 else if (m_nTotalCount >= 0)
1893 m_nTotalCount += nNumRows;
1895 EditBrowseBox::RowInserted(nRow, nNumRows, bDoPaint);
1896 m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
1900 void DbGridControl::RowRemoved(long nRow, long nNumRows, bool bDoPaint)
1902 if (nNumRows)
1904 if (m_bRecordCountFinal && m_nTotalCount < 0)
1906 m_nTotalCount = GetRowCount() - nNumRows;
1907 // if we have an insert row reduce by 1
1908 if (m_xEmptyRow.is())
1909 --m_nTotalCount;
1911 else if (m_nTotalCount >= 0)
1912 m_nTotalCount -= nNumRows;
1914 EditBrowseBox::RowRemoved(nRow, nNumRows, bDoPaint);
1915 m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
1919 void DbGridControl::AdjustRows()
1921 if (!m_pSeekCursor)
1922 return;
1924 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
1926 // refresh RecordCount
1927 sal_Int32 nRecordCount = 0;
1928 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1929 if (!m_bRecordCountFinal)
1930 m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
1932 // Did the number of rows change?
1933 // Here we need to consider that there might be an additional row for adding new data sets
1935 // add additional AppendRow for insertion
1936 if (m_nOptions & DbGridControlOptions::Insert)
1937 ++nRecordCount;
1939 // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
1940 if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
1941 m_xCurrentRow->IsNew())
1942 ++nRecordCount;
1943 // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
1944 // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
1945 // and a second time here (60787 - FS)
1947 if (nRecordCount != GetRowCount())
1949 long nDelta = GetRowCount() - static_cast<long>(nRecordCount);
1950 if (nDelta > 0) // too many
1952 RowRemoved(GetRowCount() - nDelta, nDelta, false);
1953 // some rows are gone, thus, repaint starting at the current position
1954 Invalidate();
1956 sal_Int32 nNewPos = AlignSeekCursor();
1957 if (m_bSynchDisplay)
1958 EditBrowseBox::GoToRow(nNewPos);
1960 SetCurrent(nNewPos);
1961 // there are rows so go to the selected current column
1962 if (nRecordCount)
1963 GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
1964 if (!IsResizing() && GetRowCount())
1965 RecalcRows(GetTopRow(), GetVisibleRows(), true);
1966 m_aBar->InvalidateAll(m_nCurrentPos, true);
1968 else // too few
1969 RowInserted(GetRowCount(), -nDelta);
1972 if (m_bRecordCountFinal && m_nTotalCount < 0)
1974 if (m_nOptions & DbGridControlOptions::Insert)
1975 m_nTotalCount = GetRowCount() - 1;
1976 else
1977 m_nTotalCount = GetRowCount();
1979 m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
1982 svt::EditBrowseBox::RowStatus DbGridControl::GetRowStatus(long nRow) const
1984 if (IsFilterRow(nRow))
1985 return EditBrowseBox::FILTER;
1986 else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
1988 // new row
1989 if (!IsValid(m_xCurrentRow))
1990 return EditBrowseBox::DELETED;
1991 else if (IsModified())
1992 return EditBrowseBox::MODIFIED;
1993 else if (m_xCurrentRow->IsNew())
1994 return EditBrowseBox::CURRENTNEW;
1995 else
1996 return EditBrowseBox::CURRENT;
1998 else if (IsInsertionRow(nRow))
1999 return EditBrowseBox::NEW;
2000 else if (!IsValid(m_xSeekRow))
2001 return EditBrowseBox::DELETED;
2002 else
2003 return EditBrowseBox::CLEAN;
2006 void DbGridControl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId) const
2008 if (!IsValid(m_xPaintRow))
2009 return;
2011 size_t Location = GetModelColumnPos(nColumnId);
2012 DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
2013 if (pColumn)
2015 tools::Rectangle aArea(rRect);
2016 if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS)
2018 aArea.AdjustTop(1 );
2019 aArea.AdjustBottom( -1 );
2021 pColumn->Paint(rDev, aArea, m_xPaintRow.get(), getNumberFormatter());
2025 bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol)
2028 DeactivateCell( false );
2030 if ( m_pDataCursor
2031 && ( m_nCurrentPos != nNewRow )
2032 && !SetCurrent( nNewRow )
2035 ActivateCell();
2036 return false;
2039 return EditBrowseBox::CursorMoving( nNewRow, nNewCol );
2042 bool DbGridControl::SetCurrent(long nNewRow)
2044 // Each movement of the datacursor must start with BeginCursorAction and end with
2045 // EndCursorAction to block all notifications during the movement
2046 BeginCursorAction();
2050 // compare positions
2051 if (SeekCursor(nNewRow))
2053 if (IsFilterRow(nNewRow)) // special mode for filtering
2055 m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
2056 m_nCurrentPos = nNewRow;
2058 else
2060 bool bNewRowInserted = false;
2061 // Should we go to the insertrow ?
2062 if (IsInsertionRow(nNewRow))
2064 // to we need to move the cursor to the insert row?
2065 // we need to insert the if the current row isn't the insert row or if the
2066 // cursor triggered the move by itself and we need a reinitialization of the row
2067 Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
2068 if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
2070 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
2071 xUpdateCursor->moveToInsertRow();
2073 bNewRowInserted = true;
2075 else
2078 if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
2080 Any aBookmark = m_pSeekCursor->getBookmark();
2081 if (!m_xCurrentRow.is() || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
2083 // adjust the cursor to the new desired row
2084 if (!m_pDataCursor->moveToBookmark(aBookmark))
2086 EndCursorAction();
2087 return false;
2092 m_xDataRow->SetState(m_pDataCursor.get(), false);
2093 m_xCurrentRow = m_xDataRow;
2095 long nPaintPos = -1;
2096 // do we have to repaint the last regular row in case of setting defaults or autovalues
2097 if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
2098 nPaintPos = m_nCurrentPos;
2100 m_nCurrentPos = nNewRow;
2102 // repaint the new row to display all defaults
2103 if (bNewRowInserted)
2104 RowModified(m_nCurrentPos);
2105 if (nPaintPos >= 0)
2106 RowModified(nPaintPos);
2109 else
2111 OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
2112 EndCursorAction();
2113 return false;
2116 catch ( const Exception& )
2118 DBG_UNHANDLED_EXCEPTION("svx");
2119 EndCursorAction();
2120 return false;
2123 EndCursorAction();
2124 return true;
2127 void DbGridControl::CursorMoved()
2130 // cursor movement due to deletion or insertion of rows
2131 if (m_pDataCursor && m_nCurrentPos != GetCurRow())
2133 DeactivateCell();
2134 SetCurrent(GetCurRow());
2137 EditBrowseBox::CursorMoved();
2138 m_aBar->InvalidateAll(m_nCurrentPos);
2140 // select the new column when they moved
2141 if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
2143 SelectColumnId( GetCurColumnId() );
2146 if ( m_nLastColId != GetCurColumnId() )
2147 onColumnChange();
2148 m_nLastColId = GetCurColumnId();
2150 if ( m_nLastRowId != GetCurRow() )
2151 onRowChange();
2152 m_nLastRowId = GetCurRow();
2155 void DbGridControl::onRowChange()
2157 // not interested in
2160 void DbGridControl::onColumnChange()
2162 if ( m_pGridListener )
2163 m_pGridListener->columnChanged();
2166 void DbGridControl::setDisplaySynchron(bool bSync)
2168 if (bSync != m_bSynchDisplay)
2170 m_bSynchDisplay = bSync;
2171 if (m_bSynchDisplay)
2172 AdjustDataSource();
2176 void DbGridControl::AdjustDataSource(bool bFull)
2178 SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
2179 SolarMutexGuard aGuard;
2180 // If the current row is recalculated at the moment, do not adjust
2182 if (bFull)
2183 m_xCurrentRow = nullptr;
2184 // if we are on the same row only repaint
2185 // but this is only possible for rows which are not inserted, in that case the comparison result
2186 // may not be correct
2187 else
2188 if ( m_xCurrentRow.is()
2189 && !m_xCurrentRow->IsNew()
2190 && !m_pDataCursor->isBeforeFirst()
2191 && !m_pDataCursor->isAfterLast()
2192 && !m_pDataCursor->rowDeleted()
2195 bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
2197 bool bDataCursorIsOnNew = false;
2198 m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
2200 if ( bEqualBookmarks && !bDataCursorIsOnNew )
2202 // position of my data cursor is the same as the position our current row points tpo
2203 // sync the status, repaint, done
2204 DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Errors in the data row");
2205 SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
2206 RowModified(m_nCurrentPos);
2207 return;
2211 // away from the data cursor's row
2212 if (m_xPaintRow == m_xCurrentRow)
2213 m_xPaintRow = m_xSeekRow;
2215 // not up-to-date row, thus, adjust completely
2216 if (!m_xCurrentRow.is())
2217 AdjustRows();
2219 sal_Int32 nNewPos = AlignSeekCursor();
2220 if (nNewPos < 0)// could not find any position
2221 return;
2223 if (nNewPos != m_nCurrentPos)
2225 if (m_bSynchDisplay)
2226 EditBrowseBox::GoToRow(nNewPos);
2228 if (!m_xCurrentRow.is())
2229 // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
2230 // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
2231 // CurrentRow is corrected to point two rows down, so that GoToRow will point into
2232 // emptiness (since we are - purportedly - at the correct position)
2233 SetCurrent(nNewPos);
2235 else
2237 SetCurrent(nNewPos);
2238 RowModified(nNewPos);
2241 // if the data cursor was moved from outside, this section is voided
2242 SetNoSelection();
2243 m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.is());
2246 sal_Int32 DbGridControl::AlignSeekCursor()
2248 // position SeekCursor onto the data cursor, no data transmission
2250 if (!m_pSeekCursor)
2251 return -1;
2253 Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
2255 // now align the seek cursor and the data cursor
2256 if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
2257 m_nSeekPos = GetRowCount() - 1;
2258 else
2262 if ( m_pDataCursor->isBeforeFirst() )
2264 // this is somewhat strange, but can nevertheless happen
2265 SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
2266 m_pSeekCursor->first();
2267 m_pSeekCursor->previous();
2268 m_nSeekPos = -1;
2270 else if ( m_pDataCursor->isAfterLast() )
2272 SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
2273 m_pSeekCursor->last();
2274 m_pSeekCursor->next();
2275 m_nSeekPos = -1;
2277 else
2279 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2280 if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
2281 // unfortunately, moveToBookmark might lead to a re-positioning of the seek
2282 // cursor (if the complex moveToBookmark with all its events fires an update
2283 // somewhere) -> retry
2284 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
2285 // Now there is still the chance of a failure but it is less likely.
2286 // The alternative would be a loop until everything is fine - no good solution...
2287 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2290 catch(Exception&)
2294 return m_nSeekPos;
2297 bool DbGridControl::SeekCursor(long nRow, bool bAbsolute)
2299 // position SeekCursor onto the data cursor, no data transmission
2301 // additions for the filtermode
2302 if (IsFilterRow(nRow))
2304 m_nSeekPos = 0;
2305 return true;
2308 if (!m_pSeekCursor)
2309 return false;
2311 // is this an insertion?
2312 if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
2313 nRow >= m_nCurrentPos)
2315 // if so, scrolling down must be prevented as this is already the last data set!
2316 if (nRow == m_nCurrentPos)
2318 // no adjustment necessary
2319 m_nSeekPos = nRow;
2321 else if (IsInsertionRow(nRow)) // blank row for data insertion
2322 m_nSeekPos = nRow;
2324 else if (IsInsertionRow(nRow)) // blank row for data insertion
2325 m_nSeekPos = nRow;
2326 else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & DbGridControlOptions::Insert) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
2327 m_nSeekPos = nRow;
2328 else
2330 bool bSuccess = false;
2331 long nSteps = 0;
2334 if ( m_pSeekCursor->rowDeleted() )
2336 // somebody deleted the current row of the seek cursor. Move it away from this row.
2337 m_pSeekCursor->next();
2338 if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
2339 bAbsolute = true;
2342 if ( !bAbsolute )
2344 DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
2345 "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
2346 nSteps = nRow - (m_pSeekCursor->getRow() - 1);
2347 bAbsolute = std::abs(nSteps) > 100;
2350 if ( bAbsolute )
2352 bSuccess = m_pSeekCursor->absolute(nRow + 1);
2353 if (bSuccess)
2354 m_nSeekPos = nRow;
2356 else
2358 if (nSteps > 0) // position onto the last needed data set
2360 if (m_pSeekCursor->isAfterLast())
2361 bSuccess = false;
2362 else if (m_pSeekCursor->isBeforeFirst())
2363 bSuccess = m_pSeekCursor->absolute(nSteps);
2364 else
2365 bSuccess = m_pSeekCursor->relative(nSteps);
2367 else if (nSteps < 0)
2369 if (m_pSeekCursor->isBeforeFirst())
2370 bSuccess = false;
2371 else if (m_pSeekCursor->isAfterLast())
2372 bSuccess = m_pSeekCursor->absolute(nSteps);
2373 else
2374 bSuccess = m_pSeekCursor->relative(nSteps);
2376 else
2378 m_nSeekPos = nRow;
2379 return true;
2383 catch(Exception&)
2385 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2390 if (!bSuccess)
2392 if (bAbsolute || nSteps > 0)
2394 if (m_pSeekCursor->isLast())
2395 bSuccess = true;
2396 else
2397 bSuccess = m_pSeekCursor->last();
2399 else
2401 if (m_pSeekCursor->isFirst())
2402 bSuccess = true;
2403 else
2404 bSuccess = m_pSeekCursor->first();
2408 if (bSuccess)
2409 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2410 else
2411 m_nSeekPos = -1;
2413 catch(Exception&)
2415 DBG_UNHANDLED_EXCEPTION("svx");
2416 OSL_FAIL("DbGridControl::SeekCursor : failed ...");
2417 m_nSeekPos = -1; // no further data set available
2420 return m_nSeekPos == nRow;
2423 void DbGridControl::MoveToFirst()
2425 if (m_pSeekCursor && (GetCurRow() != 0))
2426 MoveToPosition(0);
2429 void DbGridControl::MoveToLast()
2431 if (!m_pSeekCursor)
2432 return;
2434 if (m_nTotalCount < 0) // no RecordCount, yet
2438 bool bRes = m_pSeekCursor->last();
2440 if (bRes)
2442 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2443 AdjustRows();
2446 catch(Exception&)
2451 // position onto the last data set not on a blank row
2452 if (m_nOptions & DbGridControlOptions::Insert)
2454 if ((GetRowCount() - 1) > 0)
2455 MoveToPosition(GetRowCount() - 2);
2457 else if (GetRowCount())
2458 MoveToPosition(GetRowCount() - 1);
2461 void DbGridControl::MoveToPrev()
2463 long nNewRow = std::max(GetCurRow() - 1, 0L);
2464 if (GetCurRow() != nNewRow)
2465 MoveToPosition(nNewRow);
2468 void DbGridControl::MoveToNext()
2470 if (!m_pSeekCursor)
2471 return;
2473 if (m_nTotalCount > 0)
2475 // move the data cursor to the right position
2476 long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
2477 if (GetCurRow() != nNewRow)
2478 MoveToPosition(nNewRow);
2480 else
2482 bool bOk = false;
2485 // try to move to next row
2486 // when not possible our paint cursor is already on the last row
2487 // then we must be sure that the data cursor is on the position
2488 // we call ourself again
2489 bOk = m_pSeekCursor->next();
2490 if (bOk)
2492 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2493 MoveToPosition(GetCurRow() + 1);
2496 catch(SQLException &)
2498 DBG_UNHANDLED_EXCEPTION("svx");
2501 if(!bOk)
2503 AdjustRows();
2504 if (m_nTotalCount > 0) // only to avoid infinite recursion
2505 MoveToNext();
2510 void DbGridControl::MoveToPosition(sal_uInt32 nPos)
2512 if (!m_pSeekCursor)
2513 return;
2515 if (m_nTotalCount < 0 && static_cast<long>(nPos) >= GetRowCount())
2519 if (!m_pSeekCursor->absolute(nPos + 1))
2521 AdjustRows();
2522 return;
2524 else
2526 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2527 AdjustRows();
2530 catch(Exception&)
2532 return;
2535 EditBrowseBox::GoToRow(nPos);
2536 m_aBar->InvalidateAll(m_nCurrentPos);
2539 void DbGridControl::AppendNew()
2541 if (!m_pSeekCursor || !(m_nOptions & DbGridControlOptions::Insert))
2542 return;
2544 if (m_nTotalCount < 0) // no RecordCount, yet
2548 bool bRes = m_pSeekCursor->last();
2550 if (bRes)
2552 m_nSeekPos = m_pSeekCursor->getRow() - 1;
2553 AdjustRows();
2556 catch(Exception&)
2558 return;
2562 long nNewRow = m_nTotalCount + 1;
2563 if (nNewRow > 0 && GetCurRow() != nNewRow)
2564 MoveToPosition(nNewRow - 1);
2567 void DbGridControl::SetDesignMode(bool bMode)
2569 if (IsDesignMode() != bMode)
2571 // adjust Enable/Disable for design mode so that the headerbar remains configurable
2572 if (bMode)
2574 if (!IsEnabled())
2576 Enable();
2577 GetDataWindow().Disable();
2580 else
2582 // disable completely
2583 if (!GetDataWindow().IsEnabled())
2584 Disable();
2587 m_bDesignMode = bMode;
2588 GetDataWindow().SetMouseTransparent(bMode);
2589 SetMouseTransparent(bMode);
2591 m_aBar->InvalidateAll(m_nCurrentPos, true);
2595 void DbGridControl::SetFilterMode(bool bMode)
2597 if (IsFilterMode() != bMode)
2599 m_bFilterMode = bMode;
2601 if (bMode)
2603 SetUpdateMode(false);
2605 // there is no cursor anymore
2606 if (IsEditing())
2607 DeactivateCell();
2608 RemoveRows(false);
2610 m_xEmptyRow = new DbGridRow();
2612 // setting the new filter controls
2613 for (auto const & pCurCol : m_aColumns)
2615 if (!pCurCol->IsHidden())
2616 pCurCol->UpdateControl();
2619 // one row for filtering
2620 RowInserted(0);
2621 SetUpdateMode(true);
2623 else
2624 setDataSource(Reference< XRowSet > ());
2628 OUString DbGridControl::GetCellText(long _nRow, sal_uInt16 _nColId) const
2630 size_t Location = GetModelColumnPos( _nColId );
2631 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
2632 OUString sRet;
2633 if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
2634 sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
2635 return sRet;
2638 OUString DbGridControl::GetCurrentRowCellText(DbGridColumn const * pColumn,const DbGridRowRef& _rRow) const
2640 // text output for a single row
2641 OUString aText;
2642 if ( pColumn && IsValid(_rRow) )
2643 aText = pColumn->GetCellText(_rRow.get(), m_xFormatter);
2644 return aText;
2647 sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId)
2649 if (SeekRow(nRow))
2651 size_t Location = GetModelColumnPos( nColId );
2652 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
2653 return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
2655 else
2656 return 30; // FIXME magic number for default cell width
2659 void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu)
2661 bool bDelete = (m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount() && !IsCurrentAppending();
2662 // if only a blank row is selected then do not delete
2663 bDelete = bDelete && !((m_nOptions & DbGridControlOptions::Insert) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
2665 rMenu.EnableItem(rMenu.GetItemId("delete"), bDelete);
2666 rMenu.EnableItem(rMenu.GetItemId("save"), IsModified());
2668 // the undo is more difficult
2669 bool bCanUndo = IsModified();
2670 int nState = -1;
2671 if (m_aMasterStateProvider.IsSet())
2672 nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
2673 bCanUndo &= ( 0 != nState );
2675 rMenu.EnableItem(rMenu.GetItemId("undo"), bCanUndo);
2678 void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& rMenu, sal_uInt16 nExecutionResult)
2680 if (nExecutionResult == rMenu.GetItemId("delete"))
2682 // delete asynchronously
2683 if (m_nDeleteEvent)
2684 Application::RemoveUserEvent(m_nDeleteEvent);
2685 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
2687 else if (nExecutionResult == rMenu.GetItemId("undo"))
2688 Undo();
2689 else if (nExecutionResult == rMenu.GetItemId("save"))
2690 SaveRow();
2693 void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt)
2695 SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
2696 SolarMutexGuard aGuard;
2697 // prop "IsModified" changed ?
2698 // during update don't care about the modified state
2699 if (!IsUpdating() && evt.PropertyName == FM_PROP_ISMODIFIED )
2701 Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
2702 DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
2703 bool bIsNew = false;
2704 if (xSource.is())
2705 bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
2707 if (bIsNew && m_xCurrentRow.is())
2709 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 !");
2710 sal_Int32 nRecordCount = 0;
2711 xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
2712 if (::comphelper::getBOOL(evt.NewValue))
2713 { // modified state changed from sal_False to sal_True and we're on an insert row
2714 // -> we've to add a new grid row
2715 if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
2717 RowInserted(GetRowCount());
2718 InvalidateStatusCell(m_nCurrentPos);
2719 m_aBar->InvalidateAll(m_nCurrentPos);
2722 else
2723 { // modified state changed from sal_True to sal_False and we're on an insert row
2724 // we have two "new row"s at the moment : the one we're editing currently (where the current
2725 // column is the only dirty element) and a "new new" row which is completely clean. As the first
2726 // one is about to be cleaned, too, the second one is obsolete now.
2727 if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
2729 RowRemoved(GetRowCount() - 1);
2730 InvalidateStatusCell(m_nCurrentPos);
2731 m_aBar->InvalidateAll(m_nCurrentPos);
2735 if (m_xCurrentRow.is())
2737 m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean);
2738 m_xCurrentRow->SetNew( bIsNew );
2739 InvalidateStatusCell(m_nCurrentPos);
2740 SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
2745 void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
2747 if (!m_pSeekCursor || IsResizing())
2748 return;
2750 sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rPosPixel.X()));
2751 long nRow = GetRowAtYPosPixel(rPosPixel.Y());
2752 if (nColId != HandleColumnId && nRow >= 0)
2754 if (GetDataWindow().IsMouseCaptured())
2755 GetDataWindow().ReleaseMouse();
2757 size_t Location = GetModelColumnPos( nColId );
2758 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
2759 rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
2760 pTransferable->StartDrag(this, DND_ACTION_COPY);
2764 bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
2766 return (_nRow >= 0)
2767 && (_nRow < GetRowCount())
2768 && (_nColId != HandleColumnId)
2769 && (GetModelColumnPos(_nColId) != GRID_COLUMN_NOT_FOUND);
2772 void DbGridControl::copyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
2774 DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
2775 DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ].get();
2776 SeekRow(_nRow);
2777 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
2780 void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos )
2782 VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/rowsmenu.ui", "");
2783 VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
2785 PreExecuteRowContextMenu( static_cast<sal_uInt16>(_nRow), *aContextMenu );
2786 aContextMenu->RemoveDisabledEntries( true, true );
2787 PostExecuteRowContextMenu( static_cast<sal_uInt16>(_nRow), *aContextMenu, aContextMenu->Execute( this, _rPreferredPos ) );
2790 void DbGridControl::Command(const CommandEvent& rEvt)
2792 switch (rEvt.GetCommand())
2794 case CommandEventId::ContextMenu:
2796 if ( !m_pSeekCursor )
2798 EditBrowseBox::Command(rEvt);
2799 return;
2802 if ( !rEvt.IsMouseEvent() )
2803 { // context menu requested by keyboard
2804 if ( GetSelectRowCount() )
2806 long nRow = FirstSelectedRow( );
2808 ::tools::Rectangle aRowRect( GetRowRectPixel( nRow ) );
2809 executeRowContextMenu( nRow, aRowRect.LeftCenter() );
2811 // handled
2812 return;
2816 sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X()));
2817 long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
2819 if (nColId == HandleColumnId)
2821 executeRowContextMenu( nRow, rEvt.GetMousePosPixel() );
2823 else if (canCopyCellText(nRow, nColId))
2825 VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/cellmenu.ui", "");
2826 VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
2827 if (aContextMenu->Execute(this, rEvt.GetMousePosPixel()))
2828 copyCellText(nRow, nColId);
2830 else
2832 EditBrowseBox::Command(rEvt);
2833 return;
2836 [[fallthrough]];
2838 default:
2839 EditBrowseBox::Command(rEvt);
2843 IMPL_LINK_NOARG(DbGridControl, OnDelete, void*, void)
2845 m_nDeleteEvent = nullptr;
2846 DeleteSelectedRows();
2849 void DbGridControl::DeleteSelectedRows()
2851 DBG_ASSERT(GetSelection(), "no selection!!!");
2853 if (!m_pSeekCursor)
2854 return;
2857 CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId)
2859 if (!IsValid(m_xCurrentRow) || !IsEnabled())
2860 return nullptr;
2862 size_t Location = GetModelColumnPos(nColumnId);
2863 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
2864 if (!pColumn)
2865 return nullptr;
2867 CellController* pReturn = nullptr;
2868 if (IsFilterMode())
2869 pReturn = pColumn->GetController().get();
2870 else
2872 if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
2874 if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
2875 return nullptr;
2878 bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Insert));
2879 bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Update));
2881 if ((bInsert && !pColumn->IsAutoValue()) || bUpdate)
2883 pReturn = pColumn->GetController().get();
2886 return pReturn;
2889 void DbGridControl::CellModified()
2891 SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
2894 ::osl::MutexGuard aGuard(m_aAdjustSafety);
2895 if (m_nAsynAdjustEvent)
2897 SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
2898 RemoveUserEvent(m_nAsynAdjustEvent);
2899 m_nAsynAdjustEvent = nullptr;
2901 // force the call : this should be no problem as we're probably running in the solar thread here
2902 // (cell modified is triggered by user actions)
2903 if (m_bPendingAdjustRows)
2904 AdjustRows();
2905 else
2906 AdjustDataSource();
2910 if (!IsFilterMode() && IsValid(m_xCurrentRow) && !m_xCurrentRow->IsModified())
2912 // enable edit mode
2913 // a data set should be inserted
2914 if (m_xCurrentRow->IsNew())
2916 m_xCurrentRow->SetStatus(GridRowStatus::Modified);
2917 SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
2918 // if no row was added yet, do it now
2919 if (m_nCurrentPos == GetRowCount() - 1)
2921 // increment RowCount
2922 RowInserted(GetRowCount());
2923 InvalidateStatusCell(m_nCurrentPos);
2924 m_aBar->InvalidateAll(m_nCurrentPos);
2927 else if (m_xCurrentRow->GetStatus() != GridRowStatus::Modified)
2929 m_xCurrentRow->SetState(m_pDataCursor.get(), false);
2930 SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
2931 m_xCurrentRow->SetStatus(GridRowStatus::Modified);
2932 SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
2933 InvalidateStatusCell(m_nCurrentPos);
2938 void DbGridControl::Dispatch(sal_uInt16 nId)
2940 if (nId == BROWSER_CURSORENDOFFILE)
2942 if (m_nOptions & DbGridControlOptions::Insert)
2943 AppendNew();
2944 else
2945 MoveToLast();
2947 else
2948 EditBrowseBox::Dispatch(nId);
2951 void DbGridControl::Undo()
2953 if (IsFilterMode() || !IsValid(m_xCurrentRow) || !IsModified())
2954 return;
2956 // check if we have somebody doin' the UNDO for us
2957 int nState = -1;
2958 if (m_aMasterStateProvider.IsSet())
2959 nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
2960 if (nState>0)
2961 { // yes, we have, and the slot is enabled
2962 DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
2963 bool lResult = m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Undo);
2964 if (lResult)
2965 // handled
2966 return;
2968 else if (nState == 0)
2969 // yes, we have, and the slot is disabled
2970 return;
2972 BeginCursorAction();
2974 bool bAppending = m_xCurrentRow->IsNew();
2975 bool bDirty = m_xCurrentRow->IsModified();
2979 // cancel editing
2980 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
2981 // no effects if we're not updating currently
2982 if (bAppending)
2983 // just refresh the row
2984 xUpdateCursor->moveToInsertRow();
2985 else
2986 xUpdateCursor->cancelRowUpdates();
2989 catch(Exception&)
2991 DBG_UNHANDLED_EXCEPTION("svx");
2994 EndCursorAction();
2996 m_xDataRow->SetState(m_pDataCursor.get(), false);
2997 if (m_xPaintRow == m_xCurrentRow)
2998 m_xPaintRow = m_xCurrentRow = m_xDataRow;
2999 else
3000 m_xCurrentRow = m_xDataRow;
3002 if (bAppending && (EditBrowseBox::IsModified() || bDirty))
3003 // remove the row
3004 if (m_nCurrentPos == GetRowCount() - 2)
3005 { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
3006 // caused our data source form to be reset - which should be the usual case...)
3007 RowRemoved(GetRowCount() - 1);
3008 m_aBar->InvalidateAll(m_nCurrentPos);
3011 RowModified(m_nCurrentPos);
3014 void DbGridControl::resetCurrentRow()
3016 if (IsModified())
3018 // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
3019 // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
3020 // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
3021 // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
3022 // would never delete the obsolete "second insert row". Thus in this special case this method here
3023 // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
3024 // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
3025 Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
3026 if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
3028 // are we on a new row currently ?
3029 if (m_xCurrentRow->IsNew())
3031 if (m_nCurrentPos == GetRowCount() - 2)
3033 RowRemoved(GetRowCount() - 1);
3034 m_aBar->InvalidateAll(m_nCurrentPos);
3039 // update the rows
3040 m_xDataRow->SetState(m_pDataCursor.get(), false);
3041 if (m_xPaintRow == m_xCurrentRow)
3042 m_xPaintRow = m_xCurrentRow = m_xDataRow;
3043 else
3044 m_xCurrentRow = m_xDataRow;
3047 RowModified(GetCurRow()); // will update the current controller if affected
3050 void DbGridControl::RowModified( long nRow )
3052 if (nRow == m_nCurrentPos && IsEditing())
3054 CellControllerRef aTmpRef = Controller();
3055 aTmpRef->ClearModified();
3056 InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
3058 EditBrowseBox::RowModified(nRow);
3061 bool DbGridControl::IsModified() const
3063 return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || EditBrowseBox::IsModified());
3066 bool DbGridControl::IsCurrentAppending() const
3068 return m_xCurrentRow.is() && m_xCurrentRow->IsNew();
3071 bool DbGridControl::IsInsertionRow(long nRow) const
3073 return (m_nOptions & DbGridControlOptions::Insert) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
3076 bool DbGridControl::SaveModified()
3078 SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
3079 DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
3080 if (!IsValid(m_xCurrentRow))
3081 return true;
3083 // accept input for this field
3084 // Where there changes at the current input field?
3085 if (!EditBrowseBox::IsModified())
3086 return true;
3088 size_t Location = GetModelColumnPos( GetCurColumnId() );
3089 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
3090 bool bOK = pColumn && pColumn->Commit();
3091 DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
3092 if ( !Controller().is() )
3093 // this might happen if the callbacks implicitly triggered by Commit
3094 // fiddled with the form or the control ...
3095 // (Note that this here is a workaround, at most. We need a general concept how
3096 // to treat this, you can imagine an arbitrary number of scenarios where a callback
3097 // triggers something which leaves us in an expected state.)
3098 // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
3099 return bOK;
3101 if (bOK)
3103 Controller()->ClearModified();
3105 if ( IsValid(m_xCurrentRow) )
3107 m_xCurrentRow->SetState(m_pDataCursor.get(), false);
3108 SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3109 InvalidateStatusCell( m_nCurrentPos );
3111 else
3113 SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
3116 else
3118 // reset the modified flag...
3119 Controller()->SetModified();
3122 return bOK;
3125 bool DbGridControl::SaveRow()
3127 SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
3128 // valid row
3129 if (!IsValid(m_xCurrentRow) || !IsModified())
3130 return true;
3131 // value of the controller was not saved, yet
3132 else if (Controller().is() && Controller()->IsModified())
3134 if (!SaveModified())
3135 return false;
3137 m_bUpdating = true;
3139 BeginCursorAction();
3140 bool bAppending = m_xCurrentRow->IsNew();
3141 bool bSuccess = false;
3144 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
3145 if (bAppending)
3146 xUpdateCursor->insertRow();
3147 else
3148 xUpdateCursor->updateRow();
3149 bSuccess = true;
3151 catch(SQLException&)
3153 EndCursorAction();
3154 m_bUpdating = false;
3155 return false;
3160 if (bSuccess)
3162 // if we are appending we still sit on the insert row
3163 // we don't move just clear the flags not to move on the current row
3164 m_xCurrentRow->SetState(m_pDataCursor.get(), false);
3165 SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
3166 m_xCurrentRow->SetNew(false);
3168 // adjust the seekcursor if it is on the same position as the datacursor
3169 if (m_nSeekPos == m_nCurrentPos || bAppending)
3171 // get the bookmark to refetch the data
3172 // in insert mode we take the new bookmark of the data cursor
3173 Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
3174 m_pSeekCursor->moveToBookmark(aBookmark);
3175 // get the data
3176 m_xSeekRow->SetState(m_pSeekCursor.get(), true);
3177 m_nSeekPos = m_pSeekCursor->getRow() - 1;
3180 // and repaint the row
3181 RowModified(m_nCurrentPos);
3183 catch(Exception&)
3187 m_bUpdating = false;
3188 EndCursorAction();
3190 // The old code returned (nRecords != 0) here.
3191 // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
3192 // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
3193 // is zero, this simply means all fields had their original values.
3194 // FS - 06.12.99 - 70502
3195 return true;
3198 bool DbGridControl::PreNotify(NotifyEvent& rEvt)
3200 // do not handle events of the Navbar
3201 if (m_aBar->IsWindowOrChild(rEvt.GetWindow()))
3202 return BrowseBox::PreNotify(rEvt);
3204 switch (rEvt.GetType())
3206 case MouseNotifyEvent::KEYINPUT:
3208 const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
3210 sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
3211 bool bShift = pKeyEvent->GetKeyCode().IsShift();
3212 bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
3213 bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
3214 if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
3216 // Ctrl-Tab is used to step out of the control, without traveling to the
3217 // remaining cells first
3218 // -> build a new key event without the Ctrl-key, and let the very base class handle it
3219 vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
3220 KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
3222 // call the Control - our direct base class will interpret this in a way we do not want (and do
3223 // a cell traveling)
3224 Control::KeyInput( aNewEvent );
3225 return true;
3228 if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
3230 if (IsModified())
3232 Undo();
3233 return true;
3236 else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
3238 if ((m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount())
3240 // delete asynchronously
3241 if (m_nDeleteEvent)
3242 Application::RemoveUserEvent(m_nDeleteEvent);
3243 m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
3244 return true;
3248 [[fallthrough]];
3250 default:
3251 return EditBrowseBox::PreNotify(rEvt);
3255 bool DbGridControl::IsTabAllowed(bool bRight) const
3257 if (bRight)
3258 // Tab only if not on the _last_ row
3259 return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
3260 GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
3261 else
3263 // Tab only if not on the _first_ row
3264 return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
3268 void DbGridControl::KeyInput( const KeyEvent& rEvt )
3270 if (rEvt.GetKeyCode().GetFunction() == KeyFuncType::COPY)
3272 long nRow = GetCurRow();
3273 sal_uInt16 nColId = GetCurColumnId();
3274 if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
3276 size_t Location = GetModelColumnPos( nColId );
3277 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
3278 OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
3279 return;
3282 EditBrowseBox::KeyInput(rEvt);
3285 void DbGridControl::HideColumn(sal_uInt16 nId)
3287 DeactivateCell();
3289 // determine the col for the focus to set to after removal
3290 sal_uInt16 nPos = GetViewColumnPos(nId);
3291 sal_uInt16 nNewColId = nPos == (ColCount()-1)
3292 ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
3293 : GetColumnIdFromViewPos(nPos+1); // take the next
3295 long lCurrentWidth = GetColumnWidth(nId);
3296 EditBrowseBox::RemoveColumn(nId);
3297 // don't use my own RemoveColumn, this would remove it from m_aColumns, too
3299 // update my model
3300 size_t Location = GetModelColumnPos( nId );
3301 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
3302 DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
3303 if (pColumn)
3305 pColumn->m_bHidden = true;
3306 pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
3309 // and reset the focus
3310 if ( nId == GetCurColumnId() )
3311 GoToColumnId( nNewColId );
3314 void DbGridControl::ShowColumn(sal_uInt16 nId)
3316 sal_uInt16 nPos = GetModelColumnPos(nId);
3317 DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
3318 if (nPos == GRID_COLUMN_NOT_FOUND)
3319 return;
3321 DbGridColumn* pColumn = m_aColumns[ nPos ].get();
3322 if (!pColumn->IsHidden())
3324 DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3325 // if the column isn't marked as hidden, it should be visible, shouldn't it ?
3326 return;
3328 DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3329 // the opposite situation ...
3331 // to determine the new view position we need an adjacent non-hidden column
3332 sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
3333 // first search the cols to the right
3334 for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
3336 DbGridColumn* pCurCol = m_aColumns[ i ].get();
3337 if (!pCurCol->IsHidden())
3339 nNextNonHidden = i;
3340 break;
3343 if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
3345 // then to the left
3346 for ( size_t i = nPos; i > 0; --i )
3348 DbGridColumn* pCurCol = m_aColumns[ i-1 ].get();
3349 if (!pCurCol->IsHidden())
3351 nNextNonHidden = i-1;
3352 break;
3356 sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
3357 ? 1 // there is no visible column -> insert behind the handle col
3358 : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
3359 // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
3360 // a position 1 for the first non-handle col -> +1
3361 DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
3362 // we found a col marked as visible but got no view pos for it ...
3364 if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
3365 // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
3366 ++nNewViewPos;
3368 DeactivateCell();
3370 OUString aName;
3371 pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
3372 InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE, nNewViewPos);
3373 pColumn->m_bHidden = false;
3375 ActivateCell();
3376 Invalidate();
3379 sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
3381 if (nPos >= m_aColumns.size())
3383 OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
3384 return GRID_COLUMN_NOT_FOUND;
3387 DbGridColumn* pCol = m_aColumns[ nPos ].get();
3388 #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
3389 // in the debug version, we convert the ModelPos into a ViewPos and compare this with the
3390 // value we will return (nId at the corresponding Col in m_aColumns)
3392 if (!pCol->IsHidden())
3393 { // makes sense only if the column is visible
3394 sal_uInt16 nViewPos = nPos;
3395 for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
3396 if ( m_aColumns[ i ]->IsHidden())
3397 --nViewPos;
3399 DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
3400 "DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?");
3402 #endif
3403 return pCol->GetId();
3406 sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
3408 for ( size_t i = 0; i < m_aColumns.size(); ++i )
3409 if ( m_aColumns[ i ]->GetId() == nId )
3410 return i;
3412 return GRID_COLUMN_NOT_FOUND;
3415 void DbGridControl::implAdjustInSolarThread(bool _bRows)
3417 SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
3418 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3419 if (!Application::IsMainThread())
3421 m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true);
3422 m_bPendingAdjustRows = _bRows;
3423 if (_bRows)
3424 SAL_INFO("svx.fmcomp", "posting an AdjustRows");
3425 else
3426 SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
3428 else
3430 if (_bRows)
3431 SAL_INFO("svx.fmcomp", "doing an AdjustRows");
3432 else
3433 SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
3434 // always adjust the rows before adjusting the data source
3435 // If this is not necessary (because the row count did not change), nothing is done
3436 // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
3437 // to a position behind row count know 'til now, the cursorMoved notification may come before the
3438 // RowCountChanged notification
3439 // 94093 - 02.11.2001 - frank.schoenheit@sun.com
3440 AdjustRows();
3442 if ( !_bRows )
3443 AdjustDataSource();
3447 IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat, void)
3449 m_nAsynAdjustEvent = nullptr;
3451 AdjustRows();
3452 // see implAdjustInSolarThread for a comment why we do this every time
3454 if ( !pAdjustWhat )
3455 AdjustDataSource();
3458 void DbGridControl::BeginCursorAction()
3460 if (m_pFieldListeners)
3462 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3463 for (const auto& rListener : *pListeners)
3465 GridFieldValueListener* pCurrent = rListener.second;
3466 if (pCurrent)
3467 pCurrent->suspend();
3471 if (m_pDataSourcePropListener)
3472 m_pDataSourcePropListener->suspend();
3475 void DbGridControl::EndCursorAction()
3477 if (m_pFieldListeners)
3479 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3480 for (const auto& rListener : *pListeners)
3482 GridFieldValueListener* pCurrent = rListener.second;
3483 if (pCurrent)
3484 pCurrent->resume();
3488 if (m_pDataSourcePropListener)
3489 m_pDataSourcePropListener->resume();
3492 void DbGridControl::ConnectToFields()
3494 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3495 DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
3497 if (!pListeners)
3499 pListeners = new ColumnFieldValueListeners;
3500 m_pFieldListeners = pListeners;
3503 for (auto const & pCurrent : m_aColumns)
3505 sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
3506 if (GRID_COLUMN_NOT_FOUND == nViewPos)
3507 continue;
3509 Reference< XPropertySet > xField = pCurrent->GetField();
3510 if (!xField.is())
3511 continue;
3513 // column is visible and bound here
3514 GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
3515 DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
3516 rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
3520 void DbGridControl::DisconnectFromFields()
3522 if (!m_pFieldListeners)
3523 return;
3525 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3526 while (!pListeners->empty())
3528 sal_Int32 nOldSize = pListeners->size();
3529 pListeners->begin()->second->dispose();
3530 DBG_ASSERT(nOldSize > static_cast<sal_Int32>(pListeners->size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
3533 delete pListeners;
3534 m_pFieldListeners = nullptr;
3537 void DbGridControl::FieldValueChanged(sal_uInt16 _nId)
3539 osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
3540 // needed as this may run in a thread other than the main one
3541 if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED)
3542 // all other cases are handled elsewhere
3543 return;
3545 size_t Location = GetModelColumnPos( _nId );
3546 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
3547 if (pColumn)
3549 std::unique_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard;
3550 while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired()))
3551 pGuard.reset(new vcl::SolarMutexTryAndBuyGuard);
3553 if (m_bWantDestruction)
3554 { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
3555 // => don't do anything
3556 // 73365 - 23.02.00 - FS
3557 return;
3560 // and finally do the update ...
3561 pColumn->UpdateFromField(m_xCurrentRow.get(), m_xFormatter);
3562 RowModified(GetCurRow());
3566 void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
3568 ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
3569 if (!pListeners)
3571 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
3572 return;
3575 ColumnFieldValueListeners::const_iterator aPos = pListeners->find(_nId);
3576 if (aPos == pListeners->end())
3578 OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
3579 return;
3582 delete aPos->second;
3584 pListeners->erase(aPos);
3587 void DbGridControl::disposing(sal_uInt16 _nId)
3589 if (_nId == 0)
3590 { // the seek cursor is being disposed
3591 ::osl::MutexGuard aGuard(m_aAdjustSafety);
3592 setDataSource(nullptr, DbGridControlOptions::Readonly); // our clone was disposed so we set our datasource to null to avoid later access to it
3593 if (m_nAsynAdjustEvent)
3595 RemoveUserEvent(m_nAsynAdjustEvent);
3596 m_nAsynAdjustEvent = nullptr;
3601 sal_Int32 DbGridControl::GetAccessibleControlCount() const
3603 return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control
3606 Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
3608 Reference<XAccessible > xRet;
3609 if ( _nIndex == EditBrowseBox::GetAccessibleControlCount() )
3611 xRet = m_aBar->GetAccessible();
3613 else
3614 xRet = EditBrowseBox::CreateAccessibleControl( _nIndex );
3615 return xRet;
3618 Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
3620 sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
3621 size_t Location = GetModelColumnPos(nColumnId);
3622 DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
3623 if ( pColumn )
3625 Reference< css::awt::XControl> xInt(pColumn->GetCell());
3626 Reference< css::awt::XCheckBox> xBox(xInt,UNO_QUERY);
3627 if ( xBox.is() )
3629 TriState eValue = TRISTATE_FALSE;
3630 switch( xBox->getState() )
3632 case 0:
3633 eValue = TRISTATE_FALSE;
3634 break;
3635 case 1:
3636 eValue = TRISTATE_TRUE;
3637 break;
3638 case 2:
3639 eValue = TRISTATE_INDET;
3640 break;
3642 return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
3645 return EditBrowseBox::CreateAccessibleCell( _nRow, _nColumnPos );
3648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */