3431 lines
113 KiB
C++
3431 lines
113 KiB
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 >& _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;
|
|
default: break;
|
|
}
|
|
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;
|
|
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();
|
|
}
|
|
}
|
|
|
|
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& 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;
|
|
}
|
|
}
|
|
}
|
|
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<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() )
|
|
onRowChange();
|
|
m_nLastRowId = GetCurRow();
|
|
}
|
|
|
|
void DbGridControl::onRowChange()
|
|
{
|
|
// not interested in
|
|
}
|
|
|
|
void DbGridControl::onColumnChange()
|
|
{
|
|
if ( m_pGridListener )
|
|
m_pGridListener->columnChanged();
|
|
}
|
|
|
|
void DbGridControl::setDisplaySynchron(bool bSync)
|
|
{
|
|
if (bSync != m_bSynchDisplay)
|
|
{
|
|
m_bSynchDisplay = bSync;
|
|
if (m_bSynchDisplay)
|
|
AdjustDataSource();
|
|
}
|
|
}
|
|
|
|
void DbGridControl::AdjustDataSource(bool bFull)
|
|
{
|
|
SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
|
|
SolarMutexGuard aGuard;
|
|
// If the current row is recalculated at the moment, do not adjust
|
|
|
|
if (bFull)
|
|
m_xCurrentRow = nullptr;
|
|
// if we are on the same row only repaint
|
|
// but this is only possible for rows which are not inserted, in that case the comparison result
|
|
// may not be correct
|
|
else
|
|
if ( m_xCurrentRow.is()
|
|
&& !m_xCurrentRow->IsNew()
|
|
&& !m_pDataCursor->isBeforeFirst()
|
|
&& !m_pDataCursor->isAfterLast()
|
|
&& !m_pDataCursor->rowDeleted()
|
|
)
|
|
{
|
|
bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
|
|
|
|
bool bDataCursorIsOnNew = false;
|
|
m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
|
|
|
|
if ( bEqualBookmarks && !bDataCursorIsOnNew )
|
|
{
|
|
// position of my data cursor is the same as the position our current row points tpo
|
|
// sync the status, repaint, done
|
|
DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Errors in the data row");
|
|
SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
|
|
RowModified(m_nCurrentPos);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// away from the data cursor's row
|
|
if (m_xPaintRow == m_xCurrentRow)
|
|
m_xPaintRow = m_xSeekRow;
|
|
|
|
// not up-to-date row, thus, adjust completely
|
|
if (!m_xCurrentRow.is())
|
|
AdjustRows();
|
|
|
|
sal_Int32 nNewPos = AlignSeekCursor();
|
|
if (nNewPos < 0)// could not find any position
|
|
return;
|
|
|
|
if (nNewPos != m_nCurrentPos)
|
|
{
|
|
if (m_bSynchDisplay)
|
|
EditBrowseBox::GoToRow(nNewPos);
|
|
|
|
if (!m_xCurrentRow.is())
|
|
// Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
|
|
// on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
|
|
// CurrentRow is corrected to point two rows down, so that GoToRow will point into
|
|
// emptiness (since we are - purportedly - at the correct position)
|
|
SetCurrent(nNewPos);
|
|
}
|
|
else
|
|
{
|
|
SetCurrent(nNewPos);
|
|
RowModified(nNewPos);
|
|
}
|
|
|
|
// if the data cursor was moved from outside, this section is voided
|
|
SetNoSelection();
|
|
m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.is());
|
|
}
|
|
|
|
sal_Int32 DbGridControl::AlignSeekCursor()
|
|
{
|
|
// position SeekCursor onto the data cursor, no data transmission
|
|
|
|
if (!m_pSeekCursor)
|
|
return -1;
|
|
|
|
Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
|
|
|
|
// now align the seek cursor and the data cursor
|
|
if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
|
|
m_nSeekPos = GetRowCount() - 1;
|
|
else
|
|
{
|
|
try
|
|
{
|
|
if ( m_pDataCursor->isBeforeFirst() )
|
|
{
|
|
// this is somewhat strange, but can nevertheless happen
|
|
SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
|
|
m_pSeekCursor->first();
|
|
m_pSeekCursor->previous();
|
|
m_nSeekPos = -1;
|
|
}
|
|
else if ( m_pDataCursor->isAfterLast() )
|
|
{
|
|
SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
|
|
m_pSeekCursor->last();
|
|
m_pSeekCursor->next();
|
|
m_nSeekPos = -1;
|
|
}
|
|
else
|
|
{
|
|
m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
|
|
if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
|
|
// unfortunately, moveToBookmark might lead to a re-positioning of the seek
|
|
// cursor (if the complex moveToBookmark with all its events fires an update
|
|
// somewhere) -> retry
|
|
m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
|
|
// Now there is still the chance of a failure but it is less likely.
|
|
// The alternative would be a loop until everything is fine - no good solution...
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
}
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
}
|
|
}
|
|
return m_nSeekPos;
|
|
}
|
|
|
|
bool DbGridControl::SeekCursor(sal_Int32 nRow, bool bAbsolute)
|
|
{
|
|
// position SeekCursor onto the data cursor, no data transmission
|
|
|
|
// additions for the filtermode
|
|
if (IsFilterRow(nRow))
|
|
{
|
|
m_nSeekPos = 0;
|
|
return true;
|
|
}
|
|
|
|
if (!m_pSeekCursor)
|
|
return false;
|
|
|
|
// is this an insertion?
|
|
if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
|
|
nRow >= m_nCurrentPos)
|
|
{
|
|
// if so, scrolling down must be prevented as this is already the last data set!
|
|
if (nRow == m_nCurrentPos)
|
|
{
|
|
// no adjustment necessary
|
|
m_nSeekPos = nRow;
|
|
}
|
|
else if (IsInsertionRow(nRow)) // blank row for data insertion
|
|
m_nSeekPos = nRow;
|
|
}
|
|
else if (IsInsertionRow(nRow)) // blank row for data insertion
|
|
m_nSeekPos = nRow;
|
|
else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & DbGridControlOptions::Insert) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
|
|
m_nSeekPos = nRow;
|
|
else
|
|
{
|
|
bool bSuccess = false;
|
|
tools::Long nSteps = 0;
|
|
try
|
|
{
|
|
if ( m_pSeekCursor->rowDeleted() )
|
|
{
|
|
// somebody deleted the current row of the seek cursor. Move it away from this row.
|
|
m_pSeekCursor->next();
|
|
if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
|
|
bAbsolute = true;
|
|
}
|
|
|
|
if ( !bAbsolute )
|
|
{
|
|
DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
|
|
"DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
|
|
nSteps = nRow - (m_pSeekCursor->getRow() - 1);
|
|
bAbsolute = std::abs(nSteps) > 100;
|
|
}
|
|
|
|
if ( bAbsolute )
|
|
{
|
|
bSuccess = m_pSeekCursor->absolute(nRow + 1);
|
|
if (bSuccess)
|
|
m_nSeekPos = nRow;
|
|
}
|
|
else
|
|
{
|
|
if (nSteps > 0) // position onto the last needed data set
|
|
{
|
|
if (m_pSeekCursor->isAfterLast())
|
|
bSuccess = false;
|
|
else if (m_pSeekCursor->isBeforeFirst())
|
|
bSuccess = m_pSeekCursor->absolute(nSteps);
|
|
else
|
|
bSuccess = m_pSeekCursor->relative(nSteps);
|
|
}
|
|
else if (nSteps < 0)
|
|
{
|
|
if (m_pSeekCursor->isBeforeFirst())
|
|
bSuccess = false;
|
|
else if (m_pSeekCursor->isAfterLast())
|
|
bSuccess = m_pSeekCursor->absolute(nSteps);
|
|
else
|
|
bSuccess = m_pSeekCursor->relative(nSteps);
|
|
}
|
|
else
|
|
{
|
|
m_nSeekPos = nRow;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
OSL_FAIL("DbGridControl::SeekCursor : failed ...");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!bSuccess)
|
|
{
|
|
if (bAbsolute || nSteps > 0)
|
|
{
|
|
if (m_pSeekCursor->isLast())
|
|
bSuccess = true;
|
|
else
|
|
bSuccess = m_pSeekCursor->last();
|
|
}
|
|
else
|
|
{
|
|
if (m_pSeekCursor->isFirst())
|
|
bSuccess = true;
|
|
else
|
|
bSuccess = m_pSeekCursor->first();
|
|
}
|
|
}
|
|
|
|
if (bSuccess)
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
else
|
|
m_nSeekPos = -1;
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("svx");
|
|
OSL_FAIL("DbGridControl::SeekCursor : failed ...");
|
|
m_nSeekPos = -1; // no further data set available
|
|
}
|
|
}
|
|
return m_nSeekPos == nRow;
|
|
}
|
|
|
|
void DbGridControl::MoveToFirst()
|
|
{
|
|
if (m_pSeekCursor && (GetCurRow() != 0))
|
|
MoveToPosition(0);
|
|
}
|
|
|
|
void DbGridControl::MoveToLast()
|
|
{
|
|
if (!m_pSeekCursor)
|
|
return;
|
|
|
|
if (m_nTotalCount < 0) // no RecordCount, yet
|
|
{
|
|
try
|
|
{
|
|
bool bRes = m_pSeekCursor->last();
|
|
|
|
if (bRes)
|
|
{
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
AdjustRows();
|
|
}
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
}
|
|
}
|
|
|
|
// position onto the last data set not on a blank row
|
|
if (m_nOptions & DbGridControlOptions::Insert)
|
|
{
|
|
if ((GetRowCount() - 1) > 0)
|
|
MoveToPosition(GetRowCount() - 2);
|
|
}
|
|
else if (GetRowCount())
|
|
MoveToPosition(GetRowCount() - 1);
|
|
}
|
|
|
|
void DbGridControl::MoveToPrev()
|
|
{
|
|
sal_Int32 nNewRow = std::max(GetCurRow() - 1, sal_Int32(0));
|
|
if (GetCurRow() != nNewRow)
|
|
MoveToPosition(nNewRow);
|
|
}
|
|
|
|
void DbGridControl::MoveToNext()
|
|
{
|
|
if (!m_pSeekCursor)
|
|
return;
|
|
|
|
if (m_nTotalCount > 0)
|
|
{
|
|
// move the data cursor to the right position
|
|
tools::Long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
|
|
if (GetCurRow() != nNewRow)
|
|
MoveToPosition(nNewRow);
|
|
}
|
|
else
|
|
{
|
|
bool bOk = false;
|
|
try
|
|
{
|
|
// try to move to next row
|
|
// when not possible our paint cursor is already on the last row
|
|
// then we must be sure that the data cursor is on the position
|
|
// we call ourself again
|
|
bOk = m_pSeekCursor->next();
|
|
if (bOk)
|
|
{
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
MoveToPosition(GetCurRow() + 1);
|
|
}
|
|
}
|
|
catch(SQLException &)
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("svx");
|
|
}
|
|
|
|
if(!bOk)
|
|
{
|
|
AdjustRows();
|
|
if (m_nTotalCount > 0) // only to avoid infinite recursion
|
|
MoveToNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbGridControl::MoveToPosition(sal_uInt32 nPos)
|
|
{
|
|
if (!m_pSeekCursor)
|
|
return;
|
|
|
|
if (m_nTotalCount < 0 && static_cast<tools::Long>(nPos) >= GetRowCount())
|
|
{
|
|
try
|
|
{
|
|
if (!m_pSeekCursor->absolute(nPos + 1))
|
|
{
|
|
AdjustRows();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
AdjustRows();
|
|
}
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
EditBrowseBox::GoToRow(nPos);
|
|
m_aBar->InvalidateAll(m_nCurrentPos);
|
|
}
|
|
|
|
void DbGridControl::AppendNew()
|
|
{
|
|
if (!m_pSeekCursor || !(m_nOptions & DbGridControlOptions::Insert))
|
|
return;
|
|
|
|
if (m_nTotalCount < 0) // no RecordCount, yet
|
|
{
|
|
try
|
|
{
|
|
bool bRes = m_pSeekCursor->last();
|
|
|
|
if (bRes)
|
|
{
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
AdjustRows();
|
|
}
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
tools::Long nNewRow = m_nTotalCount + 1;
|
|
if (nNewRow > 0 && GetCurRow() != nNewRow)
|
|
MoveToPosition(nNewRow - 1);
|
|
}
|
|
|
|
void DbGridControl::SetDesignMode(bool bMode)
|
|
{
|
|
if (IsDesignMode() == bMode)
|
|
return;
|
|
|
|
// adjust Enable/Disable for design mode so that the headerbar remains configurable
|
|
if (bMode)
|
|
{
|
|
if (!IsEnabled())
|
|
{
|
|
Enable();
|
|
GetDataWindow().Disable();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// disable completely
|
|
if (!GetDataWindow().IsEnabled())
|
|
Disable();
|
|
}
|
|
|
|
m_bDesignMode = bMode;
|
|
GetDataWindow().SetMouseTransparent(bMode);
|
|
SetMouseTransparent(bMode);
|
|
|
|
m_aBar->InvalidateAll(m_nCurrentPos, true);
|
|
}
|
|
|
|
void DbGridControl::SetFilterMode(bool bMode)
|
|
{
|
|
if (IsFilterMode() == bMode)
|
|
return;
|
|
|
|
m_bFilterMode = bMode;
|
|
|
|
if (bMode)
|
|
{
|
|
SetUpdateMode(false);
|
|
|
|
// there is no cursor anymore
|
|
if (IsEditing())
|
|
DeactivateCell();
|
|
RemoveRows(false);
|
|
|
|
m_xEmptyRow = new DbGridRow();
|
|
|
|
// setting the new filter controls
|
|
for (auto const & pCurCol : m_aColumns)
|
|
{
|
|
if (!pCurCol->IsHidden())
|
|
pCurCol->UpdateControl();
|
|
}
|
|
|
|
// one row for filtering
|
|
RowInserted(0);
|
|
SetUpdateMode(true);
|
|
}
|
|
else
|
|
setDataSource(Reference< XRowSet > ());
|
|
}
|
|
|
|
OUString DbGridControl::GetCellText(sal_Int32 _nRow, sal_uInt16 _nColId) const
|
|
{
|
|
size_t Location = GetModelColumnPos( _nColId );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
OUString sRet;
|
|
if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
|
|
sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
|
|
return sRet;
|
|
}
|
|
|
|
OUString DbGridControl::GetCurrentRowCellText(DbGridColumn const * pColumn,const DbGridRowRef& _rRow) const
|
|
{
|
|
// text output for a single row
|
|
OUString aText;
|
|
if ( pColumn && IsValid(_rRow) )
|
|
aText = pColumn->GetCellText(_rRow.get(), m_xFormatter);
|
|
return aText;
|
|
}
|
|
|
|
sal_uInt32 DbGridControl::GetTotalCellWidth(sal_Int32 nRow, sal_uInt16 nColId)
|
|
{
|
|
if (SeekRow(nRow))
|
|
{
|
|
size_t Location = GetModelColumnPos( nColId );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
|
|
}
|
|
else
|
|
return 30; // FIXME magic number for default cell width
|
|
}
|
|
|
|
void DbGridControl::PreExecuteRowContextMenu(weld::Menu& rMenu)
|
|
{
|
|
bool bDelete = (m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount() && !IsCurrentAppending();
|
|
// if only a blank row is selected then do not delete
|
|
bDelete = bDelete && !((m_nOptions & DbGridControlOptions::Insert) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
|
|
|
|
rMenu.set_visible(u"delete"_ustr, bDelete);
|
|
rMenu.set_visible(u"save"_ustr, IsModified());
|
|
|
|
// the undo is more difficult
|
|
bool bCanUndo = IsModified();
|
|
int nState = -1;
|
|
if (m_aMasterStateProvider.IsSet())
|
|
nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
|
|
bCanUndo &= ( 0 != nState );
|
|
|
|
rMenu.set_visible(u"undo"_ustr, bCanUndo);
|
|
}
|
|
|
|
void DbGridControl::PostExecuteRowContextMenu(const OUString& rExecutionResult)
|
|
{
|
|
if (rExecutionResult == "delete")
|
|
{
|
|
// delete asynchronously
|
|
if (m_nDeleteEvent)
|
|
Application::RemoveUserEvent(m_nDeleteEvent);
|
|
m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
|
|
}
|
|
else if (rExecutionResult == "undo")
|
|
Undo();
|
|
else if (rExecutionResult == "save")
|
|
SaveRow();
|
|
}
|
|
|
|
void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt)
|
|
{
|
|
SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
|
|
SolarMutexGuard aGuard;
|
|
// prop "IsModified" changed ?
|
|
// during update don't care about the modified state
|
|
if (IsUpdating() || evt.PropertyName != FM_PROP_ISMODIFIED)
|
|
return;
|
|
|
|
Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
|
|
DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
|
|
bool bIsNew = false;
|
|
if (xSource.is())
|
|
bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
|
|
|
|
if (bIsNew && m_xCurrentRow.is())
|
|
{
|
|
DBG_ASSERT(::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ROWCOUNTFINAL)), "DbGridControl::DataSourcePropertyChanged : somebody moved the form to a new record before the row count was final !");
|
|
sal_Int32 nRecordCount = 0;
|
|
xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
|
|
if (::comphelper::getBOOL(evt.NewValue))
|
|
{ // modified state changed from sal_False to sal_True and we're on an insert row
|
|
// -> we've to add a new grid row
|
|
if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
|
|
{
|
|
RowInserted(GetRowCount());
|
|
InvalidateStatusCell(m_nCurrentPos);
|
|
m_aBar->InvalidateAll(m_nCurrentPos);
|
|
}
|
|
}
|
|
else
|
|
{ // modified state changed from sal_True to sal_False and we're on an insert row
|
|
// we have two "new row"s at the moment : the one we're editing currently (where the current
|
|
// column is the only dirty element) and a "new new" row which is completely clean. As the first
|
|
// one is about to be cleaned, too, the second one is obsolete now.
|
|
if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
|
|
{
|
|
RowRemoved(GetRowCount() - 1);
|
|
InvalidateStatusCell(m_nCurrentPos);
|
|
m_aBar->InvalidateAll(m_nCurrentPos);
|
|
}
|
|
}
|
|
}
|
|
if (m_xCurrentRow.is())
|
|
{
|
|
m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean);
|
|
m_xCurrentRow->SetNew( bIsNew );
|
|
InvalidateStatusCell(m_nCurrentPos);
|
|
SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
|
|
}
|
|
}
|
|
|
|
void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
|
|
{
|
|
if (!m_pSeekCursor || IsResizing())
|
|
return;
|
|
|
|
sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rPosPixel.X()));
|
|
tools::Long nRow = GetRowAtYPosPixel(rPosPixel.Y());
|
|
if (nColId != HandleColumnId && nRow >= 0)
|
|
{
|
|
if (GetDataWindow().IsMouseCaptured())
|
|
GetDataWindow().ReleaseMouse();
|
|
|
|
size_t Location = GetModelColumnPos( nColId );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
|
|
pTransferable->StartDrag(this, DND_ACTION_COPY);
|
|
}
|
|
}
|
|
|
|
bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
|
|
{
|
|
return (_nRow >= 0)
|
|
&& (_nRow < GetRowCount())
|
|
&& (_nColId != HandleColumnId)
|
|
&& (GetModelColumnPos(_nColId) != GRID_COLUMN_NOT_FOUND);
|
|
}
|
|
|
|
void DbGridControl::copyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
|
|
{
|
|
DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
|
|
DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ].get();
|
|
SeekRow(_nRow);
|
|
OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
|
|
}
|
|
|
|
void DbGridControl::executeRowContextMenu(const Point& _rPreferredPos)
|
|
{
|
|
std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, u"svx/ui/rowsmenu.ui"_ustr));
|
|
std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
|
|
|
|
tools::Rectangle aRect(_rPreferredPos, Size(1,1));
|
|
weld::Window* pParent = weld::GetPopupParent(*this, aRect);
|
|
|
|
PreExecuteRowContextMenu(*xContextMenu);
|
|
PostExecuteRowContextMenu(xContextMenu->popup_at_rect(pParent, aRect));
|
|
}
|
|
|
|
void DbGridControl::Command(const CommandEvent& rEvt)
|
|
{
|
|
switch (rEvt.GetCommand())
|
|
{
|
|
case CommandEventId::ContextMenu:
|
|
{
|
|
if ( !m_pSeekCursor )
|
|
{
|
|
EditBrowseBox::Command(rEvt);
|
|
return;
|
|
}
|
|
|
|
if ( !rEvt.IsMouseEvent() )
|
|
{ // context menu requested by keyboard
|
|
if ( GetSelectRowCount() )
|
|
{
|
|
tools::Long nRow = FirstSelectedRow( );
|
|
|
|
::tools::Rectangle aRowRect( GetRowRectPixel( nRow ) );
|
|
executeRowContextMenu(aRowRect.LeftCenter());
|
|
|
|
// handled
|
|
return;
|
|
}
|
|
}
|
|
|
|
sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X()));
|
|
tools::Long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
|
|
|
|
if (nColId == HandleColumnId)
|
|
{
|
|
executeRowContextMenu(rEvt.GetMousePosPixel());
|
|
}
|
|
else if (canCopyCellText(nRow, nColId))
|
|
{
|
|
::tools::Rectangle aRect(rEvt.GetMousePosPixel(), Size(1, 1));
|
|
weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
|
|
std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"svx/ui/cellmenu.ui"_ustr));
|
|
std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
|
|
if (!xContextMenu->popup_at_rect(pPopupParent, aRect).isEmpty())
|
|
copyCellText(nRow, nColId);
|
|
}
|
|
else
|
|
{
|
|
EditBrowseBox::Command(rEvt);
|
|
return;
|
|
}
|
|
|
|
[[fallthrough]];
|
|
}
|
|
default:
|
|
EditBrowseBox::Command(rEvt);
|
|
}
|
|
}
|
|
|
|
IMPL_LINK_NOARG(DbGridControl, OnDelete, void*, void)
|
|
{
|
|
m_nDeleteEvent = nullptr;
|
|
DeleteSelectedRows();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(DbGridControl, RearrangeHdl, Timer*, void)
|
|
{
|
|
if (isDisposed())
|
|
return;
|
|
|
|
// and give it a chance to rearrange
|
|
Point aPoint = GetControlArea().TopLeft();
|
|
sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
|
|
ArrangeControls(nX, aPoint.Y());
|
|
// tdf#155364 like tdf#97731 if the reserved area changed size, give
|
|
// the controls a chance to adapt to the new size
|
|
bool bChanged = ReserveControlArea(nX);
|
|
if (bChanged)
|
|
{
|
|
ArrangeControls(nX, aPoint.Y());
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void DbGridControl::DeleteSelectedRows()
|
|
{
|
|
DBG_ASSERT(GetSelection(), "no selection!!!");
|
|
|
|
if (!m_pSeekCursor)
|
|
return;
|
|
}
|
|
|
|
CellController* DbGridControl::GetController(sal_Int32 /*nRow*/, sal_uInt16 nColumnId)
|
|
{
|
|
if (!IsValid(m_xCurrentRow) || !IsEnabled())
|
|
return nullptr;
|
|
|
|
size_t Location = GetModelColumnPos(nColumnId);
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
if (!pColumn)
|
|
return nullptr;
|
|
|
|
CellController* pReturn = nullptr;
|
|
if (IsFilterMode())
|
|
pReturn = pColumn->GetController().get();
|
|
else
|
|
{
|
|
if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
|
|
{
|
|
if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
|
|
return nullptr;
|
|
}
|
|
|
|
bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Insert));
|
|
bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Update));
|
|
|
|
if ((bInsert && !pColumn->IsAutoValue()) || bUpdate)
|
|
{
|
|
pReturn = pColumn->GetController().get();
|
|
}
|
|
}
|
|
return pReturn;
|
|
}
|
|
|
|
void DbGridControl::CellModified()
|
|
{
|
|
SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
|
|
|
|
{
|
|
::osl::MutexGuard aGuard(m_aAdjustSafety);
|
|
if (m_nAsynAdjustEvent)
|
|
{
|
|
SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
|
|
RemoveUserEvent(m_nAsynAdjustEvent);
|
|
m_nAsynAdjustEvent = nullptr;
|
|
|
|
// force the call : this should be no problem as we're probably running in the solar thread here
|
|
// (cell modified is triggered by user actions)
|
|
if (m_bPendingAdjustRows)
|
|
AdjustRows();
|
|
else
|
|
AdjustDataSource();
|
|
}
|
|
}
|
|
|
|
if (IsFilterMode() || !IsValid(m_xCurrentRow) || m_xCurrentRow->IsModified())
|
|
return;
|
|
|
|
// enable edit mode
|
|
// a data set should be inserted
|
|
if (m_xCurrentRow->IsNew())
|
|
{
|
|
m_xCurrentRow->SetStatus(GridRowStatus::Modified);
|
|
SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
|
|
// if no row was added yet, do it now
|
|
if (m_nCurrentPos == GetRowCount() - 1)
|
|
{
|
|
// increment RowCount
|
|
RowInserted(GetRowCount());
|
|
InvalidateStatusCell(m_nCurrentPos);
|
|
m_aBar->InvalidateAll(m_nCurrentPos);
|
|
}
|
|
}
|
|
else if (m_xCurrentRow->GetStatus() != GridRowStatus::Modified)
|
|
{
|
|
m_xCurrentRow->SetState(m_pDataCursor.get(), false);
|
|
SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
|
|
m_xCurrentRow->SetStatus(GridRowStatus::Modified);
|
|
SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
|
|
InvalidateStatusCell(m_nCurrentPos);
|
|
}
|
|
}
|
|
|
|
void DbGridControl::Dispatch(sal_uInt16 nId)
|
|
{
|
|
if (nId == BROWSER_CURSORENDOFFILE)
|
|
{
|
|
if (m_nOptions & DbGridControlOptions::Insert)
|
|
AppendNew();
|
|
else
|
|
MoveToLast();
|
|
}
|
|
else
|
|
EditBrowseBox::Dispatch(nId);
|
|
}
|
|
|
|
void DbGridControl::Undo()
|
|
{
|
|
if (IsFilterMode() || !IsValid(m_xCurrentRow) || !IsModified())
|
|
return;
|
|
|
|
// check if we have somebody doin' the UNDO for us
|
|
int nState = -1;
|
|
if (m_aMasterStateProvider.IsSet())
|
|
nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
|
|
if (nState>0)
|
|
{ // yes, we have, and the slot is enabled
|
|
DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
|
|
bool lResult = m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Undo);
|
|
if (lResult)
|
|
// handled
|
|
return;
|
|
}
|
|
else if (nState == 0)
|
|
// yes, we have, and the slot is disabled
|
|
return;
|
|
|
|
BeginCursorAction();
|
|
|
|
bool bAppending = m_xCurrentRow->IsNew();
|
|
bool bDirty = m_xCurrentRow->IsModified();
|
|
|
|
try
|
|
{
|
|
// cancel editing
|
|
Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
|
|
// no effects if we're not updating currently
|
|
if (bAppending)
|
|
// just refresh the row
|
|
xUpdateCursor->moveToInsertRow();
|
|
else
|
|
xUpdateCursor->cancelRowUpdates();
|
|
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
DBG_UNHANDLED_EXCEPTION("svx");
|
|
}
|
|
|
|
EndCursorAction();
|
|
|
|
m_xDataRow->SetState(m_pDataCursor.get(), false);
|
|
if (m_xPaintRow == m_xCurrentRow)
|
|
m_xPaintRow = m_xCurrentRow = m_xDataRow;
|
|
else
|
|
m_xCurrentRow = m_xDataRow;
|
|
|
|
if (bAppending && (EditBrowseBox::IsModified() || bDirty))
|
|
// remove the row
|
|
if (m_nCurrentPos == GetRowCount() - 2)
|
|
{ // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
|
|
// caused our data source form to be reset - which should be the usual case...)
|
|
RowRemoved(GetRowCount() - 1);
|
|
m_aBar->InvalidateAll(m_nCurrentPos);
|
|
}
|
|
|
|
RowModified(m_nCurrentPos);
|
|
}
|
|
|
|
void DbGridControl::resetCurrentRow()
|
|
{
|
|
if (IsModified())
|
|
{
|
|
// scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
|
|
// is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
|
|
// the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
|
|
// data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
|
|
// would never delete the obsolete "second insert row". Thus in this special case this method here
|
|
// is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
|
|
// "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
|
|
Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
|
|
if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
|
|
{
|
|
// are we on a new row currently ?
|
|
if (m_xCurrentRow->IsNew())
|
|
{
|
|
if (m_nCurrentPos == GetRowCount() - 2)
|
|
{
|
|
RowRemoved(GetRowCount() - 1);
|
|
m_aBar->InvalidateAll(m_nCurrentPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
// update the rows
|
|
m_xDataRow->SetState(m_pDataCursor.get(), false);
|
|
if (m_xPaintRow == m_xCurrentRow)
|
|
m_xPaintRow = m_xCurrentRow = m_xDataRow;
|
|
else
|
|
m_xCurrentRow = m_xDataRow;
|
|
}
|
|
|
|
RowModified(GetCurRow()); // will update the current controller if affected
|
|
}
|
|
|
|
void DbGridControl::RowModified( sal_Int32 nRow )
|
|
{
|
|
if (nRow == m_nCurrentPos && IsEditing())
|
|
{
|
|
CellControllerRef aTmpRef = Controller();
|
|
aTmpRef->SaveValue();
|
|
InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
|
|
}
|
|
EditBrowseBox::RowModified(nRow);
|
|
}
|
|
|
|
bool DbGridControl::IsModified() const
|
|
{
|
|
return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || EditBrowseBox::IsModified());
|
|
}
|
|
|
|
bool DbGridControl::IsCurrentAppending() const
|
|
{
|
|
return m_xCurrentRow.is() && m_xCurrentRow->IsNew();
|
|
}
|
|
|
|
bool DbGridControl::IsInsertionRow(sal_Int32 nRow) const
|
|
{
|
|
return (m_nOptions & DbGridControlOptions::Insert) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
|
|
}
|
|
|
|
bool DbGridControl::SaveModified()
|
|
{
|
|
SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
|
|
DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
|
|
if (!IsValid(m_xCurrentRow))
|
|
return true;
|
|
|
|
// accept input for this field
|
|
// Where there changes at the current input field?
|
|
if (!EditBrowseBox::IsModified())
|
|
return true;
|
|
|
|
size_t Location = GetModelColumnPos( GetCurColumnId() );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
bool bOK = pColumn && pColumn->Commit();
|
|
DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
|
|
if ( !Controller().is() )
|
|
// this might happen if the callbacks implicitly triggered by Commit
|
|
// fiddled with the form or the control ...
|
|
// (Note that this here is a workaround, at most. We need a general concept how
|
|
// to treat this, you can imagine an arbitrary number of scenarios where a callback
|
|
// triggers something which leaves us in an expected state.)
|
|
// #i67147# / 2006-07-17 / frank.schoenheit@sun.com
|
|
return bOK;
|
|
|
|
if (bOK)
|
|
{
|
|
Controller()->SaveValue();
|
|
|
|
if ( IsValid(m_xCurrentRow) )
|
|
{
|
|
m_xCurrentRow->SetState(m_pDataCursor.get(), false);
|
|
SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
|
|
InvalidateStatusCell( m_nCurrentPos );
|
|
}
|
|
else
|
|
{
|
|
SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
|
|
}
|
|
}
|
|
|
|
return bOK;
|
|
}
|
|
|
|
bool DbGridControl::SaveRow()
|
|
{
|
|
SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
|
|
// valid row
|
|
if (!IsValid(m_xCurrentRow) || !IsModified())
|
|
return true;
|
|
// value of the controller was not saved, yet
|
|
else if (Controller().is() && Controller()->IsValueChangedFromSaved())
|
|
{
|
|
if (!SaveModified())
|
|
return false;
|
|
}
|
|
m_bUpdating = true;
|
|
|
|
BeginCursorAction();
|
|
bool bAppending = m_xCurrentRow->IsNew();
|
|
bool bSuccess = false;
|
|
try
|
|
{
|
|
Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
|
|
if (bAppending)
|
|
xUpdateCursor->insertRow();
|
|
else
|
|
xUpdateCursor->updateRow();
|
|
bSuccess = true;
|
|
}
|
|
catch(SQLException&)
|
|
{
|
|
EndCursorAction();
|
|
m_bUpdating = false;
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (bSuccess)
|
|
{
|
|
// if we are appending we still sit on the insert row
|
|
// we don't move just clear the flags not to move on the current row
|
|
m_xCurrentRow->SetState(m_pDataCursor.get(), false);
|
|
SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
|
|
m_xCurrentRow->SetNew(false);
|
|
|
|
// adjust the seekcursor if it is on the same position as the datacursor
|
|
if (m_nSeekPos == m_nCurrentPos || bAppending)
|
|
{
|
|
// get the bookmark to refetch the data
|
|
// in insert mode we take the new bookmark of the data cursor
|
|
Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
|
|
m_pSeekCursor->moveToBookmark(aBookmark);
|
|
// get the data
|
|
m_xSeekRow->SetState(m_pSeekCursor.get(), true);
|
|
m_nSeekPos = m_pSeekCursor->getRow() - 1;
|
|
}
|
|
}
|
|
// and repaint the row
|
|
RowModified(m_nCurrentPos);
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
}
|
|
|
|
m_bUpdating = false;
|
|
EndCursorAction();
|
|
|
|
// The old code returned (nRecords != 0) here.
|
|
// Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
|
|
// which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
|
|
// is zero, this simply means all fields had their original values.
|
|
// FS - 06.12.99 - 70502
|
|
return true;
|
|
}
|
|
|
|
bool DbGridControl::PreNotify(NotifyEvent& rEvt)
|
|
{
|
|
// do not handle events of the Navbar
|
|
if (m_aBar->IsWindowOrChild(rEvt.GetWindow()))
|
|
return BrowseBox::PreNotify(rEvt);
|
|
|
|
switch (rEvt.GetType())
|
|
{
|
|
case NotifyEventType::KEYINPUT:
|
|
{
|
|
const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
|
|
|
|
sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
|
|
bool bShift = pKeyEvent->GetKeyCode().IsShift();
|
|
bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
|
|
bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
|
|
if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
|
|
{
|
|
// Ctrl-Tab is used to step out of the control, without traveling to the
|
|
// remaining cells first
|
|
// -> build a new key event without the Ctrl-key, and let the very base class handle it
|
|
vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
|
|
KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
|
|
|
|
// call the Control - our direct base class will interpret this in a way we do not want (and do
|
|
// a cell traveling)
|
|
Control::KeyInput( aNewEvent );
|
|
return true;
|
|
}
|
|
|
|
if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
|
|
{
|
|
if (IsModified())
|
|
{
|
|
Undo();
|
|
return true;
|
|
}
|
|
}
|
|
else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
|
|
{
|
|
if ((m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount())
|
|
{
|
|
// delete asynchronously
|
|
if (m_nDeleteEvent)
|
|
Application::RemoveUserEvent(m_nDeleteEvent);
|
|
m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[[fallthrough]];
|
|
}
|
|
default:
|
|
return EditBrowseBox::PreNotify(rEvt);
|
|
}
|
|
}
|
|
|
|
bool DbGridControl::IsTabAllowed(bool bRight) const
|
|
{
|
|
if (bRight)
|
|
// Tab only if not on the _last_ row
|
|
return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
|
|
GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
|
|
else
|
|
{
|
|
// Tab only if not on the _first_ row
|
|
return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
|
|
}
|
|
}
|
|
|
|
void DbGridControl::KeyInput( const KeyEvent& rEvt )
|
|
{
|
|
if (rEvt.GetKeyCode().GetFunction() == KeyFuncType::COPY)
|
|
{
|
|
tools::Long nRow = GetCurRow();
|
|
sal_uInt16 nColId = GetCurColumnId();
|
|
if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
|
|
{
|
|
size_t Location = GetModelColumnPos( nColId );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
|
|
return;
|
|
}
|
|
}
|
|
EditBrowseBox::KeyInput(rEvt);
|
|
}
|
|
|
|
void DbGridControl::HideColumn(sal_uInt16 nId)
|
|
{
|
|
DeactivateCell();
|
|
|
|
// determine the col for the focus to set to after removal
|
|
sal_uInt16 nPos = GetViewColumnPos(nId);
|
|
sal_uInt16 nNewColId = nPos == (ColCount()-1)
|
|
? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
|
|
: GetColumnIdFromViewPos(nPos+1); // take the next
|
|
|
|
tools::Long lCurrentWidth = GetColumnWidth(nId);
|
|
EditBrowseBox::RemoveColumn(nId);
|
|
// don't use my own RemoveColumn, this would remove it from m_aColumns, too
|
|
|
|
// update my model
|
|
size_t Location = GetModelColumnPos( nId );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
|
|
if (pColumn)
|
|
{
|
|
pColumn->m_bHidden = true;
|
|
pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
|
|
}
|
|
|
|
// and reset the focus
|
|
if ( nId == GetCurColumnId() )
|
|
GoToColumnId( nNewColId );
|
|
}
|
|
|
|
void DbGridControl::ShowColumn(sal_uInt16 nId)
|
|
{
|
|
sal_uInt16 nPos = GetModelColumnPos(nId);
|
|
DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
|
|
if (nPos == GRID_COLUMN_NOT_FOUND)
|
|
return;
|
|
|
|
DbGridColumn* pColumn = m_aColumns[ nPos ].get();
|
|
if (!pColumn->IsHidden())
|
|
{
|
|
DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
|
|
// if the column isn't marked as hidden, it should be visible, shouldn't it ?
|
|
return;
|
|
}
|
|
DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
|
|
// the opposite situation ...
|
|
|
|
// to determine the new view position we need an adjacent non-hidden column
|
|
sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
|
|
// first search the cols to the right
|
|
for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
|
|
{
|
|
DbGridColumn* pCurCol = m_aColumns[ i ].get();
|
|
if (!pCurCol->IsHidden())
|
|
{
|
|
nNextNonHidden = i;
|
|
break;
|
|
}
|
|
}
|
|
if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
|
|
{
|
|
// then to the left
|
|
for ( size_t i = nPos; i > 0; --i )
|
|
{
|
|
DbGridColumn* pCurCol = m_aColumns[ i-1 ].get();
|
|
if (!pCurCol->IsHidden())
|
|
{
|
|
nNextNonHidden = i-1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
|
|
? 1 // there is no visible column -> insert behind the handle col
|
|
: GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
|
|
// the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
|
|
// a position 1 for the first non-handle col -> +1
|
|
DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
|
|
// we found a col marked as visible but got no view pos for it ...
|
|
|
|
if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
|
|
// nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
|
|
++nNewViewPos;
|
|
|
|
DeactivateCell();
|
|
|
|
OUString aName;
|
|
pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
|
|
InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE, nNewViewPos);
|
|
pColumn->m_bHidden = false;
|
|
|
|
ActivateCell();
|
|
Invalidate();
|
|
}
|
|
|
|
sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
|
|
{
|
|
if (nPos >= m_aColumns.size())
|
|
{
|
|
OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
|
|
return GRID_COLUMN_NOT_FOUND;
|
|
}
|
|
|
|
DbGridColumn* pCol = m_aColumns[ nPos ].get();
|
|
#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
|
|
// in the debug version, we convert the ModelPos into a ViewPos and compare this with the
|
|
// value we will return (nId at the corresponding Col in m_aColumns)
|
|
|
|
if (!pCol->IsHidden())
|
|
{ // makes sense only if the column is visible
|
|
sal_uInt16 nViewPos = nPos;
|
|
for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
|
|
if ( m_aColumns[ i ]->IsHidden())
|
|
--nViewPos;
|
|
|
|
DBG_ASSERT(GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
|
|
"DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?");
|
|
}
|
|
#endif
|
|
return pCol->GetId();
|
|
}
|
|
|
|
sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
|
|
{
|
|
for ( size_t i = 0; i < m_aColumns.size(); ++i )
|
|
if ( m_aColumns[ i ]->GetId() == nId )
|
|
return i;
|
|
|
|
return GRID_COLUMN_NOT_FOUND;
|
|
}
|
|
|
|
void DbGridControl::implAdjustInSolarThread(bool _bRows)
|
|
{
|
|
SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
|
|
::osl::MutexGuard aGuard(m_aAdjustSafety);
|
|
if (!Application::IsMainThread())
|
|
{
|
|
m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true);
|
|
m_bPendingAdjustRows = _bRows;
|
|
if (_bRows)
|
|
SAL_INFO("svx.fmcomp", "posting an AdjustRows");
|
|
else
|
|
SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
|
|
}
|
|
else
|
|
{
|
|
if (_bRows)
|
|
SAL_INFO("svx.fmcomp", "doing an AdjustRows");
|
|
else
|
|
SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
|
|
// always adjust the rows before adjusting the data source
|
|
// If this is not necessary (because the row count did not change), nothing is done
|
|
// The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
|
|
// to a position behind row count know 'til now, the cursorMoved notification may come before the
|
|
// RowCountChanged notification
|
|
// 94093 - 02.11.2001 - frank.schoenheit@sun.com
|
|
AdjustRows();
|
|
|
|
if ( !_bRows )
|
|
AdjustDataSource();
|
|
}
|
|
}
|
|
|
|
IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat, void)
|
|
{
|
|
m_nAsynAdjustEvent = nullptr;
|
|
|
|
AdjustRows();
|
|
// see implAdjustInSolarThread for a comment why we do this every time
|
|
|
|
if ( !pAdjustWhat )
|
|
AdjustDataSource();
|
|
}
|
|
|
|
void DbGridControl::BeginCursorAction()
|
|
{
|
|
for (const auto& rListener : m_aFieldListeners)
|
|
{
|
|
GridFieldValueListener* pCurrent = rListener.second;
|
|
if (pCurrent)
|
|
pCurrent->suspend();
|
|
}
|
|
|
|
if (m_pDataSourcePropListener)
|
|
m_pDataSourcePropListener->suspend();
|
|
}
|
|
|
|
void DbGridControl::EndCursorAction()
|
|
{
|
|
for (const auto& rListener : m_aFieldListeners)
|
|
{
|
|
GridFieldValueListener* pCurrent = rListener.second;
|
|
if (pCurrent)
|
|
pCurrent->resume();
|
|
}
|
|
|
|
if (m_pDataSourcePropListener)
|
|
m_pDataSourcePropListener->resume();
|
|
}
|
|
|
|
void DbGridControl::ConnectToFields()
|
|
{
|
|
DBG_ASSERT(m_aFieldListeners.empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
|
|
|
|
for (auto const & pCurrent : m_aColumns)
|
|
{
|
|
sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
|
|
if (GRID_COLUMN_NOT_FOUND == nViewPos)
|
|
continue;
|
|
|
|
Reference< XPropertySet > xField = pCurrent->GetField();
|
|
if (!xField.is())
|
|
continue;
|
|
|
|
// column is visible and bound here
|
|
GridFieldValueListener*& rpListener = m_aFieldListeners[pCurrent->GetId()];
|
|
DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
|
|
rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
|
|
}
|
|
}
|
|
|
|
void DbGridControl::DisconnectFromFields()
|
|
{
|
|
if (m_aFieldListeners.empty())
|
|
return;
|
|
|
|
while (!m_aFieldListeners.empty())
|
|
{
|
|
sal_Int32 nOldSize = m_aFieldListeners.size();
|
|
m_aFieldListeners.begin()->second->dispose();
|
|
DBG_ASSERT(nOldSize > static_cast<sal_Int32>(m_aFieldListeners.size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
|
|
}
|
|
}
|
|
|
|
void DbGridControl::FieldValueChanged(sal_uInt16 _nId)
|
|
{
|
|
osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
|
|
// needed as this may run in a thread other than the main one
|
|
if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED)
|
|
// all other cases are handled elsewhere
|
|
return;
|
|
|
|
size_t Location = GetModelColumnPos( _nId );
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
if (!pColumn)
|
|
return;
|
|
|
|
std::unique_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard;
|
|
while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired()))
|
|
pGuard.reset(new vcl::SolarMutexTryAndBuyGuard);
|
|
|
|
if (m_bWantDestruction)
|
|
{ // at this moment, within another thread, our destructor tries to destroy the listener which called this method
|
|
// => don't do anything
|
|
// 73365 - 23.02.00 - FS
|
|
return;
|
|
}
|
|
|
|
// and finally do the update ...
|
|
pColumn->UpdateFromField(m_xCurrentRow.get(), m_xFormatter);
|
|
RowModified(GetCurRow());
|
|
}
|
|
|
|
void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
|
|
{
|
|
auto aPos = m_aFieldListeners.find(_nId);
|
|
if (aPos == m_aFieldListeners.end())
|
|
{
|
|
OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
|
|
return;
|
|
}
|
|
|
|
delete aPos->second;
|
|
|
|
m_aFieldListeners.erase(aPos);
|
|
}
|
|
|
|
void DbGridControl::disposing(sal_uInt16 _nId)
|
|
{
|
|
if (_nId == 0)
|
|
{ // the seek cursor is being disposed
|
|
::osl::MutexGuard aGuard(m_aAdjustSafety);
|
|
setDataSource(nullptr, DbGridControlOptions::Readonly); // our clone was disposed so we set our datasource to null to avoid later access to it
|
|
if (m_nAsynAdjustEvent)
|
|
{
|
|
RemoveUserEvent(m_nAsynAdjustEvent);
|
|
m_nAsynAdjustEvent = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
sal_Int32 DbGridControl::GetAccessibleControlCount() const
|
|
{
|
|
return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control
|
|
}
|
|
|
|
Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
|
|
{
|
|
Reference<XAccessible > xRet;
|
|
if ( _nIndex == EditBrowseBox::GetAccessibleControlCount() )
|
|
{
|
|
xRet = m_aBar->GetAccessible();
|
|
}
|
|
else
|
|
xRet = EditBrowseBox::CreateAccessibleControl( _nIndex );
|
|
return xRet;
|
|
}
|
|
|
|
Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
|
|
{
|
|
sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
|
|
size_t Location = GetModelColumnPos(nColumnId);
|
|
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
|
|
if ( pColumn )
|
|
{
|
|
Reference< css::awt::XControl> xInt(pColumn->GetCell());
|
|
Reference< css::awt::XCheckBox> xBox(xInt,UNO_QUERY);
|
|
if ( xBox.is() )
|
|
{
|
|
TriState eValue = TRISTATE_FALSE;
|
|
switch( xBox->getState() )
|
|
{
|
|
case 0:
|
|
eValue = TRISTATE_FALSE;
|
|
break;
|
|
case 1:
|
|
eValue = TRISTATE_TRUE;
|
|
break;
|
|
case 2:
|
|
eValue = TRISTATE_INDET;
|
|
break;
|
|
}
|
|
return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
|
|
}
|
|
}
|
|
return EditBrowseBox::CreateAccessibleCell( _nRow, _nColumnPos );
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|