Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/svx/source/fmcomp/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 112 kB image not shown  

Quelle  gridctrl.cxx   Sprache: C

 
/* -*- 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 <sal/log.hxx>
#include <helpids.h>
#include <svx/gridctrl.hxx>
#include <gridcell.hxx>
#include <svx/fmtools.hxx>
#include <svtools/stringtransfer.hxx>
#include <connectivity/dbtools.hxx>
#include <connectivity/dbconversion.hxx>

#include <fmprop.hxx>
#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/sdb/XResultSetAccess.hpp>
#include <com/sun/star/sdb/RowChangeAction.hpp>
#include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
#include <com/sun/star/sdbc/SQLException.hpp>
#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
#include <com/sun/star/sdbc/XRowSet.hpp>
#include <com/sun/star/sdbcx/Privilege.hpp>
#include <com/sun/star/util/NumberFormatter.hpp>
#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/PropertyChangeEvent.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <comphelper/diagnose_ex.hxx>
#include <tools/debug.hxx>
#include <tools/fract.hxx>
#include <vcl/settings.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <vcl/weldutils.hxx>

#include <svx/strings.hrc>

#include <svx/dialmgr.hxx>
#include <sdbdatacolumn.hxx>

#include <comphelper/property.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/implbase.hxx>

#include <algorithm>
#include <cstdlib>
#include <memory>

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<XRowsChangeListener>
{
    VclPtr<DbGridControl> 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 : protected ::comphelper::OPropertyChangeListener
{
    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_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<FmXDisposeMultiplexer>  m_xRealListener;

public:
    DisposeListenerGridBridge(  DbGridControl& _rParent, const Reference< XComponent >&&nbsp;_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<DbGridControl> m_pParent;

    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_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<weld::Entry> 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<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1);
    m_bPositioning = false;
}

NavigationBar::NavigationBar(DbGridControl* pParent)
    : InterimItemWindow(pParent, u"svx/ui/navigationbar.ui"_ustr, u"NavigationBar"_ustr)
    , m_xRecordText(m_xBuilder->weld_label(u"recordtext"_ustr))
    , m_xAbsolute(new NavigationBar::AbsolutePos(m_xBuilder->weld_entry(u"entry-noframe"_ustr), this))
    , m_xRecordOf(m_xBuilder->weld_label(u"recordof"_ustr))
    , m_xRecordCount(m_xBuilder->weld_label(u"recordcount"_ustr))
    , m_xFirstBtn(m_xBuilder->weld_button(u"first"_ustr))
    , m_xPrevBtn(m_xBuilder->weld_button(u"prev"_ustr))
    , m_xNextBtn(m_xBuilder->weld_button(u"next"_ustr))
    , m_xLastBtn(m_xBuilder->weld_button(u"last"_ustr))
    , m_xNewBtn(m_xBuilder->weld_button(u"new"_ustr))
    , m_xPrevRepeater(std::make_shared<weld::ButtonPressRepeater>(*m_xPrevBtn, LINK(this,NavigationBar,OnClick)))
    , m_xNextRepeater(std::make_shared<weld::ButtonPressRepeater>(*m_xNextBtn, LINK(this,NavigationBar,OnClick)))
    , m_nCurrentPos(-1)
    , m_bPositioning(false)
{
    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('?'));

    vcl::Font aApplFont(Application::GetSettings().GetStyleSettings().GetToolFont());
    SetPointFontAndZoom(aApplFont, Fraction(1, 1));

    m_xContainer->connect_size_allocate(LINK(this, NavigationBar, SizeAllocHdl));
}

IMPL_LINK(NavigationBar, SizeAllocHdl, const Size&, rSize, void)
{
    if (rSize == m_aLastAllocSize)
        return;
    m_aLastAllocSize = rSize;
    static_cast<DbGridControl*>(GetParent())->RearrangeAtIdle();
}

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::GetPreferredWidth() const
{
    return m_xContainer->get_preferred_size().Width();
}

IMPL_LINK(NavigationBar, OnClick, weld::Button&, rButton, void)
{
    DbGridControl* pParent = static_cast<DbGridControl*>(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<DbGridControl*>(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<DbGridControl*>(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;
            defaultbreak;
        }
        return bAvailable;
    }
}

void NavigationBar::SetState(DbGridControlNavigationBarState nWhich)
{
    bool bAvailable = GetState(nWhich);
    DbGridControl* pParent = static_cast<DbGridControl*>(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;
        defaultbreak;
    }
    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();
    }
}

static void ScaleButton(weld::Button& rBtn, const Fraction& rZoom)
{
    rBtn.set_size_request(-1, -1);
    Size aPrefSize = rBtn.get_preferred_size();
    aPrefSize.setWidth(std::round(double(aPrefSize.Width() * rZoom)));
    aPrefSize.setHeight(std::round(double(aPrefSize.Height() * rZoom)));
    rBtn.set_size_request(aPrefSize.Width(), aPrefSize.Height());
}

void NavigationBar::SetPointFontAndZoom(const vcl::Font& rFont, const Fraction&&nbsp;rZoom)
{
    vcl::Font aFont(rFont);
    if (rZoom.GetNumerator() != rZoom.GetDenominator())
    {
        Size aSize = aFont.GetFontSize();
        aSize.setWidth(std::round(double(aSize.Width() * rZoom)));
        aSize.setHeight(std::round(double(aSize.Height() * rZoom)));
        aFont.SetFontSize(aSize);
    }

    m_xRecordText->set_font(aFont);
    m_xAbsolute->GetWidget()->set_font(aFont);
    m_xRecordOf->set_font(aFont);
    m_xRecordCount->set_font(aFont);

    auto nReserveWidth = m_xRecordCount->get_approximate_digit_width() * nReserveNumDigits;
    m_xAbsolute->GetWidget()->set_size_request(nReserveWidth, -1);
    m_xRecordCount->set_size_request(nReserveWidth, -1);

    ScaleButton(*m_xFirstBtn, rZoom);
    ScaleButton(*m_xPrevBtn, rZoom);
    ScaleButton(*m_xNextBtn, rZoom);
    ScaleButton(*m_xLastBtn, rZoom);
    ScaleButton(*m_xNewBtn, rZoom);

    SetZoom(rZoom);

    InvalidateChildSizeCache();
}

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<NavigationBar>::Create(this))
            ,m_nAsynAdjustEvent(nullptr)
            ,m_pDataSourcePropListener(nullptr)
            ,m_pGridListener(nullptr)
            ,m_nSeekPos(-1)
            ,m_nTotalCount(-1)
            ,m_aRearrangeIdle("DbGridControl Rearrange Idle")
            ,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_bSynchDisplay(true)
            ,m_bHandle(true)
            ,m_bFilterMode(false)
            ,m_bWantDestruction(false)
            ,m_bPendingAdjustRows(false)
            ,m_bHideScrollbars( false )
            ,m_bUpdating(false)
{
    m_bNavigationBar = true;

    OUString sName(SvxResId(RID_STR_NAVIGATIONBAR));
    m_aBar->SetAccessibleName(sName);
    m_aBar->Show();
    ImplInitWindow( InitWindowFacet::All );

    m_aRearrangeIdle.SetInvokeHandler(LINK(this, DbGridControl, RearrangeHdl));
}

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<BrowserHeader> 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_aFieldListeners.empty())
        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();

    m_aRearrangeIdle.Stop();

    EditBrowseBox::dispose();
}

void DbGridControl::RearrangeAtIdle()
{
    if (isDisposed())
        return;
    m_aRearrangeIdle.Start();
}

void DbGridControl::StateChanged( StateChangedType nType )
{
    EditBrowseBox::StateChanged( nType );

    switch (nType)
    {
        case StateChangedType::Mirroring:
            ImplInitWindow( InitWindowFacet::WritingMode );
            Invalidate();
            break;

        case StateChangedType::Zoom:
        {
            ImplInitWindow( InitWindowFacet::Font );
            RearrangeAtIdle();
        }
        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 )
        {
            const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
            vcl::Font aFont = rStyleSettings.GetToolFont();
            if (IsControlFont())
                aFont.Merge(GetControlFont());

            m_aBar->SetPointFontAndZoom(aFont, 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());
        nX = m_aBar->GetPreferredWidth();
        m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(nX, aRect.GetSize().Height() - 1));
    }
}

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<sal_uInt16>(aPoint.X());

        ArrangeControls(nX, static_cast<sal_uInt16>(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(u"NullDate"_ustr) >>= 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;
                }
            }
        }
        catchconst 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;
                }
                catchconst 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<DbGridColumn> DbGridControl::CreateColumn(sal_uInt16 nId)
{
    return std::unique_ptr<DbGridColumn>(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<n (in the
    // other case we can use analogue arguments).
    // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
    // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
    // within this range is constant, so we may calculate the view pos from the model pos in the above way.

    // for instance, let's look at a grid with six columns where the third one is hidden. this will
    // initially look like this :

    //              +---+---+---+---+---+---+
    // model pos    | 0 | 1 |*2*| 3 | 4 | 5 |
    //              +---+---+---+---+---+---+
    // ID           | 1 | 2 | 3 | 4 | 5 | 6 |
    //              +---+---+---+---+---+---+
    // view pos     | 0 | 1 | - | 2 | 3 | 4 |
    //              +---+---+---+---+---+---+

    // if we move the column at (view) pos 1 to (view) pos 3 we have :

    //              +---+---+---+---+---+---+
    // model pos    | 0 | 3 |*2*| 4 | 1 | 5 |   // not reflecting the changes, yet
    //              +---+---+---+---+---+---+
    // ID           | 1 | 4 | 3 | 5 | 2 | 6 |   // already reflecting the changes
    //              +---+---+---+---+---+---+
    // view pos     | 0 | 1 | - | 2 | 3 | 4 |
    //              +---+---+---+---+---+---+

    // or, sorted by the out-of-date model positions :

    //              +---+---+---+---+---+---+
    // model pos    | 0 | 1 |*2*| 3 | 4 | 5 |
    //              +---+---+---+---+---+---+
    // ID           | 1 | 2 | 3 | 4 | 5 | 6 |
    //              +---+---+---+---+---+---+
    // view pos     | 0 | 3 | - | 1 | 2 | 4 |
    //              +---+---+---+---+---+---+

    // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
    // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
    // exactly the pos where we have to re-insert our column's model, so it looks ike this :

    //              +---+---+---+---+---+---+
    // model pos    | 0 |*1*| 2 | 3 | 4 | 5 |
    //              +---+---+---+---+---+---+
    // ID           | 1 | 3 | 4 | 5 | 2 | 6 |
    //              +---+---+---+---+---+---+
    // view pos     | 0 | - | 1 | 2 | 3 | 4 |
    //              +---+---+---+---+---+---+

    // Now, all is consistent again.
    // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
    // the user expected the latter but there really is no good argument against our method ;) ...)

    // And no, this large explanation isn't just because I wanted to play a board game or something like
    // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
    // positions, view col positions)  is really painful (at least for me) so the above pictures helped me a lot ;)

    auto temp = std::move(m_aColumns[ nOldModelPos ]);
    m_aColumns.erase( m_aColumns.begin() + nOldModelPos );
    m_aColumns.insert( m_aColumns.begin() + nNewModelPos, std::move(temp) );
}

bool DbGridControl::SeekRow(sal_Int32 nRow)
{
    // in filter mode or in insert only mode we don't have any cursor!
    if ( !SeekCursor( nRow ) )
        return false;

    if ( IsFilterMode() )
    {
        DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
        m_xPaintRow = m_xEmptyRow;
    }
    else
    {
        // on the current position we have to take the current row for display as we want
        // to have the most recent values for display
        if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
            m_xPaintRow = m_xCurrentRow;
        // seek to the empty insert row
        else if ( IsInsertionRow( nRow ) )
            m_xPaintRow = m_xEmptyRow;
        else
        {
            m_xSeekRow->SetState( m_pSeekCursor.get(), true );
            m_xPaintRow = m_xSeekRow;
        }
    }

    EditBrowseBox::SeekRow(nRow);

    return m_nSeekPos >= 0;
}

// Is called whenever the visible amount of data changes
void DbGridControl::VisibleRowsChanged( sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen )
{
    RecalcRows(nNewTopRow, nLinesOnScreen, false);
}

void DbGridControl::RecalcRows(sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
{
    // If no cursor -> no rows in the browser.
    if (!m_pSeekCursor)
    {
        DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there");
        return;
    }

    // ignore any implicitly made updates
    bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
    if (bDisablePaint)
        EnablePaint(false);

    // adjust cache to the visible area
    Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
    sal_Int32 nCacheSize = 0;
    xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
    bool bCacheAligned   = false;
    // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
    // positioned on the first sentence
    tools::Long nDelta = nNewTopRow - GetTopRow();
    // limit for relative positioning
    tools::Long nLimit = nCacheSize ? nCacheSize / 2 : 0;

    // more lines on screen than in cache
    if (nLimit < nLinesOnScreen)
    {
        Any aCacheSize;
        aCacheSize <<= sal_Int32(nLinesOnScreen*2);
        xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
        // here we need to update the cursor for sure
        bUpdateCursor = true;
        bCacheAligned = true;
        nLimit = nLinesOnScreen;
    }

    // In the following, all positionings are done as it is
    // ensured that there are enough lines in the data cache

    // window goes downwards with less than two windows difference or
    // the cache was updated and no rowcount yet
    if (nDelta < nLimit && (nDelta > 0
        || (bCacheAligned && m_nTotalCount < 0)) )
        SeekCursor(nNewTopRow + nLinesOnScreen - 1);
    else if (nDelta < 0 && std::abs(nDelta) < nLimit)
        SeekCursor(nNewTopRow);
    else if (nDelta != 0 || bUpdateCursor)
        SeekCursor(nNewTopRow, true);

    AdjustRows();

    // ignore any updates implicit made
    EnablePaint(true);
}

void DbGridControl::RowInserted(sal_Int32 nRow, sal_Int32 nNumRows, bool bDoPaint)
{
    if (!nNumRows)
        return;

    if (m_bRecordCountFinal && m_nTotalCount < 0)
    {
        // if we have an insert row we have to reduce to count by 1
        // as the total count reflects only the existing rows in database
        m_nTotalCount = GetRowCount() + nNumRows;
        if (m_xEmptyRow.is())
            --m_nTotalCount;
    }
    else if (m_nTotalCount >= 0)
        m_nTotalCount += nNumRows;

    EditBrowseBox::RowInserted(nRow, nNumRows, bDoPaint);
    m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
}

void DbGridControl::RowRemoved(sal_Int32 nRow, sal_Int32 nNumRows, bool bDoPaint)
{
    if (!nNumRows)
        return;

    if (m_bRecordCountFinal && m_nTotalCount < 0)
    {
        m_nTotalCount = GetRowCount() - nNumRows;
        // if we have an insert row reduce by 1
        if (m_xEmptyRow.is())
            --m_nTotalCount;
    }
    else if (m_nTotalCount >= 0)
        m_nTotalCount -= nNumRows;

    EditBrowseBox::RowRemoved(nRow, nNumRows, bDoPaint);
    m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
}

void DbGridControl::AdjustRows()
{
    if (!m_pSeekCursor)
        return;

    Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();

    // refresh RecordCount
    sal_Int32 nRecordCount = 0;
    xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
    if (!m_bRecordCountFinal)
        m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));

    // Did the number of rows change?
    // Here we need to consider that there might be an additional row for adding new data sets

    // add additional AppendRow for insertion
    if (m_nOptions & DbGridControlOptions::Insert)
        ++nRecordCount;

    // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
    if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
        m_xCurrentRow->IsNew())
        ++nRecordCount;
    // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
    // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
    // and a second time here (60787 - FS)

    if (nRecordCount != GetRowCount())
    {
        tools::Long nDelta = GetRowCount() - static_cast<tools::Long>(nRecordCount);
        if (nDelta > 0) // too many
        {
            RowRemoved(GetRowCount() - nDelta, nDelta, false);
            // some rows are gone, thus, repaint starting at the current position
            Invalidate();

            sal_Int32 nNewPos = AlignSeekCursor();
            if (m_bSynchDisplay)
                EditBrowseBox::GoToRow(nNewPos);

            SetCurrent(nNewPos);
            // there are rows so go to the selected current column
            if (nRecordCount)
                GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
            if (!IsResizing() && GetRowCount())
                RecalcRows(GetTopRow(), GetVisibleRows(), true);
            m_aBar->InvalidateAll(m_nCurrentPos, true);
        }
        else  // too few
            RowInserted(GetRowCount(), -nDelta);
    }

    if (m_bRecordCountFinal && m_nTotalCount < 0)
    {
        if (m_nOptions & DbGridControlOptions::Insert)
            m_nTotalCount = GetRowCount() - 1;
        else
            m_nTotalCount = GetRowCount();
    }
    m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
}

svt::EditBrowseBox::RowStatus DbGridControl::GetRowStatus(sal_Int32 nRow) const
{
    if (IsFilterRow(nRow))
        return EditBrowseBox::FILTER;
    else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
    {
        // new row
        if (!IsValid(m_xCurrentRow))
            return EditBrowseBox::DELETED;
        else if (IsModified())
            return EditBrowseBox::MODIFIED;
        else if (m_xCurrentRow->IsNew())
            return EditBrowseBox::CURRENTNEW;
        else
            return EditBrowseBox::CURRENT;
    }
    else if (IsInsertionRow(nRow))
        return EditBrowseBox::NEW;
    else if (!IsValid(m_xSeekRow))
        return EditBrowseBox::DELETED;
    else
        return EditBrowseBox::CLEAN;
}

void DbGridControl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId) const
{
    if (!IsValid(m_xPaintRow))
        return;

    size_t Location = GetModelColumnPos(nColumnId);
    DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
    if (pColumn)
    {
        tools::Rectangle aArea(rRect);
        if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS)
        {
            aArea.AdjustTop(1 );
            aArea.AdjustBottom( -1 );
        }
        pColumn->Paint(rDev, aArea, m_xPaintRow.get(), getNumberFormatter());
    }
}

bool DbGridControl::CursorMoving(sal_Int32 nNewRow, sal_uInt16 nNewCol)
{

    DeactivateCell( false );

    if  (   m_pDataCursor
        &&  ( m_nCurrentPos != nNewRow )
        && !SetCurrent( nNewRow )
        )
    {
        ActivateCell();
        return false;
    }

    return EditBrowseBox::CursorMoving( nNewRow, nNewCol );
}

bool DbGridControl::SetCurrent(sal_Int32 nNewRow)
{
    // Each movement of the datacursor must start with BeginCursorAction and end with
    // EndCursorAction to block all notifications during the movement
    BeginCursorAction();

    try
    {
        // compare positions
        if (SeekCursor(nNewRow))
        {
            if (IsFilterRow(nNewRow))   // special mode for filtering
            {
                m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
                m_nCurrentPos = nNewRow;
            }
            else
            {
                bool bNewRowInserted = false;
                // Should we go to the insertrow ?
                if (IsInsertionRow(nNewRow))
                {
                    // to we need to move the cursor to the insert row?
                    // we need to insert the if the current row isn't the insert row or if the
                    // cursor triggered the move by itself and we need a reinitialization of the row
                    Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
                    if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
                    {
                        Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
                        xUpdateCursor->moveToInsertRow();
                    }
                    bNewRowInserted = true;
                }
                else
                {

                    if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
                    {
                        Any aBookmark = m_pSeekCursor->getBookmark();
                        if (!m_xCurrentRow.is() || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
                        {
                            // adjust the cursor to the new desired row
                            if (!m_pDataCursor->moveToBookmark(aBookmark))
                            {
                                EndCursorAction();
                                return false;
                            }
                        }
                    }
                }
                m_xDataRow->SetState(m_pDataCursor.get(), false);
                m_xCurrentRow = m_xDataRow;

                tools::Long nPaintPos = -1;
                // do we have to repaint the last regular row in case of setting defaults or autovalues
                if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
                    nPaintPos = m_nCurrentPos;

                m_nCurrentPos = nNewRow;

                // repaint the new row to display all defaults
                if (bNewRowInserted)
                    RowModified(m_nCurrentPos);
                if (nPaintPos >= 0)
                    RowModified(nPaintPos);
            }
        }
        else
        {
            OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
            EndCursorAction();
            return false;
        }
    }
    catch ( const Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("svx");
        EndCursorAction();
        return false;
    }

    EndCursorAction();
    return true;
}

void DbGridControl::CursorMoved()
{

    // cursor movement due to deletion or insertion of rows
    if (m_pDataCursor && m_nCurrentPos != GetCurRow())
    {
        DeactivateCell();
        SetCurrent(GetCurRow());
    }

    EditBrowseBox::CursorMoved();
    m_aBar->InvalidateAll(m_nCurrentPos);

    // select the new column when they moved
    if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
    {
        SelectColumnId( GetCurColumnId() );
    }

    if ( m_nLastColId != GetCurColumnId() )
        onColumnChange();
    m_nLastColId = GetCurColumnId();

    if ( m_nLastRowId != GetCurRow() )
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.24 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.