From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- svx/source/fmcomp/gridctrl.cxx | 3396 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3396 insertions(+) create mode 100644 svx/source/fmcomp/gridctrl.cxx (limited to 'svx/source/fmcomp/gridctrl.cxx') diff --git a/svx/source/fmcomp/gridctrl.cxx b/svx/source/fmcomp/gridctrl.cxx new file mode 100644 index 000000000..ab8f128a7 --- /dev/null +++ b/svx/source/fmcomp/gridctrl.cxx @@ -0,0 +1,3396 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::dbtools; +using namespace ::dbtools::DBTypeConversion; +using namespace ::svxform; +using namespace ::svt; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::container; +using namespace com::sun::star::accessibility; + +#define ROWSTATUS(row) (!row.is() ? "NULL" : row->GetStatus() == GridRowStatus::Clean ? "CLEAN" : row->GetStatus() == GridRowStatus::Modified ? "MODIFIED" : row->GetStatus() == GridRowStatus::Deleted ? "DELETED" : "INVALID") + +constexpr auto DEFAULT_BROWSE_MODE = + BrowserMode::COLUMNSELECTION + | BrowserMode::MULTISELECTION + | BrowserMode::KEEPHIGHLIGHT + | BrowserMode::TRACKING_TIPS + | BrowserMode::HLINES + | BrowserMode::VLINES + | BrowserMode::HEADERBAR_NEW; + +class RowSetEventListener : public ::cppu::WeakImplHelper +{ + VclPtr m_pControl; +public: + explicit RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl) + { + } + +private: + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& /*i_aEvt*/) override + { + } + virtual void SAL_CALL rowsChanged(const css::sdb::RowsChangeEvent& i_aEvt) override + { + if ( i_aEvt.Action != RowChangeAction::UPDATE ) + return; + + ::DbGridControl::GrantControlAccess aAccess; + CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess); + const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess); + for(const Any& rBookmark : i_aEvt.Bookmarks) + { + pSeek->moveToBookmark(rBookmark); + // get the data + rSeekRow->SetState(pSeek, true); + sal_Int32 nSeekPos = pSeek->getRow() - 1; + m_pControl->SetSeekPos(nSeekPos,aAccess); + m_pControl->RowModified(nSeekPos); + } + } +}; + +class GridFieldValueListener; +typedef std::map ColumnFieldValueListeners; + +class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener +{ + osl::Mutex m_aMutex; + DbGridControl& m_rParent; + rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_pRealListener; + sal_uInt16 m_nId; + sal_Int16 m_nSuspended; + bool m_bDisposed : 1; + +public: + GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId); + virtual ~GridFieldValueListener() override; + + virtual void _propertyChanged(const PropertyChangeEvent& evt) override; + + void suspend() { ++m_nSuspended; } + void resume() { --m_nSuspended; } + + void dispose(); +}; + +GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId) + :OPropertyChangeListener(m_aMutex) + ,m_rParent(_rParent) + ,m_nId(_nId) + ,m_nSuspended(0) + ,m_bDisposed(false) +{ + if (_rField.is()) + { + m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField); + m_pRealListener->addProperty(FM_PROP_VALUE); + } +} + +GridFieldValueListener::~GridFieldValueListener() +{ + dispose(); +} + +void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& /*_evt*/) +{ + DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !"); + if (m_nSuspended <= 0) + m_rParent.FieldValueChanged(m_nId); +} + +void GridFieldValueListener::dispose() +{ + if (m_bDisposed) + { + DBG_ASSERT(!m_pRealListener, "GridFieldValueListener::dispose : inconsistent !"); + return; + } + + if (m_pRealListener.is()) + { + m_pRealListener->dispose(); + m_pRealListener.clear(); + } + + m_bDisposed = true; + m_rParent.FieldListenerDisposing(m_nId); +} + +class DisposeListenerGridBridge : public FmXDisposeListener +{ + DbGridControl& m_rParent; + rtl::Reference m_xRealListener; + +public: + DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject); + virtual ~DisposeListenerGridBridge() override; + + virtual void disposing(sal_Int16 _nId) override { m_rParent.disposing(_nId); } +}; + +DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject) + :FmXDisposeListener() + ,m_rParent(_rParent) +{ + + if (_rxObject.is()) + { + m_xRealListener = new FmXDisposeMultiplexer(this, _rxObject); + } +} + +DisposeListenerGridBridge::~DisposeListenerGridBridge() +{ + if (m_xRealListener.is()) + { + m_xRealListener->dispose(); + } +} + +const DbGridControlNavigationBarState ControlMap[] = + { + DbGridControlNavigationBarState::Text, + DbGridControlNavigationBarState::Absolute, + DbGridControlNavigationBarState::Of, + DbGridControlNavigationBarState::Count, + DbGridControlNavigationBarState::First, + DbGridControlNavigationBarState::Next, + DbGridControlNavigationBarState::Prev, + DbGridControlNavigationBarState::Last, + DbGridControlNavigationBarState::New, + DbGridControlNavigationBarState::NONE + }; + +bool CompareBookmark(const Any& aLeft, const Any& aRight) +{ + return aLeft == aRight; +} + +class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener +{ + VclPtr m_pParent; + + // a DbGridControl has no mutex, so we use our own as the base class expects one + osl::Mutex m_aMutex; + sal_Int16 m_nSuspended; + +public: + explicit FmXGridSourcePropListener(DbGridControl* _pParent); + + void suspend() { ++m_nSuspended; } + void resume() { --m_nSuspended; } + + virtual void _propertyChanged(const PropertyChangeEvent& evt) override; +}; + +FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent) + :OPropertyChangeListener(m_aMutex) + ,m_pParent(_pParent) + ,m_nSuspended(0) +{ + DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !"); +} + +void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) +{ + DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !"); + if (m_nSuspended <= 0) + m_pParent->DataSourcePropertyChanged(evt); +} + +const int nReserveNumDigits = 7; + +NavigationBar::AbsolutePos::AbsolutePos(std::unique_ptr xEntry, NavigationBar* pBar) + : RecordItemWindowBase(std::move(xEntry)) + , m_xParent(pBar) +{ +} + +bool NavigationBar::AbsolutePos::DoKeyInput(const KeyEvent& rEvt) +{ + if (rEvt.GetKeyCode() == KEY_TAB) + { + m_xParent->GetParent()->GrabFocus(); + return true; + } + return RecordItemWindowBase::DoKeyInput(rEvt); +} + +void NavigationBar::AbsolutePos::PositionFired(sal_Int64 nRecord) +{ + m_xParent->PositionDataSource(nRecord); + m_xParent->InvalidateState(DbGridControlNavigationBarState::Absolute); +} + +void NavigationBar::PositionDataSource(sal_Int32 nRecord) +{ + if (m_bPositioning) + return; + // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition, + // so protect against this recursion + m_bPositioning = true; + static_cast(GetParent())->MoveToPosition(nRecord - 1); + m_bPositioning = false; +} + +NavigationBar::NavigationBar(vcl::Window* pParent) + : InterimItemWindow(pParent, "svx/ui/navigationbar.ui", "NavigationBar") + , m_xRecordText(m_xBuilder->weld_label("recordtext")) + , m_xAbsolute(new NavigationBar::AbsolutePos(m_xBuilder->weld_entry("entry-noframe"), this)) + , m_xRecordOf(m_xBuilder->weld_label("recordof")) + , m_xRecordCount(m_xBuilder->weld_label("recordcount")) + , m_xFirstBtn(m_xBuilder->weld_button("first")) + , m_xPrevBtn(m_xBuilder->weld_button("prev")) + , m_xNextBtn(m_xBuilder->weld_button("next")) + , m_xLastBtn(m_xBuilder->weld_button("last")) + , m_xNewBtn(m_xBuilder->weld_button("new")) + , m_xPrevRepeater(std::make_shared(*m_xPrevBtn, LINK(this,NavigationBar,OnClick))) + , m_xNextRepeater(std::make_shared(*m_xNextBtn, LINK(this,NavigationBar,OnClick))) + , m_nCurrentPos(-1) + , m_bPositioning(false) +{ + vcl::Font aApplFont(Application::GetSettings().GetStyleSettings().GetToolFont()); + m_xAbsolute->set_font(aApplFont); + aApplFont.SetTransparent(true); + m_xRecordText->set_font(aApplFont); + m_xRecordOf->set_font(aApplFont); + m_xRecordCount->set_font(aApplFont); + + m_xFirstBtn->set_help_id(HID_GRID_TRAVEL_FIRST); + m_xPrevBtn->set_help_id(HID_GRID_TRAVEL_PREV); + m_xNextBtn->set_help_id(HID_GRID_TRAVEL_NEXT); + m_xLastBtn->set_help_id(HID_GRID_TRAVEL_LAST); + m_xNewBtn->set_help_id(HID_GRID_TRAVEL_NEW); + m_xAbsolute->set_help_id(HID_GRID_TRAVEL_ABSOLUTE); + m_xRecordCount->set_help_id(HID_GRID_NUMBEROFRECORDS); + + // set handlers for buttons + m_xFirstBtn->connect_clicked(LINK(this,NavigationBar,OnClick)); + m_xLastBtn->connect_clicked(LINK(this,NavigationBar,OnClick)); + m_xNewBtn->connect_clicked(LINK(this,NavigationBar,OnClick)); + + m_xRecordText->set_label(SvxResId(RID_STR_REC_TEXT)); + m_xRecordOf->set_label(SvxResId(RID_STR_REC_FROM_TEXT)); + m_xRecordCount->set_label(OUString('?')); + + auto nReserveWidth = m_xRecordCount->get_approximate_digit_width() * nReserveNumDigits; + m_xAbsolute->GetWidget()->set_size_request(nReserveWidth, -1); + m_xRecordCount->set_size_request(nReserveWidth, -1); +} + +NavigationBar::~NavigationBar() +{ + disposeOnce(); +} + +void NavigationBar::dispose() +{ + m_xRecordText.reset(); + m_xAbsolute.reset(); + m_xRecordOf.reset(); + m_xRecordCount.reset(); + m_xFirstBtn.reset(); + m_xPrevBtn.reset(); + m_xNextBtn.reset(); + m_xLastBtn.reset(); + m_xNewBtn.reset(); + InterimItemWindow::dispose(); +} + +sal_uInt16 NavigationBar::ArrangeControls() +{ + return m_xContainer->get_preferred_size().Width(); +} + +IMPL_LINK(NavigationBar, OnClick, weld::Button&, rButton, void) +{ + DbGridControl* pParent = static_cast(GetParent()); + + if (pParent->m_aMasterSlotExecutor.IsSet()) + { + bool lResult = false; + if (&rButton == m_xFirstBtn.get()) + lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::First); + else if( &rButton == m_xPrevBtn.get() ) + lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Prev); + else if( &rButton == m_xNextBtn.get() ) + lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Next); + else if( &rButton == m_xLastBtn.get() ) + lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Last); + else if( &rButton == m_xNewBtn.get() ) + lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::New); + + if (lResult) + // the link already handled it + return; + } + + if (&rButton == m_xFirstBtn.get()) + pParent->MoveToFirst(); + else if( &rButton == m_xPrevBtn.get() ) + pParent->MoveToPrev(); + else if( &rButton == m_xNextBtn.get() ) + pParent->MoveToNext(); + else if( &rButton == m_xLastBtn.get() ) + pParent->MoveToLast(); + else if( &rButton == m_xNewBtn.get() ) + pParent->AppendNew(); +} + +void NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll) +{ + if (!(m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll)) + return; + + DbGridControl* pParent = static_cast(GetParent()); + + sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControlOptions::Insert) ? 2 : 1); + + // check if everything needs to be invalidated + bAll = bAll || m_nCurrentPos <= 0; + bAll = bAll || nCurrentPos <= 0; + bAll = bAll || m_nCurrentPos >= nAdjustedRowCount; + bAll = bAll || nCurrentPos >= nAdjustedRowCount; + + if ( bAll ) + { + m_nCurrentPos = nCurrentPos; + int i = 0; + while (ControlMap[i] != DbGridControlNavigationBarState::NONE) + SetState(ControlMap[i++]); + } + else // is in the center + { + m_nCurrentPos = nCurrentPos; + SetState(DbGridControlNavigationBarState::Count); + SetState(DbGridControlNavigationBarState::Absolute); + } +} + +bool NavigationBar::GetState(DbGridControlNavigationBarState nWhich) const +{ + DbGridControl* pParent = static_cast(GetParent()); + + if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled() + || pParent->IsFilterMode() ) + return false; + else + { + // check if we have a master state provider + if (pParent->m_aMasterStateProvider.IsSet()) + { + tools::Long nState = pParent->m_aMasterStateProvider.Call( nWhich ); + if (nState>=0) + return (nState>0); + } + + bool bAvailable = true; + + switch (nWhich) + { + case DbGridControlNavigationBarState::First: + case DbGridControlNavigationBarState::Prev: + bAvailable = m_nCurrentPos > 0; + break; + case DbGridControlNavigationBarState::Next: + if(pParent->m_bRecordCountFinal) + { + bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1; + if (!bAvailable && pParent->GetOptions() & DbGridControlOptions::Insert) + bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified(); + } + break; + case DbGridControlNavigationBarState::Last: + if(pParent->m_bRecordCountFinal) + { + if (pParent->GetOptions() & DbGridControlOptions::Insert) + bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 : + m_nCurrentPos != pParent->GetRowCount() - 2; + else + bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1; + } + break; + case DbGridControlNavigationBarState::New: + bAvailable = (pParent->GetOptions() & DbGridControlOptions::Insert) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1; + break; + case DbGridControlNavigationBarState::Absolute: + bAvailable = pParent->GetRowCount() > 0; + break; + default: break; + } + return bAvailable; + } +} + +void NavigationBar::SetState(DbGridControlNavigationBarState nWhich) +{ + bool bAvailable = GetState(nWhich); + DbGridControl* pParent = static_cast(GetParent()); + weld::Widget* pWnd = nullptr; + switch (nWhich) + { + case DbGridControlNavigationBarState::First: + pWnd = m_xFirstBtn.get(); + break; + case DbGridControlNavigationBarState::Prev: + pWnd = m_xPrevBtn.get(); + break; + case DbGridControlNavigationBarState::Next: + pWnd = m_xNextBtn.get(); + break; + case DbGridControlNavigationBarState::Last: + pWnd = m_xLastBtn.get(); + break; + case DbGridControlNavigationBarState::New: + pWnd = m_xNewBtn.get(); + break; + case DbGridControlNavigationBarState::Absolute: + pWnd = m_xAbsolute->GetWidget(); + if (bAvailable) + m_xAbsolute->set_text(OUString::number(m_nCurrentPos + 1)); + else + m_xAbsolute->set_text(OUString()); + break; + case DbGridControlNavigationBarState::Text: + pWnd = m_xRecordText.get(); + break; + case DbGridControlNavigationBarState::Of: + pWnd = m_xRecordOf.get(); + break; + case DbGridControlNavigationBarState::Count: + { + pWnd = m_xRecordCount.get(); + OUString aText; + if (bAvailable) + { + if (pParent->GetOptions() & DbGridControlOptions::Insert) + { + if (pParent->IsCurrentAppending() && !pParent->IsModified()) + aText = OUString::number(pParent->GetRowCount()); + else + aText = OUString::number(pParent->GetRowCount() - 1); + } + else + aText = OUString::number(pParent->GetRowCount()); + if(!pParent->m_bRecordCountFinal) + aText += " *"; + } + else + aText.clear(); + + // add the number of selected rows, if applicable + if (pParent->GetSelectRowCount()) + { + OUString aExtendedInfo = aText + " (" + + OUString::number(pParent->GetSelectRowCount()) + ")"; + m_xRecordCount->set_label(aExtendedInfo); + } + else + m_xRecordCount->set_label(aText); + + pParent->SetRealRowCount(aText); + } break; + default: break; + } + DBG_ASSERT(pWnd, "no window"); + if (!(pWnd && (pWnd->get_sensitive() != bAvailable))) + return; + + // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user + // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we + // do this check. + // For further explanation see Bug 69900. + pWnd->set_sensitive(bAvailable); + if (!bAvailable) + { + if (pWnd == m_xNextBtn.get()) + m_xNextRepeater->Stop(); + else if (pWnd == m_xPrevBtn.get()) + m_xPrevRepeater->Stop(); + } +} + +DbGridRow::DbGridRow():m_eStatus(GridRowStatus::Clean), m_bIsNew(true) +{} + +DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor) + :m_bIsNew(false) +{ + + if (pCur && pCur->Is()) + { + Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY); + for (sal_Int32 i = 0; i < xColumns->getCount(); ++i) + { + Reference< XPropertySet > xColSet( + xColumns->getByIndex(i), css::uno::UNO_QUERY); + m_aVariants.emplace_back( new DataColumn(xColSet) ); + } + + if (pCur->rowDeleted()) + m_eStatus = GridRowStatus::Deleted; + else + { + if (bPaintCursor) + m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GridRowStatus::Invalid : GridRowStatus::Clean; + else + { + const Reference< XPropertySet >& xSet = pCur->getPropertySet(); + if (xSet.is()) + { + m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)); + if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst())) + m_eStatus = GridRowStatus::Invalid; + else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED))) + m_eStatus = GridRowStatus::Modified; + else + m_eStatus = GridRowStatus::Clean; + } + else + m_eStatus = GridRowStatus::Invalid; + } + } + if (!m_bIsNew && IsValid()) + m_aBookmark = pCur->getBookmark(); + else + m_aBookmark = Any(); + } + else + m_eStatus = GridRowStatus::Invalid; +} + +DbGridRow::~DbGridRow() +{ +} + +void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor) +{ + if (pCur && pCur->Is()) + { + if (pCur->rowDeleted()) + { + m_eStatus = GridRowStatus::Deleted; + m_bIsNew = false; + } + else + { + m_eStatus = GridRowStatus::Clean; + if (!bPaintCursor) + { + const Reference< XPropertySet >& xSet = pCur->getPropertySet(); + DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !"); + + if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED))) + m_eStatus = GridRowStatus::Modified; + m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)); + } + else + m_bIsNew = false; + } + + try + { + if (!m_bIsNew && IsValid()) + m_aBookmark = pCur->getBookmark(); + else + m_aBookmark = Any(); + } + catch(SQLException&) + { + DBG_UNHANDLED_EXCEPTION("svx"); + m_aBookmark = Any(); + m_eStatus = GridRowStatus::Invalid; + m_bIsNew = false; + } + } + else + { + m_aBookmark = Any(); + m_eStatus = GridRowStatus::Invalid; + m_bIsNew = false; + } +} + +DbGridControl::DbGridControl( + Reference< XComponentContext > const & _rxContext, + vcl::Window* pParent, + WinBits nBits) + :EditBrowseBox(pParent, EditBrowseBoxFlags::NONE, nBits, DEFAULT_BROWSE_MODE ) + ,m_xContext(_rxContext) + ,m_aBar(VclPtr::Create(this)) + ,m_nAsynAdjustEvent(nullptr) + ,m_pDataSourcePropListener(nullptr) + ,m_pFieldListeners(nullptr) + ,m_pGridListener(nullptr) + ,m_nSeekPos(-1) + ,m_nTotalCount(-1) + ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate()) + ,m_nMode(DEFAULT_BROWSE_MODE) + ,m_nCurrentPos(-1) + ,m_nDeleteEvent(nullptr) + ,m_nOptions(DbGridControlOptions::Readonly) + ,m_nOptionMask(DbGridControlOptions::Insert | DbGridControlOptions::Update | DbGridControlOptions::Delete) + ,m_nLastColId(sal_uInt16(-1)) + ,m_nLastRowId(-1) + ,m_bDesignMode(false) + ,m_bRecordCountFinal(false) + ,m_bNavigationBar(true) + ,m_bSynchDisplay(true) + ,m_bHandle(true) + ,m_bFilterMode(false) + ,m_bWantDestruction(false) + ,m_bPendingAdjustRows(false) + ,m_bHideScrollbars( false ) + ,m_bUpdating(false) +{ + + OUString sName(SvxResId(RID_STR_NAVIGATIONBAR)); + m_aBar->SetAccessibleName(sName); + m_aBar->Show(); + ImplInitWindow( InitWindowFacet::All ); +} + +void DbGridControl::InsertHandleColumn() +{ + // BrowseBox has problems when painting without a handleColumn (hide it here) + if (HasHandle()) + BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString())); + else + BrowseBox::InsertHandleColumn(0); +} + +void DbGridControl::Init() +{ + VclPtr pNewHeader = CreateHeaderBar(this); + pHeader->SetMouseTransparent(false); + + SetHeaderBar(pNewHeader); + SetMode(m_nMode); + SetCursorColor(Color(0xFF, 0, 0)); + + InsertHandleColumn(); +} + +DbGridControl::~DbGridControl() +{ + disposeOnce(); +} + +void DbGridControl::dispose() +{ + RemoveColumns(); + + m_bWantDestruction = true; + osl::MutexGuard aGuard(m_aDestructionSafety); + if (m_pFieldListeners) + DisconnectFromFields(); + m_pCursorDisposeListener.reset(); + + if (m_nDeleteEvent) + Application::RemoveUserEvent(m_nDeleteEvent); + + if (m_pDataSourcePropMultiplexer.is()) + { + m_pDataSourcePropMultiplexer->dispose(); + m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer + delete m_pDataSourcePropListener; + m_pDataSourcePropListener = nullptr; + } + m_xRowSetListener.clear(); + + m_pDataCursor.reset(); + m_pSeekCursor.reset(); + + m_aBar.disposeAndClear(); + + EditBrowseBox::dispose(); +} + +void DbGridControl::StateChanged( StateChangedType nType ) +{ + EditBrowseBox::StateChanged( nType ); + + switch (nType) + { + case StateChangedType::Mirroring: + ImplInitWindow( InitWindowFacet::WritingMode ); + Invalidate(); + break; + + case StateChangedType::Zoom: + { + ImplInitWindow( InitWindowFacet::Font ); + + // and give it a chance to rearrange + Point aPoint = GetControlArea().TopLeft(); + sal_uInt16 nX = static_cast(aPoint.X()); + ArrangeControls(nX, static_cast(aPoint.Y())); + ReserveControlArea(nX); + } + break; + case StateChangedType::ControlFont: + ImplInitWindow( InitWindowFacet::Font ); + Invalidate(); + break; + case StateChangedType::ControlForeground: + ImplInitWindow( InitWindowFacet::Foreground ); + Invalidate(); + break; + case StateChangedType::ControlBackground: + ImplInitWindow( InitWindowFacet::Background ); + Invalidate(); + break; + default:; + } +} + +void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt ) +{ + EditBrowseBox::DataChanged( rDCEvt ); + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS ) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + ImplInitWindow( InitWindowFacet::All ); + Invalidate(); + } +} + +void DbGridControl::Select() +{ + EditBrowseBox::Select(); + + // as the selected rows may have changed, update the according display in our navigation bar + m_aBar->InvalidateState(DbGridControlNavigationBarState::Count); + + if (m_pGridListener) + m_pGridListener->selectionChanged(); +} + +void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat ) +{ + for (auto const & pCol : m_aColumns) + { + pCol->ImplInitWindow( GetDataWindow(), _eInitWhat ); + } + + if ( _eInitWhat & InitWindowFacet::WritingMode ) + { + if ( m_bNavigationBar ) + { + m_aBar->EnableRTL( IsRTLEnabled() ); + } + } + + if ( _eInitWhat & InitWindowFacet::Font ) + { + if ( m_bNavigationBar ) + { + if ( IsControlFont() ) + m_aBar->SetControlFont( GetControlFont() ); + else + m_aBar->SetControlFont(); + + m_aBar->SetZoom( GetZoom() ); + } + } + + if ( !(_eInitWhat & InitWindowFacet::Background) ) + return; + + if (IsControlBackground()) + { + GetDataWindow().SetBackground(GetControlBackground()); + GetDataWindow().SetControlBackground(GetControlBackground()); + GetDataWindow().GetOutDev()->SetFillColor(GetControlBackground()); + } + else + { + GetDataWindow().SetControlBackground(); + GetDataWindow().GetOutDev()->SetFillColor(GetOutDev()->GetFillColor()); + } +} + +void DbGridControl::RemoveRows(bool bNewCursor) +{ + // Did the data cursor change? + if (!bNewCursor) + { + m_pSeekCursor.reset(); + m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr; + m_nCurrentPos = m_nSeekPos = -1; + m_nOptions = DbGridControlOptions::Readonly; + + RowRemoved(0, GetRowCount(), false); + m_nTotalCount = -1; + } + else + { + RemoveRows(); + } +} + +void DbGridControl::RemoveRows() +{ + // we're going to remove all columns and all row, so deactivate the current cell + if (IsEditing()) + DeactivateCell(); + + // de-initialize all columns + // if there are columns, free all controllers + for (auto const & pColumn : m_aColumns) + pColumn->Clear(); + + m_pSeekCursor.reset(); + m_pDataCursor.reset(); + + m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr; + m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1; + m_nOptions = DbGridControlOptions::Readonly; + + // reset number of sentences to zero in the browser + EditBrowseBox::RemoveRows(); + m_aBar->InvalidateAll(m_nCurrentPos, true); +} + +void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY) +{ + // positioning of the controls + if (m_bNavigationBar) + { + tools::Rectangle aRect(GetControlArea()); + m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(aRect.GetSize().Width(), aRect.GetSize().Height() - 1)); + nX = m_aBar->ArrangeControls(); + } +} + +void DbGridControl::EnableHandle(bool bEnable) +{ + if (m_bHandle == bEnable) + return; + + // HandleColumn is only hidden because there are a lot of problems while painting otherwise + RemoveColumn( HandleColumnId ); + m_bHandle = bEnable; + InsertHandleColumn(); +} + +namespace +{ + bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars ) + { + BrowserMode nOldMode = _rMode; + + if ( !_bNavigationBar ) + { + _rMode &= ~BrowserMode::AUTO_HSCROLL; + } + + if ( _bHideScrollbars ) + { + _rMode |= BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL; + _rMode &= ~BrowserMode( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL ); + } + else + { + _rMode |= BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL; + _rMode &= ~BrowserMode( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL ); + } + + // note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular, + // _bHideScrollbars is ignored then + if ( _bNavigationBar ) + { + _rMode |= BrowserMode::AUTO_HSCROLL; + _rMode &= ~BrowserMode::NO_HSCROLL; + } + + return nOldMode != _rMode; + } +} + +void DbGridControl::EnableNavigationBar(bool bEnable) +{ + if (m_bNavigationBar == bEnable) + return; + + m_bNavigationBar = bEnable; + + if (bEnable) + { + m_aBar->Show(); + m_aBar->Enable(); + m_aBar->InvalidateAll(m_nCurrentPos, true); + + if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) + SetMode( m_nMode ); + + // get size of the reserved ControlArea + Point aPoint = GetControlArea().TopLeft(); + sal_uInt16 nX = static_cast(aPoint.X()); + + ArrangeControls(nX, static_cast(aPoint.Y())); + ReserveControlArea(nX); + } + else + { + m_aBar->Hide(); + m_aBar->Disable(); + + if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) + SetMode( m_nMode ); + + ReserveControlArea(); + } +} + +DbGridControlOptions DbGridControl::SetOptions(DbGridControlOptions nOpt) +{ + DBG_ASSERT(!m_xCurrentRow.is() || !m_xCurrentRow->IsModified(), + "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !"); + + // for the next setDataSource (which is triggered by a refresh, for instance) + m_nOptionMask = nOpt; + + // normalize the new options + Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet(); + if (xDataSourceSet.is()) + { + // check what kind of options are available + sal_Int32 nPrivileges = 0; + xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges; + if ((nPrivileges & Privilege::INSERT) == 0) + nOpt &= ~DbGridControlOptions::Insert; + if ((nPrivileges & Privilege::UPDATE) == 0) + nOpt &= ~DbGridControlOptions::Update; + if ((nPrivileges & Privilege::DELETE) == 0) + nOpt &= ~DbGridControlOptions::Delete; + } + else + nOpt = DbGridControlOptions::Readonly; + + // need to do something after that ? + if (nOpt == m_nOptions) + return m_nOptions; + + // the 'update' option only affects our BrowserMode (with or w/o focus rect) + BrowserMode nNewMode = m_nMode; + if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS)) + { + if (nOpt & DbGridControlOptions::Update) + nNewMode |= BrowserMode::HIDECURSOR; + else + nNewMode &= ~BrowserMode::HIDECURSOR; + } + else + nNewMode &= ~BrowserMode::HIDECURSOR; + // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ... + + if (nNewMode != m_nMode) + { + SetMode(nNewMode); + m_nMode = nNewMode; + } + + // _after_ setting the mode because this results in an ActivateCell + DeactivateCell(); + + bool bInsertChanged = (nOpt & DbGridControlOptions::Insert) != (m_nOptions & DbGridControlOptions::Insert); + m_nOptions = nOpt; + // we need to set this before the code below because it indirectly uses m_nOptions + + // the 'insert' option affects our empty row + if (bInsertChanged) + { + if (m_nOptions & DbGridControlOptions::Insert) + { // the insert option is to be set + m_xEmptyRow = new DbGridRow(); + RowInserted(GetRowCount()); + } + else + { // the insert option is to be reset + m_xEmptyRow = nullptr; + if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0)) + GoToRowColumnId(GetCurRow() - 1, GetCurColumnId()); + RowRemoved(GetRowCount()); + } + } + + // the 'delete' options has no immediate consequences + + ActivateCell(); + Invalidate(); + return m_nOptions; +} + +void DbGridControl::ForceHideScrollbars() +{ + if ( m_bHideScrollbars ) + return; + + m_bHideScrollbars = true; + + if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) + SetMode( m_nMode ); +} + +void DbGridControl::EnablePermanentCursor(bool bEnable) +{ + if (IsPermanentCursorEnabled() == bEnable) + return; + + if (bEnable) + { + m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect + m_nMode |= BrowserMode::CURSOR_WO_FOCUS; + } + else + { + if (m_nOptions & DbGridControlOptions::Update) + m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all + else + m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor + + m_nMode &= ~BrowserMode::CURSOR_WO_FOCUS; + } + SetMode(m_nMode); + + bool bWasEditing = IsEditing(); + DeactivateCell(); + if (bWasEditing) + ActivateCell(); +} + +bool DbGridControl::IsPermanentCursorEnabled() const +{ + return (m_nMode & BrowserMode::CURSOR_WO_FOCUS) && !(m_nMode & BrowserMode::HIDECURSOR); +} + +void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/) +{ + if ((GetCurColumnId() == _nColId) && IsEditing()) + { // the controller which is currently active needs to be refreshed + DeactivateCell(); + ActivateCell(); + } +} + +void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, DbGridControlOptions nOpts) +{ + if (!_xCursor.is() && !m_pDataCursor) + return; + + if (m_pDataSourcePropMultiplexer.is()) + { + m_pDataSourcePropMultiplexer->dispose(); + m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer + delete m_pDataSourcePropListener; + m_pDataSourcePropListener = nullptr; + } + m_xRowSetListener.clear(); + + // is the new cursor valid ? + // the cursor is only valid if it contains some columns + // if there is no cursor or the cursor is not valid we have to clean up and leave + if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY_THROW)->getColumns()->hasElements()) + { + RemoveRows(); + return; + } + + // did the data cursor change? + sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId()); + + SetUpdateMode(false); + RemoveRows(); + DisconnectFromFields(); + + m_pCursorDisposeListener.reset(); + + { + ::osl::MutexGuard aGuard(m_aAdjustSafety); + if (m_nAsynAdjustEvent) + { + // the adjust was thought to work with the old cursor which we don't have anymore + RemoveUserEvent(m_nAsynAdjustEvent); + m_nAsynAdjustEvent = nullptr; + } + } + + // get a new formatter and data cursor + m_xFormatter = nullptr; + Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true); + if (xSupplier.is()) + { + m_xFormatter = css::util::NumberFormatter::create(m_xContext); + m_xFormatter->attachNumberFormatsSupplier(xSupplier); + + // retrieve the datebase of the Numberformatter + try + { + xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate; + } + catch(Exception&) + { + } + } + + m_pDataCursor.reset(new CursorWrapper(_xCursor)); + + // now create a cursor for painting rows + // we need that cursor only if we are not in insert only mode + Reference< XResultSet > xClone; + Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY ); + try + { + xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > (); + } + catch(Exception&) + { + } + if (xClone.is()) + m_pSeekCursor.reset(new CursorWrapper(xClone)); + + // property listening on the data source + // (Normally one class would be sufficient : the multiplexer which could forward the property change to us. + // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported. + // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class) + // and forwards the property changes to our special method "DataSourcePropertyChanged".) + if (m_pDataCursor) + { + m_pDataSourcePropListener = new FmXGridSourcePropListener(this); + m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() ); + m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED); + m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW); + } + + BrowserMode nOldMode = m_nMode; + if (m_pSeekCursor) + { + try + { + Reference< XPropertySet > xSet(_xCursor, UNO_QUERY); + if (xSet.is()) + { + // check what kind of options are available + sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY; + xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency; + + if ( ResultSetConcurrency::UPDATABLE == nConcurrency ) + { + sal_Int32 nPrivileges = 0; + xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges; + + // Insert Option should be set if insert only otherwise you won't see any rows + // and no insertion is possible + if ((m_nOptionMask & DbGridControlOptions::Insert) + && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & DbGridControlOptions::Insert)) + m_nOptions |= DbGridControlOptions::Insert; + if ((m_nOptionMask & DbGridControlOptions::Update) + && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & DbGridControlOptions::Update)) + m_nOptions |= DbGridControlOptions::Update; + if ((m_nOptionMask & DbGridControlOptions::Delete) + && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & DbGridControlOptions::Delete)) + m_nOptions |= DbGridControlOptions::Delete; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + } + + bool bPermanentCursor = IsPermanentCursorEnabled(); + m_nMode = DEFAULT_BROWSE_MODE; + + if ( bPermanentCursor ) + { + m_nMode |= BrowserMode::CURSOR_WO_FOCUS; + m_nMode &= ~BrowserMode::HIDECURSOR; + } + else + { + // updates are allowed -> no focus rectangle + if ( m_nOptions & DbGridControlOptions::Update ) + m_nMode |= BrowserMode::HIDECURSOR; + } + + m_nMode |= BrowserMode::MULTISELECTION; + + adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ); + + Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY); + if (xSupplyColumns.is()) + InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY)); + + ConnectToFields(); + } + + sal_uInt32 nRecordCount(0); + + if (m_pSeekCursor) + { + Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet(); + xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; + m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL)); + + m_xRowSetListener = new RowSetEventListener(this); + Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY); + if ( xChangeBroad.is( ) ) + xChangeBroad->addRowsChangeListener(m_xRowSetListener); + + + // insert the currently known rows + // and one row if we are able to insert rows + if (m_nOptions & DbGridControlOptions::Insert) + { + // insert the empty row for insertion + m_xEmptyRow = new DbGridRow(); + ++nRecordCount; + } + if (nRecordCount) + { + m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor.get(), true); + m_xDataRow = new DbGridRow(m_pDataCursor.get(), false); + RowInserted(0, nRecordCount, false); + + if (m_xSeekRow->IsValid()) + try + { + m_nSeekPos = m_pSeekCursor->getRow() - 1; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svx"); + m_nSeekPos = -1; + } + } + else + { + // no rows so we don't need a seekcursor + m_pSeekCursor.reset(); + } + } + + // go to the old column + if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount()) + nCurPos = 0; + + // Column zero is a valid choice and guaranteed to exist, + // but invisible to the user; if we have at least one + // user-visible column, go to that one. + if (nCurPos == 0 && ColCount() > 1) + nCurPos = 1; + + // there are rows so go to the selected current column + if (nRecordCount) + GoToRowColumnId(0, GetColumnId(nCurPos)); + // else stop the editing if necessary + else if (IsEditing()) + DeactivateCell(); + + // now reset the mode + if (m_nMode != nOldMode) + SetMode(m_nMode); + + // RecalcRows was already called while resizing + if (!IsResizing() && GetRowCount()) + RecalcRows(GetTopRow(), GetVisibleRows(), true); + + m_aBar->InvalidateAll(m_nCurrentPos, true); + SetUpdateMode(true); + + // start listening on the seek cursor + if (m_pSeekCursor) + m_pCursorDisposeListener.reset(new DisposeListenerGridBridge(*this, Reference< XComponent > (Reference< XInterface >(*m_pSeekCursor), UNO_QUERY))); +} + +void DbGridControl::RemoveColumns() +{ + if ( !isDisposed() && IsEditing() ) + DeactivateCell(); + + m_aColumns.clear(); + + EditBrowseBox::RemoveColumns(); +} + +std::unique_ptr DbGridControl::CreateColumn(sal_uInt16 nId) +{ + return std::unique_ptr(new DbGridColumn(nId, *this)); +} + +sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId) +{ + DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ..."); + sal_uInt16 nRealPos = nModelPos; + if (nModelPos != HEADERBAR_APPEND) + { + // calc the view pos. we can't use our converting functions because the new column + // has no VCL-representation, yet. + sal_Int16 nViewPos = nModelPos; + while (nModelPos--) + { + if ( m_aColumns[ nModelPos ]->IsHidden() ) + --nViewPos; + } + // restore nModelPos, we need it later + nModelPos = nRealPos; + // the position the base class gets is the view pos + 1 (because of the handle column) + nRealPos = nViewPos + 1; + } + + // calculate the new id + for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && size_t(nId) <= m_aColumns.size(); ++nId) + ; + DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !"); + // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..." + + EditBrowseBox::AppendColumn(rName, nWidth, nRealPos, nId); + if (nModelPos == HEADERBAR_APPEND) + m_aColumns.push_back( CreateColumn(nId) ); + else + m_aColumns.insert( m_aColumns.begin() + nModelPos, CreateColumn(nId) ); + + return nId; +} + +void DbGridControl::RemoveColumn(sal_uInt16 nId) +{ + EditBrowseBox::RemoveColumn(nId); + + const sal_uInt16 nIndex = GetModelColumnPos(nId); + if(nIndex != GRID_COLUMN_NOT_FOUND) + { + m_aColumns.erase( m_aColumns.begin()+nIndex ); + } +} + +void DbGridControl::ColumnMoved(sal_uInt16 nId) +{ + EditBrowseBox::ColumnMoved(nId); + + // remove the col from the model + sal_uInt16 nOldModelPos = GetModelColumnPos(nId); +#ifdef DBG_UTIL + DbGridColumn* pCol = m_aColumns[ nOldModelPos ].get(); + DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?"); +#endif + + // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment + // so the method won't work (in fact it would return the old model pos) + + // the new view pos is calculated easily + sal_uInt16 nNewViewPos = GetViewColumnPos(nId); + + // from that we can compute the new model pos + size_t nNewModelPos; + for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos) + { + if (!m_aColumns[ nNewModelPos ]->IsHidden()) + { + if (!nNewViewPos) + break; + else + --nNewViewPos; + } + } + DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !"); + + // this will work. of course the model isn't fully consistent with our view right now, but let's + // look at the situation : a column has been moved with in the VIEW from pos m to n, say m