diff options
Diffstat (limited to 'svx/source/fmcomp/gridctrl.cxx')
-rw-r--r-- | svx/source/fmcomp/gridctrl.cxx | 3893 |
1 files changed, 3893 insertions, 0 deletions
diff --git a/svx/source/fmcomp/gridctrl.cxx b/svx/source/fmcomp/gridctrl.cxx new file mode 100644 index 000000000000..1944bd47dad5 --- /dev/null +++ b/svx/source/fmcomp/gridctrl.cxx @@ -0,0 +1,3893 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" + +#ifndef _SVX_FMHELP_HRC +#include "fmhelp.hrc" +#endif +#include <svx/gridctrl.hxx> +#include "gridcell.hxx" +#include "svx/dbtoolsclient.hxx" +#include "svx/fmtools.hxx" +#include <svtools/stringtransfer.hxx> + +#ifndef _SVX_FMPROP_HRC +#include "fmprop.hrc" +#endif +#include <svtools/stringtransfer.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/XResultSetUpdate.hpp> +#include <com/sun/star/sdbcx/Privilege.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/util/XNumberFormatter.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyChangeEvent.hpp> +#include <comphelper/extract.hxx> +#include <tools/resid.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/sound.hxx> +#include <vcl/menu.hxx> + +#ifndef _SVX_FMRESIDS_HRC +#include "fmresids.hrc" +#endif + +#ifndef _SVX_SVXIDS_HRC +#include <svx/svxids.hrc> +#endif +#include <tools/shl.hxx> +#include <svx/dialmgr.hxx> +#include "fmservs.hxx" +#include "sdbdatacolumn.hxx" + +#define HANDLE_ID 0 + +#include <comphelper/stl_types.hxx> +#include <comphelper/property.hxx> +#include "trace.hxx" + +#include <algorithm> + +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() == GRS_CLEAN ? "CLEAN" : row->GetStatus() == GRS_MODIFIED ? "MODIFIED" : row->GetStatus() == GRS_DELETED ? "DELETED" : "INVALID" + + +#define DEFAULT_BROWSE_MODE \ + BROWSER_COLUMNSELECTION \ + | BROWSER_MULTISELECTION \ + | BROWSER_KEEPSELECTION \ + | BROWSER_TRACKING_TIPS \ + | BROWSER_HLINESFULL \ + | BROWSER_VLINESFULL \ + | BROWSER_HEADERBAR_NEW \ + +class RowSetEventListener : public ::cppu::WeakImplHelper1<XRowsChangeListener> +{ + DbGridControl* m_pControl; +public: + RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl) + { + } +private: + // XEventListener + virtual void SAL_CALL disposing(const ::com::sun::star::lang::EventObject& /*i_aEvt*/) throw ( RuntimeException ) + { + } + virtual void SAL_CALL rowsChanged(const ::com::sun::star::sdb::RowsChangeEvent& i_aEvt) throw ( RuntimeException ) + { + if ( i_aEvt.Action == RowChangeAction::UPDATE ) + { + ::DbGridControl::GrantControlAccess aAccess; + CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess); + const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess); + const Any* pIter = i_aEvt.Bookmarks.getConstArray(); + const Any* pEnd = pIter + i_aEvt.Bookmarks.getLength(); + for(;pIter != pEnd;++pIter) + { + pSeek->moveToBookmark(*pIter); + // get the data + rSeekRow->SetState(pSeek, sal_True); + sal_Int32 nSeekPos = pSeek->getRow() - 1; + m_pControl->SetSeekPos(nSeekPos,aAccess); + m_pControl->RowModified(nSeekPos); + } + } + } +}; +//============================================================================== + +class GridFieldValueListener; +DECLARE_STL_MAP(sal_uInt16, GridFieldValueListener*, ::std::less<sal_uInt16>, ColumnFieldValueListeners); + +//============================================================================== + +DBG_NAME(GridFieldValueListener) +class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener +{ + osl::Mutex m_aMutex; + DbGridControl& m_rParent; + ::comphelper::OPropertyChangeMultiplexer* m_pRealListener; + sal_uInt16 m_nId; + sal_Int16 m_nSuspended; + sal_Bool m_bDisposed : 1; + +public: + GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId); + virtual ~GridFieldValueListener(); + + virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ); + + void suspend() { ++m_nSuspended; } + void resume() { --m_nSuspended; } + + void dispose(); +}; +//------------------------------------------------------------------------------ +GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId) + :OPropertyChangeListener(m_aMutex) + ,m_rParent(_rParent) + ,m_pRealListener(NULL) + ,m_nId(_nId) + ,m_nSuspended(0) + ,m_bDisposed(sal_False) +{ + DBG_CTOR(GridFieldValueListener, NULL); + if (_rField.is()) + { + m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField); + m_pRealListener->addProperty(FM_PROP_VALUE); + m_pRealListener->acquire(); + } +} + +//------------------------------------------------------------------------------ +GridFieldValueListener::~GridFieldValueListener() +{ + DBG_DTOR(GridFieldValueListener, NULL); + dispose(); +} + +//------------------------------------------------------------------------------ +void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& _evt) throw( RuntimeException ) +{ + DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !"); + if (m_nSuspended <= 0) + m_rParent.FieldValueChanged(m_nId, _evt); +} + +//------------------------------------------------------------------------------ +void GridFieldValueListener::dispose() +{ + if (m_bDisposed) + { + DBG_ASSERT(m_pRealListener == NULL, "GridFieldValueListener::dispose : inconsistent !"); + return; + } + + if (m_pRealListener) + { + m_pRealListener->dispose(); + m_pRealListener->release(); + m_pRealListener = NULL; + } + + m_bDisposed = sal_True; + m_rParent.FieldListenerDisposing(m_nId); +} + +//============================================================================== + +class DisposeListenerGridBridge : public FmXDisposeListener +{ + osl::Mutex m_aMutex; + DbGridControl& m_rParent; + FmXDisposeMultiplexer* m_pRealListener; + +public: + DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId = -1); + virtual ~DisposeListenerGridBridge(); + + virtual void disposing(const EventObject& _rEvent, sal_Int16 _nId) throw( RuntimeException ) { m_rParent.disposing(_nId, _rEvent); } +}; + +//============================================================================== + + +DBG_NAME(DisposeListenerGridBridge) +//------------------------------------------------------------------------------ +DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject, sal_Int16 _rId) + :FmXDisposeListener(m_aMutex) + ,m_rParent(_rParent) + ,m_pRealListener(NULL) +{ + DBG_CTOR(DisposeListenerGridBridge,NULL); + + if (_rxObject.is()) + { + m_pRealListener = new FmXDisposeMultiplexer(this, _rxObject, _rId); + m_pRealListener->acquire(); + } +} + +//------------------------------------------------------------------------------ +DisposeListenerGridBridge::~DisposeListenerGridBridge() +{ + if (m_pRealListener) + { + m_pRealListener->dispose(); + m_pRealListener->release(); + m_pRealListener = NULL; + } + + DBG_DTOR(DisposeListenerGridBridge,NULL); +} + +//============================================================================== + +static sal_uInt16 ControlMap[] = + { + DbGridControl::NavigationBar::RECORD_TEXT, + DbGridControl::NavigationBar::RECORD_ABSOLUTE, + DbGridControl::NavigationBar::RECORD_OF, + DbGridControl::NavigationBar::RECORD_COUNT, + DbGridControl::NavigationBar::RECORD_FIRST, + DbGridControl::NavigationBar::RECORD_NEXT, + DbGridControl::NavigationBar::RECORD_PREV, + DbGridControl::NavigationBar::RECORD_LAST, + DbGridControl::NavigationBar::RECORD_NEW, + 0 + }; + +//------------------------------------------------------------------------------ +sal_Bool CompareBookmark(const Any& aLeft, const Any& aRight) +{ + return ::comphelper::compare(aLeft, aRight); +} + +//============================================================================== +class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener +{ + DbGridControl* m_pParent; + + // a DbGridControl has no mutex, so we use our own as the base class expects one + osl::Mutex m_aMutex; + sal_Int16 m_nSuspended; + +public: + FmXGridSourcePropListener(DbGridControl* _pParent); + + void suspend() { ++m_nSuspended; } + void resume() { --m_nSuspended; } + + virtual void _propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ); +}; + +//------------------------------------------------------------------------------ +FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent) + :OPropertyChangeListener(m_aMutex) + ,m_pParent(_pParent) + ,m_nSuspended(0) +{ + DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !"); +} + +//------------------------------------------------------------------------------ +void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) +{ + DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !"); + if (m_nSuspended <= 0) + m_pParent->DataSourcePropertyChanged(evt); +} + +//============================================================================== +//------------------------------------------------------------------------------ +DbGridControl::NavigationBar::AbsolutePos::AbsolutePos(Window* pParent, WinBits nStyle) + :NumericField(pParent, nStyle) +{ + SetMin(1); + SetFirst(1); + SetSpinSize(1); + + SetDecimalDigits(0); + SetStrictFormat(sal_True); +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::AbsolutePos::KeyInput(const KeyEvent& rEvt) +{ + if (rEvt.GetKeyCode() == KEY_RETURN && GetText().Len()) + { + sal_Int64 nRecord = GetValue(); + if (nRecord < GetMin() || nRecord > GetMax()) + return; + else + ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord)); + } + else if (rEvt.GetKeyCode() == KEY_TAB) + GetParent()->GetParent()->GrabFocus(); + else + NumericField::KeyInput(rEvt); +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::AbsolutePos::LoseFocus() +{ + NumericField::LoseFocus(); + sal_Int64 nRecord = GetValue(); + if (nRecord < GetMin() || nRecord > GetMax()) + return; + else + { + ((NavigationBar*)GetParent())->PositionDataSource(static_cast<sal_Int32>(nRecord)); + ((NavigationBar*)GetParent())->InvalidateState(NavigationBar::RECORD_ABSOLUTE); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::PositionDataSource(sal_Int32 nRecord) +{ + if (m_bPositioning) + return; + // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition, so protect agains this + // recursion + // 68167 - 13.08.99 - FS + m_bPositioning = sal_True; + ((DbGridControl*)GetParent())->MoveToPosition(nRecord - 1); + m_bPositioning = sal_False; +} + +//------------------------------------------------------------------------------ +DbGridControl::NavigationBar::NavigationBar(Window* pParent, WinBits nStyle) + :Control(pParent, nStyle) + ,m_aRecordText(this, WB_VCENTER) + ,m_aAbsolute(this, WB_VCENTER) + ,m_aRecordOf(this, WB_VCENTER) + ,m_aRecordCount(this, WB_CENTER | WB_VCENTER) + ,m_aFirstBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS) + ,m_aPrevBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS) + ,m_aNextBtn(this, WB_REPEAT|WB_RECTSTYLE|WB_NOPOINTERFOCUS) + ,m_aLastBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS) + ,m_aNewBtn(this, WB_RECTSTYLE|WB_NOPOINTERFOCUS) + ,m_nDefaultWidth(0) + ,m_nCurrentPos(-1) + ,m_bPositioning(sal_False) +{ + m_aFirstBtn.SetSymbol(SYMBOL_FIRST); + m_aPrevBtn.SetSymbol(SYMBOL_PREV); + m_aNextBtn.SetSymbol(SYMBOL_NEXT); + m_aLastBtn.SetSymbol(SYMBOL_LAST); + m_aNewBtn.SetModeImage(((DbGridControl*)pParent)->GetImage(DbGridControl_Base::NEW)); + + m_aFirstBtn.SetHelpId(HID_GRID_TRAVEL_FIRST); + m_aPrevBtn.SetHelpId(HID_GRID_TRAVEL_PREV); + m_aNextBtn.SetHelpId(HID_GRID_TRAVEL_NEXT); + m_aLastBtn.SetHelpId(HID_GRID_TRAVEL_LAST); + m_aNewBtn.SetHelpId(HID_GRID_TRAVEL_NEW); + m_aAbsolute.SetHelpId(HID_GRID_TRAVEL_ABSOLUTE); + m_aRecordCount.SetHelpId(HID_GRID_NUMBEROFRECORDS); + + // Handler fuer Buttons einrichten + m_aFirstBtn.SetClickHdl(LINK(this,NavigationBar,OnClick)); + m_aPrevBtn.SetClickHdl(LINK(this,NavigationBar,OnClick)); + m_aNextBtn.SetClickHdl(LINK(this,NavigationBar,OnClick)); + m_aLastBtn.SetClickHdl(LINK(this,NavigationBar,OnClick)); + m_aNewBtn.SetClickHdl(LINK(this,NavigationBar,OnClick)); + + m_aRecordText.SetText(XubString(SVX_RES(RID_STR_REC_TEXT))); + m_aRecordOf.SetText(XubString(SVX_RES(RID_STR_REC_FROM_TEXT))); + m_aRecordCount.SetText('?'); + + m_nDefaultWidth = ArrangeControls(); + + m_aFirstBtn.Disable(); + m_aPrevBtn.Disable(); + m_aNextBtn.Disable(); + m_aLastBtn.Disable(); + m_aNewBtn.Disable(); + m_aRecordText.Disable(); + m_aRecordOf.Disable(); + m_aRecordCount.Disable(); + m_aAbsolute.Disable(); + + AllSettings aSettings = m_aNextBtn.GetSettings(); + MouseSettings aMouseSettings = aSettings.GetMouseSettings(); + aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4); + aSettings.SetMouseSettings(aMouseSettings); + m_aNextBtn.SetSettings(aSettings, sal_True); + m_aPrevBtn.SetSettings(aSettings, sal_True); + + m_aFirstBtn.Show(); + m_aPrevBtn.Show(); + m_aNextBtn.Show(); + m_aLastBtn.Show(); + m_aNewBtn.Show(); + m_aRecordText.Show(); + m_aRecordOf.Show(); + m_aRecordCount.Show(); + m_aAbsolute.Show(); +} + +namespace +{ + void SetPosAndSize(Button& _rButton,Point& _rPos,const Size& _rSize) + { + _rButton.SetPosPixel( _rPos ); + _rButton.SetSizePixel( _rSize ); + _rPos.X() += (sal_uInt16)_rSize.Width(); + } +} +//------------------------------------------------------------------------------ +sal_uInt16 DbGridControl::NavigationBar::ArrangeControls() +{ + // Positionierung der Controls + // Basisgroessen ermitteln + sal_uInt16 nX = 0; + sal_uInt16 nY = 0; + Rectangle aRect(((DbGridControl*)GetParent())->GetControlArea()); + const long nH = aRect.GetSize().Height(); + Size aBorder = LogicToPixel(Size(3, 3),MAP_APPFONT); + aBorder = Size(CalcZoom(aBorder.Width()), CalcZoom(aBorder.Height())); + + // Controls Groessen und Positionen setzen + // + XubString aText = m_aRecordText.GetText(); + long nTextWidth = m_aRecordText.GetTextWidth(aText); + m_aRecordText.SetPosPixel(Point(nX,nY) ); + m_aRecordText.SetSizePixel(Size(nTextWidth,nH)); + nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width()); + + m_aAbsolute.SetPosPixel( Point(nX,nY)); + m_aAbsolute.SetSizePixel( Size(3*nH,aRect.GetSize().Height()) ); // Heuristik XXXXXXX + nX = sal::static_int_cast< sal_uInt16 >(nX + (3*nH) + aBorder.Width()); + + aText = m_aRecordOf.GetText(); + nTextWidth = m_aRecordOf.GetTextWidth(aText); + m_aRecordOf.SetPosPixel(Point(nX,nY) ); + m_aRecordOf.SetSizePixel(Size(nTextWidth,nH)); + nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width()); + + nTextWidth = m_aRecordCount.GetTextWidth( String::CreateFromAscii("0000000 (00000) *") ); + m_aRecordCount.SetPosPixel(Point(nX,nY) ); + m_aRecordCount.SetSizePixel(Size(nTextWidth,nH)); + nX = sal::static_int_cast< sal_uInt16 >(nX + nTextWidth + aBorder.Width()); + + Point aButtonPos(nX,nY); + Size aButtonSize(nH,nH); + SetPosAndSize(m_aFirstBtn, aButtonPos, aButtonSize); + SetPosAndSize(m_aPrevBtn, aButtonPos, aButtonSize); + SetPosAndSize(m_aNextBtn, aButtonPos, aButtonSize); + SetPosAndSize(m_aLastBtn, aButtonPos, aButtonSize); + SetPosAndSize(m_aNewBtn, aButtonPos, aButtonSize); + + nX = sal::static_int_cast< sal_uInt16 >( + aButtonPos.X() + (sal_uInt16)(nH + aBorder.Width())); + + // Ist der Font des Edits groesser als das Feld? + Font aOutputFont = m_aAbsolute.GetFont(); + if (aOutputFont.GetSize().Height() > nH) + { + Font aApplFont = OutputDevice::GetDefaultFont( + DEFAULTFONT_SANS_UNICODE, + Application::GetSettings().GetUILanguage(), + DEFAULTFONT_FLAGS_ONLYONE, + this + ); + aApplFont.SetSize( Size( 0, nH - 2 ) ); + m_aAbsolute.SetControlFont( aApplFont ); + + aApplFont.SetTransparent( sal_True ); + m_aRecordText.SetControlFont( aApplFont ); + m_aRecordOf.SetControlFont( aApplFont ); + m_aRecordCount.SetControlFont( aApplFont ); + } + return nX; +} + +//------------------------------------------------------------------------------ +IMPL_LINK(DbGridControl::NavigationBar, OnClick, Button *, pButton ) +{ + DbGridControl* pParent = (DbGridControl*)GetParent(); + + if (pParent->m_aMasterSlotExecutor.IsSet()) + { + long lResult = 0; + if (pButton == &m_aFirstBtn) + lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_FIRST); + else if( pButton == &m_aPrevBtn ) + lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_PREV); + else if( pButton == &m_aNextBtn ) + lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEXT); + else if( pButton == &m_aLastBtn ) + lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_LAST); + else if( pButton == &m_aNewBtn ) + lResult = pParent->m_aMasterSlotExecutor.Call((void*)RECORD_NEW); + + if (lResult) + // the link already handled it + return 0; + } + + if (pButton == &m_aFirstBtn) + pParent->MoveToFirst(); + else if( pButton == &m_aPrevBtn ) + pParent->MoveToPrev(); + else if( pButton == &m_aNextBtn ) + pParent->MoveToNext(); + else if( pButton == &m_aLastBtn ) + pParent->MoveToLast(); + else if( pButton == &m_aNewBtn ) + pParent->AppendNew(); + return 0; +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, sal_Bool bAll) +{ + if (m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll) + { + DbGridControl* pParent = (DbGridControl*)GetParent(); + + sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControl::OPT_INSERT) ? 2 : 1); + + // Wann mu� alles invalidiert werden + 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]) + SetState(ControlMap[i++]); + } + else // befindet sich in der Mitte + { + m_nCurrentPos = nCurrentPos; + SetState(NavigationBar::RECORD_COUNT); + SetState(NavigationBar::RECORD_ABSOLUTE); + } + } +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::NavigationBar::GetState(sal_uInt16 nWhich) const +{ + DbGridControl* pParent = (DbGridControl*)GetParent(); + + if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled() + || pParent->IsFilterMode() ) + return sal_False; + else + { + // check if we have a master state provider + if (pParent->m_aMasterStateProvider.IsSet()) + { + long nState = pParent->m_aMasterStateProvider.Call(reinterpret_cast< void* >( nWhich ) ); + if (nState>=0) + return (nState>0); + } + + sal_Bool bAvailable = sal_True; + + switch (nWhich) + { + case NavigationBar::RECORD_FIRST: + case NavigationBar::RECORD_PREV: + bAvailable = m_nCurrentPos > 0; + break; + case NavigationBar::RECORD_NEXT: + if(pParent->m_bRecordCountFinal) + { + bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1; + if (!bAvailable && pParent->GetOptions() & DbGridControl::OPT_INSERT) + bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified(); + } + break; + case NavigationBar::RECORD_LAST: + if(pParent->m_bRecordCountFinal) + { + if (pParent->GetOptions() & DbGridControl::OPT_INSERT) + bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 : + m_nCurrentPos != pParent->GetRowCount() - 2; + else + bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1; + } + break; + case NavigationBar::RECORD_NEW: + bAvailable = (pParent->GetOptions() & DbGridControl::OPT_INSERT) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1; + break; + case NavigationBar::RECORD_ABSOLUTE: + bAvailable = pParent->GetRowCount() > 0; + break; + } + return bAvailable; + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::SetState(sal_uInt16 nWhich) +{ + sal_Bool bAvailable = GetState(nWhich); + DbGridControl* pParent = (DbGridControl*)GetParent(); + Window* pWnd = NULL; + switch (nWhich) + { + case NavigationBar::RECORD_FIRST: + pWnd = &m_aFirstBtn; + break; + case NavigationBar::RECORD_PREV: + pWnd = &m_aPrevBtn; + break; + case NavigationBar::RECORD_NEXT: + pWnd = &m_aNextBtn; + break; + case NavigationBar::RECORD_LAST: + pWnd = &m_aLastBtn; + break; + case NavigationBar::RECORD_NEW: + pWnd = &m_aNewBtn; + break; + case NavigationBar::RECORD_ABSOLUTE: + pWnd = &m_aAbsolute; + if (bAvailable) + { + if (pParent->m_nTotalCount >= 0) + { + if (pParent->IsCurrentAppending()) + m_aAbsolute.SetMax(pParent->m_nTotalCount + 1); + else + m_aAbsolute.SetMax(pParent->m_nTotalCount); + } + else + m_aAbsolute.SetMax(LONG_MAX); + + m_aAbsolute.SetValue(m_nCurrentPos + 1); + } + else + m_aAbsolute.SetText(String()); + break; + case NavigationBar::RECORD_TEXT: + pWnd = &m_aRecordText; + break; + case NavigationBar::RECORD_OF: + pWnd = &m_aRecordOf; + break; + case NavigationBar::RECORD_COUNT: + { + pWnd = &m_aRecordCount; + String aText; + if (bAvailable) + { + if (pParent->GetOptions() & DbGridControl::OPT_INSERT) + { + if (pParent->IsCurrentAppending() && !pParent->IsModified()) + aText = String::CreateFromInt32(pParent->GetRowCount()); + else + aText = String::CreateFromInt32(pParent->GetRowCount() - 1); + } + else + aText = String::CreateFromInt32(pParent->GetRowCount()); + if(!pParent->m_bRecordCountFinal) + aText += String::CreateFromAscii(" *"); + } + else + aText = String(); + + // add the number of selected rows, if applicable + if (pParent->GetSelectRowCount()) + { + String aExtendedInfo(aText); + aExtendedInfo.AppendAscii(" ("); + aExtendedInfo += String::CreateFromInt32(pParent->GetSelectRowCount()); + aExtendedInfo += ')'; + pWnd->SetText(aExtendedInfo); + } + else + pWnd->SetText(aText); + + pParent->SetRealRowCount(aText); + } break; + } + DBG_ASSERT(pWnd, "kein Fenster"); + if (pWnd && (pWnd->IsEnabled() != bAvailable)) + // 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. + // FS - 18.11.99 + pWnd->Enable(bAvailable); +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::Resize() +{ + Control::Resize(); + ArrangeControls(); +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::Paint(const Rectangle& rRect) +{ + Control::Paint(rRect); + Point aAbsolutePos = m_aAbsolute.GetPosPixel(); + Size aAbsoluteSize = m_aAbsolute.GetSizePixel(); + + DrawLine(Point(aAbsolutePos.X() - 1, 0 ), + Point(aAbsolutePos.X() - 1, aAbsolutePos.Y() + aAbsoluteSize.Height())); + + DrawLine(Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, 0 ), + Point(aAbsolutePos.X() + aAbsoluteSize.Width() + 1, aAbsolutePos.Y() + aAbsoluteSize.Height())); +} + +//------------------------------------------------------------------------------ +void DbGridControl::NavigationBar::StateChanged( StateChangedType nType ) +{ + Control::StateChanged( nType ); + + Window* pWindows[] = { &m_aRecordText, + &m_aAbsolute, + &m_aRecordOf, + &m_aRecordCount, + &m_aFirstBtn, + &m_aPrevBtn, + &m_aNextBtn, + &m_aLastBtn, + &m_aNewBtn + }; + + switch ( nType ) + { + case STATE_CHANGE_MIRRORING: + { + BOOL bIsRTLEnabled = IsRTLEnabled(); + for ( size_t i=0; i < sizeof( pWindows ) / sizeof( pWindows[0] ); ++i ) + pWindows[i]->EnableRTL( bIsRTLEnabled ); + } + break; + + case STATE_CHANGE_ZOOM: + { + Fraction aZoom = GetZoom(); + + // not all of these controls need to know the new zoom, but to be sure ... + Font aFont( GetSettings().GetStyleSettings().GetFieldFont() ); + if ( IsControlFont() ) + aFont.Merge( GetControlFont() ); + + for (size_t i=0; i < sizeof(pWindows)/sizeof(pWindows[0]); ++i) + { + pWindows[i]->SetZoom(aZoom); + pWindows[i]->SetZoomedPointFont(aFont); + } + + SetZoomedPointFont( aFont ); + + // rearrange the controls + m_nDefaultWidth = ArrangeControls(); + } + break; + } +} + +//------------------------------------------------------------------------------ +DbGridRow::DbGridRow(CursorWrapper* pCur, sal_Bool bPaintCursor) + :m_bIsNew(sal_False) +{ + + if (pCur && pCur->Is()) + { + Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY); + DataColumn* pColumn; + for (sal_Int32 i = 0; i < xColumns->getCount(); ++i) + { + Reference< XPropertySet > xColSet; + ::cppu::extractInterface(xColSet, xColumns->getByIndex(i)); + pColumn = new DataColumn(xColSet); + m_aVariants.Insert(pColumn, LIST_APPEND); + } + + if (pCur->rowDeleted()) + m_eStatus = GRS_DELETED; + else + { + if (bPaintCursor) + m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GRS_INVALID : GRS_CLEAN; + else + { + 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 = GRS_INVALID; + else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED))) + m_eStatus = GRS_MODIFIED; + else + m_eStatus = GRS_CLEAN; + } + else + m_eStatus = GRS_INVALID; + } + } + if (!m_bIsNew && IsValid()) + m_aBookmark = pCur->getBookmark(); + else + m_aBookmark = Any(); + } + else + m_eStatus = GRS_INVALID; +} + +//------------------------------------------------------------------------------ +DbGridRow::~DbGridRow() +{ + sal_uInt32 nCount = m_aVariants.Count(); + for (sal_uInt32 i = 0; i < nCount; i++) + delete m_aVariants.GetObject(i); +} + +//------------------------------------------------------------------------------ +void DbGridRow::SetState(CursorWrapper* pCur, sal_Bool bPaintCursor) +{ + if (pCur && pCur->Is()) + { + if (pCur->rowDeleted()) + { + m_eStatus = GRS_DELETED; + m_bIsNew = sal_False; + } + else + { + m_eStatus = GRS_CLEAN; + if (!bPaintCursor) + { + Reference< XPropertySet > xSet = pCur->getPropertySet(); + DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !"); + + if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED))) + m_eStatus = GRS_MODIFIED; + m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)); + } + else + m_bIsNew = sal_False; + } + + try + { + if (!m_bIsNew && IsValid()) + m_aBookmark = pCur->getBookmark(); + else + m_aBookmark = Any(); + } + catch(SQLException&) + { + DBG_UNHANDLED_EXCEPTION(); + m_aBookmark = Any(); + m_eStatus = GRS_INVALID; + m_bIsNew = sal_False; + } + } + else + { + m_aBookmark = Any(); + m_eStatus = GRS_INVALID; + m_bIsNew = sal_False; + } +} + +DBG_NAME(DbGridControl); +//------------------------------------------------------------------------------ +DbGridControl::DbGridControl( + Reference< XMultiServiceFactory > _rxFactory, + Window* pParent, + WinBits nBits) + :DbGridControl_Base(pParent, EBBF_NONE, nBits, DEFAULT_BROWSE_MODE ) + ,m_xServiceFactory(_rxFactory) + ,m_aBar(this) + ,m_nAsynAdjustEvent(0) + ,m_pDataSourcePropMultiplexer(NULL) + ,m_pDataSourcePropListener(NULL) + ,m_pFieldListeners(NULL) + ,m_pCursorDisposeListener(NULL) + ,m_pGridListener(NULL) + ,m_pDataCursor(NULL) + ,m_pSeekCursor(NULL) + ,m_nSeekPos(-1) + ,m_nTotalCount(-1) + ,m_aNullDate(OTypeConversionClient().getStandardDate()) + ,m_nMode(DEFAULT_BROWSE_MODE) + ,m_nCurrentPos(-1) + ,m_nDeleteEvent(0) + ,m_nOptions(OPT_READONLY) + ,m_nOptionMask(OPT_INSERT | OPT_UPDATE | OPT_DELETE) + ,m_nLastColId((USHORT)-1) + ,m_nLastRowId(-1) + ,m_bDesignMode(sal_False) + ,m_bRecordCountFinal(sal_False) + ,m_bMultiSelection(sal_True) + ,m_bNavigationBar(sal_True) + ,m_bSynchDisplay(sal_True) + ,m_bForceROController(sal_False) + ,m_bHandle(sal_True) + ,m_bFilterMode(sal_False) + ,m_bWantDestruction(sal_False) + ,m_bInAdjustDataSource(sal_False) + ,m_bPendingAdjustRows(sal_False) + ,m_bHideScrollbars( sal_False ) + ,m_bUpdating(sal_False) +{ + DBG_CTOR(DbGridControl,NULL); + + String sName(SVX_RES(RID_STR_NAVIGATIONBAR)); + m_aBar.SetAccessibleName(sName); + m_aBar.Show(); + ImplInitWindow( InitAll ); +} + +//------------------------------------------------------------------------------ +void DbGridControl::InsertHandleColumn() +{ + // Handle Column einfuegen + // Da die BrowseBox ohne handleColums Paintprobleme hat + // wird diese versteckt + if (HasHandle()) + BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(String())); + else + BrowseBox::InsertHandleColumn(0); +} + +//------------------------------------------------------------------------------ +void DbGridControl::Init() +{ + BrowserHeader* pNewHeader = CreateHeaderBar(this); + pHeader->SetMouseTransparent(sal_False); + + SetHeaderBar(pNewHeader); + SetMode(m_nMode); + SetCursorColor(Color(0xFF, 0, 0)); + + InsertHandleColumn(); +} + +//------------------------------------------------------------------------------ +DbGridControl::~DbGridControl() +{ + RemoveColumns(); + + { + m_bWantDestruction = sal_True; + osl::MutexGuard aGuard(m_aDestructionSafety); + if (m_pFieldListeners) + DisconnectFromFields(); + if (m_pCursorDisposeListener) + { + delete m_pCursorDisposeListener; + m_pCursorDisposeListener = NULL; + } + } + + if (m_nDeleteEvent) + Application::RemoveUserEvent(m_nDeleteEvent); + + if (m_pDataSourcePropMultiplexer) + { + m_pDataSourcePropMultiplexer->dispose(); + m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer + delete m_pDataSourcePropListener; + m_pDataSourcePropMultiplexer = NULL; + m_pDataSourcePropListener = NULL; + } + m_xRowSetListener.clear(); + + delete m_pDataCursor; + delete m_pSeekCursor; + + DBG_DTOR(DbGridControl,NULL); +} + +//------------------------------------------------------------------------------ +void DbGridControl::StateChanged( StateChangedType nType ) +{ + DbGridControl_Base::StateChanged( nType ); + + switch (nType) + { + case STATE_CHANGE_MIRRORING: + ImplInitWindow( InitWritingMode ); + Invalidate(); + break; + + case STATE_CHANGE_ZOOM: + { + ImplInitWindow( InitFont ); + + // and give it a chance to rearrange + Point aPoint = GetControlArea().TopLeft(); + sal_uInt16 nX = (sal_uInt16)aPoint.X(); + ArrangeControls(nX, (sal_uInt16)aPoint.Y()); + ReserveControlArea((sal_uInt16)nX); + } + break; + case STATE_CHANGE_CONTROLFONT: + ImplInitWindow( InitFont ); + Invalidate(); + break; + case STATE_CHANGE_CONTROLFOREGROUND: + ImplInitWindow( InitForeground ); + Invalidate(); + break; + case STATE_CHANGE_CONTROLBACKGROUND: + ImplInitWindow( InitBackground ); + Invalidate(); + break; + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt ) +{ + DbGridControl_Base::DataChanged( rDCEvt ); + if ( (rDCEvt.GetType() == DATACHANGED_SETTINGS ) && + (rDCEvt.GetFlags() & SETTINGS_STYLE) ) + { + ImplInitWindow( InitAll ); + Invalidate(); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::Select() +{ + DbGridControl_Base::Select(); + + // as the selected rows may have changed, udate the according display in our navigation bar + m_aBar.InvalidateState(NavigationBar::RECORD_COUNT); + + if (m_pGridListener) + m_pGridListener->selectionChanged(); +} + +//------------------------------------------------------------------------------ +void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat ) +{ + for ( sal_uInt32 i = 0; i < m_aColumns.Count(); ++i ) + { + DbGridColumn* pCol = m_aColumns.GetObject(i); + if (pCol) + pCol->ImplInitWindow( GetDataWindow(), _eInitWhat ); + } + + if ( ( _eInitWhat & InitWritingMode ) != 0 ) + { + if ( m_bNavigationBar ) + { + m_aBar.EnableRTL( IsRTLEnabled() ); + } + } + + if ( ( _eInitWhat & InitFont ) != 0 ) + { + if ( m_bNavigationBar ) + { + Font aFont = m_aBar.GetSettings().GetStyleSettings().GetFieldFont(); + if ( IsControlFont() ) + m_aBar.SetControlFont( GetControlFont() ); + else + m_aBar.SetControlFont(); + + m_aBar.SetZoom( GetZoom() ); + } + } + + if ( ( _eInitWhat & InitBackground ) != 0 ) + { + if (IsControlBackground()) + { + GetDataWindow().SetBackground(GetControlBackground()); + GetDataWindow().SetControlBackground(GetControlBackground()); + GetDataWindow().SetFillColor(GetControlBackground()); + } + else + { + GetDataWindow().SetControlBackground(); + GetDataWindow().SetFillColor(GetFillColor()); + } + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::RemoveRows(sal_Bool bNewCursor) +{ + // Hat sich der DatenCursor verandert ? + if (!bNewCursor) + { + DELETEZ(m_pSeekCursor); + m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL; + m_nCurrentPos = m_nSeekPos = -1; + m_nOptions = OPT_READONLY; + + RowRemoved(0, GetRowCount(), sal_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(); + + // alle Columns deinitialisieren + // existieren Spalten, dann alle Controller freigeben + for (sal_uInt32 i = 0; i < m_aColumns.Count(); i++) + m_aColumns.GetObject(i)->Clear(); + + DELETEZ(m_pSeekCursor); + DELETEZ(m_pDataCursor); + + m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = NULL; + m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1; + m_nOptions = OPT_READONLY; + + // Anzahl Saetze im Browser auf 0 zuruecksetzen + DbGridControl_Base::RemoveRows(); + m_aBar.InvalidateAll(m_nCurrentPos, sal_True); +} + +//------------------------------------------------------------------------------ +void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY) +{ + // Positionierung der Controls + if (m_bNavigationBar) + { + nX = m_aBar.GetDefaultWidth(); + Rectangle aRect(GetControlArea()); + m_aBar.SetPosSizePixel(Point(0,nY + 1), Size(nX, aRect.GetSize().Height() - 1)); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::EnableHandle(sal_Bool bEnable) +{ + if (m_bHandle == bEnable) + return; + + // HandleColumn wird nur ausgeblendet, + // da es sonst etliche Probleme mit dem Zeichnen gibt + RemoveColumn(0); + m_bHandle = bEnable; + InsertHandleColumn(); +} + +//------------------------------------------------------------------------------ +namespace +{ + bool adjustModeForScrollbars( BrowserMode& _rMode, sal_Bool _bNavigationBar, sal_Bool _bHideScrollbars ) + { + BrowserMode nOldMode = _rMode; + + if ( !_bNavigationBar ) + { + _rMode &= ~BROWSER_AUTO_HSCROLL; + } + + if ( _bHideScrollbars ) + { + _rMode |= ( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL ); + _rMode &= ~( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL ); + } + else + { + _rMode |= ( BROWSER_AUTO_HSCROLL | BROWSER_AUTO_VSCROLL ); + _rMode &= ~( BROWSER_NO_HSCROLL | BROWSER_NO_VSCROLL ); + } + + // note: if we have a navigation bar, we always have a AUTO_HSCROLL. In particular, + // _bHideScrollbars is ignored then + if ( _bNavigationBar ) + { + _rMode |= BROWSER_AUTO_HSCROLL; + _rMode &= ~BROWSER_NO_HSCROLL; + } + + return nOldMode != _rMode; + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::EnableNavigationBar(sal_Bool bEnable) +{ + if (m_bNavigationBar == bEnable) + return; + + m_bNavigationBar = bEnable; + + if (bEnable) + { + m_aBar.Show(); + m_aBar.Enable(); + m_aBar.InvalidateAll(m_nCurrentPos, sal_True); + + if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) + SetMode( m_nMode ); + + // liefert die Groe�e der Reserved ControlArea + Point aPoint = GetControlArea().TopLeft(); + sal_uInt16 nX = (sal_uInt16)aPoint.X(); + + ArrangeControls(nX, (sal_uInt16)aPoint.Y()); + ReserveControlArea((sal_uInt16)nX); + } + else + { + m_aBar.Hide(); + m_aBar.Disable(); + + if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) + SetMode( m_nMode ); + + ReserveControlArea(); + } +} + +//------------------------------------------------------------------------------ +sal_uInt16 DbGridControl::SetOptions(sal_uInt16 nOpt) +{ + DBG_ASSERT(!m_xCurrentRow || !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()) + { + // feststellen welche Updatem�glichkeiten bestehen + sal_Int32 nPrivileges = 0; + xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges; + if ((nPrivileges & Privilege::INSERT) == 0) + nOpt &= ~OPT_INSERT; + if ((nPrivileges & Privilege::UPDATE) == 0) + nOpt &= ~OPT_UPDATE; + if ((nPrivileges & Privilege::DELETE) == 0) + nOpt &= ~OPT_DELETE; + } + else + nOpt = OPT_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 & BROWSER_CURSOR_WO_FOCUS) == 0) + { + if (nOpt & OPT_UPDATE) + nNewMode |= BROWSER_HIDECURSOR; + else + nNewMode &= ~BROWSER_HIDECURSOR; + } + else + nNewMode &= ~BROWSER_HIDECURSOR; + // should not be neccessary 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(); + + sal_Bool bInsertChanged = (nOpt & OPT_INSERT) != (m_nOptions & OPT_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 & OPT_INSERT) + { // the insert option is to be set + m_xEmptyRow = new DbGridRow(); + RowInserted(GetRowCount(), 1, sal_True); + } + else + { // the insert option is to be reset + m_xEmptyRow = NULL; + if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0)) + GoToRowColumnId(GetCurRow() - 1, GetCurColumnId()); + RowRemoved(GetRowCount(), 1, sal_True); + } + } + + // the 'delete' options has no immediate consequences + + ActivateCell(); + Invalidate(); + return m_nOptions; +} + +//------------------------------------------------------------------------------ +void DbGridControl::ForceHideScrollbars( sal_Bool _bForce ) +{ + if ( m_bHideScrollbars == _bForce ) + return; + + m_bHideScrollbars = _bForce; + + if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) ) + SetMode( m_nMode ); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::IsForceHideScrollbars() const +{ + return m_bHideScrollbars; +} + +//------------------------------------------------------------------------------ +void DbGridControl::EnablePermanentCursor(sal_Bool bEnable) +{ + if (IsPermanentCursorEnabled() == bEnable) + return; + + if (bEnable) + { + m_nMode &= ~BROWSER_HIDECURSOR; // without this BROWSER_CURSOR_WO_FOCUS won't have any affect + m_nMode |= BROWSER_CURSOR_WO_FOCUS; + } + else + { + if (m_nOptions & OPT_UPDATE) + m_nMode |= BROWSER_HIDECURSOR; // no cursor at all + else + m_nMode &= ~BROWSER_HIDECURSOR; // at least the "non-permanent" cursor + + m_nMode &= ~BROWSER_CURSOR_WO_FOCUS; + } + SetMode(m_nMode); + + sal_Bool bWasEditing = IsEditing(); + DeactivateCell(); + if (bWasEditing) + ActivateCell(); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::IsPermanentCursorEnabled() const +{ + return ((m_nMode & BROWSER_CURSOR_WO_FOCUS) != 0) && ((m_nMode & BROWSER_HIDECURSOR) == 0); +} + +//------------------------------------------------------------------------------ +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::SetMultiSelection(sal_Bool bMulti) +{ + m_bMultiSelection = bMulti; + if (m_bMultiSelection) + m_nMode |= BROWSER_MULTISELECTION; + else + m_nMode &= ~BROWSER_MULTISELECTION; + + SetMode(m_nMode); +} + +//------------------------------------------------------------------------------ +void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, sal_uInt16 nOpts) +{ + if (!_xCursor.is() && !m_pDataCursor) + return; + + if (m_pDataSourcePropMultiplexer) + { + m_pDataSourcePropMultiplexer->dispose(); + m_pDataSourcePropMultiplexer->release(); // this should delete the multiplexer + delete m_pDataSourcePropListener; + m_pDataSourcePropMultiplexer = NULL; + m_pDataSourcePropListener = NULL; + } + 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 an leave + if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY)->getColumns()->hasElements()) + { + RemoveRows(); + return; + } + + // Hat sich der DatenCursor verandert ? + sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId()); + + SetUpdateMode(sal_False); + RemoveRows(); + DisconnectFromFields(); + + DELETEZ(m_pCursorDisposeListener); + + { + ::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 = 0; + } + } + + // get a new formatter and data cursor + m_xFormatter = NULL; + OStaticDataAccessTools aStaticTools; + Reference< ::com::sun::star::util::XNumberFormatsSupplier > xSupplier = aStaticTools.getNumberFormats(aStaticTools.getRowSetConnection(_xCursor), sal_True); + if (xSupplier.is() && m_xServiceFactory.is()) + { + m_xFormatter = Reference< ::com::sun::star::util::XNumberFormatter >( + m_xServiceFactory->createInstance(FM_NUMBER_FORMATTER), + UNO_QUERY); + if (m_xFormatter.is()) + { + m_xFormatter->attachNumberFormatsSupplier(xSupplier); + + // retrieve the datebase of the Numberformatter + try + { + xSupplier->getNumberFormatSettings()->getPropertyValue(rtl::OUString::createFromAscii("NullDate")) >>= m_aNullDate; + } + catch(Exception&) + { + } + } + } + + m_pDataCursor = 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 = 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 a our special method "DataSourcePropertyChanged".) + if (m_pDataCursor) + { + m_pDataSourcePropListener = new FmXGridSourcePropListener(this); + m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() ); + m_pDataSourcePropMultiplexer->acquire(); + 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()) + { + // feststellen welche Updatemoeglichkeiten bestehen + 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 & OPT_INSERT) && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & OPT_INSERT)) + m_nOptions |= OPT_INSERT; + if ((m_nOptionMask & OPT_UPDATE) && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & OPT_UPDATE)) + m_nOptions |= OPT_UPDATE; + if ((m_nOptionMask & OPT_DELETE) && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & OPT_DELETE)) + m_nOptions |= OPT_DELETE; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + + sal_Bool bPermanentCursor = IsPermanentCursorEnabled(); + m_nMode = DEFAULT_BROWSE_MODE; + + if ( bPermanentCursor ) + { + m_nMode |= BROWSER_CURSOR_WO_FOCUS; + m_nMode &= ~BROWSER_HIDECURSOR; + } + else + { + // Duerfen Updates gemacht werden, kein Focus-RechtEck + if ( m_nOptions & OPT_UPDATE ) + m_nMode |= BROWSER_HIDECURSOR; + } + + if ( m_bMultiSelection ) + m_nMode |= BROWSER_MULTISELECTION; + else + m_nMode &= ~BROWSER_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 & OPT_INSERT) + { + // insert the empty row for insertion + m_xEmptyRow = new DbGridRow(); + ++nRecordCount; + } + if (nRecordCount) + { + m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor, sal_True); + m_xDataRow = new DbGridRow(m_pDataCursor, sal_False); + RowInserted(0, nRecordCount, sal_False); + + if (m_xSeekRow->IsValid()) + try + { + m_nSeekPos = m_pSeekCursor->getRow() - 1; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + m_nSeekPos = -1; + } + } + else + { + // no rows so we don't need a seekcursor + DELETEZ(m_pSeekCursor); + } + } + + // Zur alten Spalte gehen + if (!nCurPos || nCurPos >= ColCount()) + nCurPos = 1; + + // there are rows so go to the selected current column + if (nRecordCount) + GoToRowColumnId(0, GetColumnId(nCurPos)); + // else stop the editing if neccessary + else if (IsEditing()) + DeactivateCell(); + + // now reset the mode + if (m_nMode != nOldMode) + SetMode(m_nMode); + + // beim Resizen wird RecalcRows gerufen + if (!IsResizing() && GetRowCount()) + RecalcRows(GetTopRow(), GetVisibleRows(), sal_True); + + m_aBar.InvalidateAll(m_nCurrentPos, sal_True); + SetUpdateMode(sal_True); + + // start listening on the seek cursor + if (m_pSeekCursor) + m_pCursorDisposeListener = new DisposeListenerGridBridge(*this, Reference< XComponent > ((Reference< XInterface >)*m_pSeekCursor, UNO_QUERY), 0); +} + +//------------------------------------------------------------------------------ +void DbGridControl::RemoveColumns() +{ + if ( IsEditing() ) + DeactivateCell(); + + for (sal_uInt32 i = 0; i < m_aColumns.Count(); i++) + delete m_aColumns.GetObject(i); + m_aColumns.Clear(); + + DbGridControl_Base::RemoveColumns(); +} + +//------------------------------------------------------------------------------ +DbGridColumn* DbGridControl::CreateColumn(sal_uInt16 nId) const +{ + return new DbGridColumn(nId, *(DbGridControl*)this); +} + +//------------------------------------------------------------------------------ +sal_uInt16 DbGridControl::AppendColumn(const XubString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId) +{ + DBG_ASSERT(nId == (sal_uInt16)-1, "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.GetObject(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) && (nId<=m_aColumns.Count()); ++nId) + ; + DBG_ASSERT(GetViewColumnPos(nId) == (sal_uInt16)-1, "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 ..." + + DbGridControl_Base::AppendColumn(rName, nWidth, nRealPos, nId); + if (nModelPos == HEADERBAR_APPEND) + m_aColumns.Insert(CreateColumn(nId), LIST_APPEND); + else + m_aColumns.Insert(CreateColumn(nId), nModelPos); + + return nId; +} + +//------------------------------------------------------------------------------ +void DbGridControl::RemoveColumn(sal_uInt16 nId) +{ + sal_Int16 nIndex = GetModelColumnPos(nId); + DbGridControl_Base::RemoveColumn(nId); + delete m_aColumns.Remove(nIndex); +} + +//------------------------------------------------------------------------------ +void DbGridControl::ColumnMoved(sal_uInt16 nId) +{ + DbGridControl_Base::ColumnMoved(nId); + + // remove the col from the model + sal_Int16 nOldModelPos = GetModelColumnPos(nId); +#ifdef DBG_UTIL + DbGridColumn* pCol = m_aColumns.GetObject((sal_uInt32)nOldModelPos); + 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 + sal_uInt16 nNewModelPos; + for (nNewModelPos = 0; nNewModelPos < m_aColumns.Count(); ++nNewModelPos) + { + if (!m_aColumns.GetObject(nNewModelPos)->IsHidden()) + { + if (!nNewViewPos) + break; + else + --nNewViewPos; + } + } + DBG_ASSERT(nNewModelPos<m_aColumns.Count(), "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 occured 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 ;) + + m_aColumns.Insert(m_aColumns.Remove((sal_uInt32)nOldModelPos), nNewModelPos); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::SeekRow(long nRow) +{ + // in filter mode or in insert only mode we don't have any cursor! + if ( !SeekCursor( nRow ) ) + return sal_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, sal_True ); + m_xPaintRow = m_xSeekRow; + } + } + + DbGridControl_Base::SeekRow(nRow); + + return m_nSeekPos >= 0; +} +//------------------------------------------------------------------------------ +// Wird aufgerufen, wenn die dargestellte Datenmenge sich aendert +//------------------------------------------------------------------------------ +void DbGridControl::VisibleRowsChanged( long nNewTopRow, sal_uInt16 nLinesOnScreen ) +{ + RecalcRows(nNewTopRow, nLinesOnScreen , sal_False); +} + +//------------------------------------------------------------------------------ +void DbGridControl::RecalcRows(long nNewTopRow, sal_uInt16 nLinesOnScreen, sal_Bool bUpdateCursor) +{ + DBG_CHKTHIS( DbGridControl, NULL ); + // Wenn kein Cursor -> keine Rows im Browser. + if (!m_pSeekCursor) + { + DBG_ASSERT(GetRowCount() == 0,"DbGridControl: ohne Cursor darf es keine Rows geben"); + return; + } + + // ignore any updates implicit made + sal_Bool bDisablePaint = !bUpdateCursor && IsPaintEnabled(); + if (bDisablePaint) + EnablePaint(sal_False); + + // Cache an den sichtbaren Bereich anpassen + Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet(); + sal_Int32 nCacheSize = 0; + xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize; + sal_Bool bCacheAligned = sal_False; + // Nach der Initialisierung (m_nSeekPos < 0) keine Cursorbewegung, da bereits auf den ersten + // Satz positioniert + long nDelta = nNewTopRow - GetTopRow(); + // Limit fuer relative Positionierung + long nLimit = (nCacheSize) ? nCacheSize / 2 : 0; + + // mehr Zeilen auf dem Bildschirm als im Cache + if (nLimit < nLinesOnScreen) + { + Any aCacheSize; + aCacheSize <<= sal_Int32(nLinesOnScreen*2); + xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize); + // jetzt auf alle Faelle den Cursor anpassen + bUpdateCursor = sal_True; + bCacheAligned = sal_True; + nLimit = nLinesOnScreen; + } + + // Im folgenden werden die Positionierungen so vorgenommen, da� sichergestellt ist + // da� ausreichend Zeilen im DatenCache vorhanden sind + + // Fenster geht nach unten, weniger als zwei Fenster Differenz + // oder Cache angepasst und noch kein Rowcount + if (nDelta < nLimit && (nDelta > 0 + || (bCacheAligned && m_nTotalCount < 0)) ) + SeekCursor(nNewTopRow + nLinesOnScreen - 1, sal_False); + else if (nDelta < 0 && Abs(nDelta) < nLimit) + SeekCursor(nNewTopRow, sal_False); + else if (nDelta != 0 || bUpdateCursor) + SeekCursor(nNewTopRow, sal_True); + + AdjustRows(); + + // ignore any updates implicit made + EnablePaint(sal_True); +} + +//------------------------------------------------------------------------------ +void DbGridControl::RowInserted(long nRow, long nNumRows, sal_Bool bDoPaint, sal_Bool bKeepSelection) +{ + if (nNumRows) + { + 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; + + DbGridControl_Base::RowInserted(nRow, nNumRows, bDoPaint, bKeepSelection); + m_aBar.InvalidateState(NavigationBar::RECORD_COUNT); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::RowRemoved(long nRow, long nNumRows, sal_Bool bDoPaint) +{ + if (nNumRows) + { + 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; + + DbGridControl_Base::RowRemoved(nRow, nNumRows, bDoPaint); + m_aBar.InvalidateState(NavigationBar::RECORD_COUNT); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::AdjustRows() +{ + if (!m_pSeekCursor) + return; + + Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet(); + + // Aktualisieren des RecordCounts + sal_Int32 nRecordCount = 0; + xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; + if (!m_bRecordCountFinal) + m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL)); + + // hat sich die aktuelle Anzahl Rows veraendert + // hierbei muss auch beruecksichtigt werden, + // das eine zusaetzliche Zeile zum einfuegen von Datensaetzen vorhanden sein kann + + // zusaetzliche AppendRow fuers einfuegen + if (m_nOptions & OPT_INSERT) + ++nRecordCount; + + // wird gerade eingefuegt, dann gehoert die gerade hinzuzufuegende + // Zeile nicht zum RecordCount und die Appendrow ebenfalls nicht + if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow && + m_xCurrentRow->IsNew()) + ++nRecordCount; + // das ist mit !m_bUpdating abgesichert : innerhalb von SaveRow (m_bUpdating == sal_True) wuerde sonst der Datensatz, den ich editiere + // (und den SaveRow gerade angefuegt hat, wodurch diese Methode hier getriggert wurde), doppelt zaehlen : einmal ist er schon + // in dem normalen RecordCount drin, zum zweiten wuerde er hier gezaehlt werden (60787 - FS) + + if (nRecordCount != GetRowCount()) + { + long nDelta = GetRowCount() - (long)nRecordCount; + if (nDelta > 0) // zuviele + { + RowRemoved(GetRowCount() - nDelta, nDelta, sal_False); + // es sind Zeilen weggefallen, dann ab der aktuellen Position neu zeichen + Invalidate(); + } + else // zuwenig + RowInserted(GetRowCount(), -nDelta, sal_True); + } + + if (m_bRecordCountFinal && m_nTotalCount < 0) + { + if (m_nOptions & OPT_INSERT) + m_nTotalCount = GetRowCount() - 1; + else + m_nTotalCount = GetRowCount(); + } + m_aBar.InvalidateState(NavigationBar::RECORD_COUNT); +} + +//------------------------------------------------------------------------------ +DbGridControl_Base::RowStatus DbGridControl::GetRowStatus(long nRow) const +{ + if (IsFilterRow(nRow)) + return DbGridControl_Base::FILTER; + else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos) + { + // neue Zeile + if (!IsValid(m_xCurrentRow)) + return DbGridControl_Base::DELETED; + else if (IsModified()) + return DbGridControl_Base::MODIFIED; + else if (m_xCurrentRow->IsNew()) + return DbGridControl_Base::CURRENTNEW; + else + return DbGridControl_Base::CURRENT; + } + else if (IsInsertionRow(nRow)) + return DbGridControl_Base::NEW; + else if (!IsValid(m_xSeekRow)) + return DbGridControl_Base::DELETED; + else + return DbGridControl_Base::CLEAN; +} + +//------------------------------------------------------------------------------ +void DbGridControl::PaintStatusCell(OutputDevice& rDev, const Rectangle& rRect) const +{ + DbGridControl_Base::PaintStatusCell(rDev, rRect); +} + +//------------------------------------------------------------------------------ +void DbGridControl::PaintCell(OutputDevice& rDev, const Rectangle& rRect, sal_uInt16 nColumnId) const +{ + if (!IsValid(m_xPaintRow)) + return; + + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId)); + if (pColumn) + { + Rectangle aArea(rRect); + if ((GetMode() & BROWSER_CURSOR_WO_FOCUS) == BROWSER_CURSOR_WO_FOCUS) + { + aArea.Top() += 1; + aArea.Bottom() -= 1; + } + pColumn->Paint(rDev, aArea, m_xPaintRow, getNumberFormatter()); + } +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::CursorMoving(long nNewRow, sal_uInt16 nNewCol) +{ + DBG_CHKTHIS( DbGridControl, NULL ); + + DeactivateCell( sal_False ); + + if ( m_pDataCursor + && ( m_nCurrentPos != nNewRow ) + && !SetCurrent( nNewRow ) + ) + { + ActivateCell(); + return sal_False; + } + + if ( !DbGridControl_Base::CursorMoving( nNewRow, nNewCol ) ) + return sal_False; + + return sal_True; +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::SetCurrent(long nNewRow) +{ + // Each movement of the datacursor must start with BeginCursorAction and end with + // EndCursorAction to block all notifications during the movement + BeginCursorAction(); + + try + { + // Abgleichen der Positionen + if (SeekCursor(nNewRow)) + { + if (IsFilterRow(nNewRow)) // special mode for filtering + { + m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow; + m_nCurrentPos = nNewRow; + } + else + { + sal_Bool bNewRowInserted = sal_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 itselt 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 = sal_True; + } + else + { + + if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() ) + { + Any aBookmark = m_pSeekCursor->getBookmark(); + if (!m_xCurrentRow || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark())) + { + // adjust the cursor to the new desired row + if (!m_pDataCursor->moveToBookmark(aBookmark)) + { + EndCursorAction(); + return sal_False; + } + } + } + } + m_xDataRow->SetState(m_pDataCursor, sal_False); + m_xCurrentRow = m_xDataRow; + + 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 + { + DBG_ERROR("DbGridControl::SetCurrent : SeekRow failed !"); + EndCursorAction(); + return sal_False; + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + EndCursorAction(); + return sal_False; + } + + EndCursorAction(); + return sal_True; +} + +//------------------------------------------------------------------------------ +void DbGridControl::CursorMoved() +{ + DBG_CHKTHIS( DbGridControl, NULL ); + + // CursorBewegung durch loeschen oder einfuegen von Zeilen + if (m_pDataCursor && m_nCurrentPos != GetCurRow()) + { + DeactivateCell(sal_True); + SetCurrent(GetCurRow()); + } + + DbGridControl_Base::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(sal_Bool bSync) +{ + if (bSync != m_bSynchDisplay) + { + m_bSynchDisplay = bSync; + if (m_bSynchDisplay) + AdjustDataSource(sal_False); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::forceSyncDisplay() +{ + sal_Bool bOld = getDisplaySynchron(); + setDisplaySynchron(sal_True); + if (!bOld) + setDisplaySynchron(bOld); +} + +//------------------------------------------------------------------------------ +void DbGridControl::forceROController(sal_Bool bForce) +{ + if (m_bForceROController == bForce) + return; + + m_bForceROController = bForce; + // alle Columns durchgehen und denen Bescheid geben + for (sal_uInt16 i=0; i<m_aColumns.Count(); ++i) + { + DbGridColumn* pColumn = m_aColumns.GetObject(i); + if (!pColumn) + continue; + + CellController* pReturn = &pColumn->GetController(); + if (!pReturn) + continue; + + // nur wenn es eine Edit-Zeile ist, kann ich ihr das forced read-only mitgeben + if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController)) + continue; + + Edit& rEdit = (Edit&)pReturn->GetWindow(); + rEdit.SetReadOnly(m_bForceROController); + if (m_bForceROController) + rEdit.SetStyle(rEdit.GetStyle() | WB_NOHIDESELECTION); + else + rEdit.SetStyle(rEdit.GetStyle() & ~WB_NOHIDESELECTION); + } + + // die aktive Zelle erneut aktivieren, da sich ihr Controller geaendert haben kann + if (IsEditing()) + DeactivateCell(); + ActivateCell(); +} + + +//------------------------------------------------------------------------------ +void DbGridControl::AdjustDataSource(sal_Bool bFull) +{ + TRACE_RANGE("DbGridControl::AdjustDataSource"); + ::vos::OGuard aGuard(Application::GetSolarMutex()); + // wird die aktuelle Zeile gerade neu bestimmt, + // wird kein abgleich vorgenommen + + if (bFull) + m_xCurrentRow = NULL; + // if we are on the same row only repaint + // but this is only possible for rows which are not inserted, in that case the comparision result + // may not be correct + else + if ( m_xCurrentRow.Is() + && !m_xCurrentRow->IsNew() + && !m_pDataCursor->isBeforeFirst() + && !m_pDataCursor->isAfterLast() + && !m_pDataCursor->rowDeleted() + ) + { + sal_Bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() ); + + sal_Bool bDataCursorIsOnNew = sal_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, "Fehler in den Datenzeilen"); + TRACE_RANGE_MESSAGE1("same position, new state : %s", ROWSTATUS(m_xCurrentRow)); + RowModified(m_nCurrentPos); + return; + } + } + + // weg von der Row des DatenCursors + if (m_xPaintRow == m_xCurrentRow) + m_xPaintRow = m_xSeekRow; + + // keine aktuelle Zeile dann komplett anpassen + if (!m_xCurrentRow) + AdjustRows(); + + sal_Int32 nNewPos = AlignSeekCursor(); + if (nNewPos < 0) // keine Position gefunden + return; + + m_bInAdjustDataSource = TRUE; + if (nNewPos != m_nCurrentPos) + { + if (m_bSynchDisplay) + DbGridControl_Base::GoToRow(nNewPos); + + if (!m_xCurrentRow.Is()) + // das tritt zum Beispiel auf, wenn man die n (n>1) letzten Datensaetze geloescht hat, waehrend der Cursor auf dem letzten + // steht : AdjustRows entfernt dann zwei Zeilen aus der BrowseBox, wodurch diese ihre CurrentRow um zwei nach unten + // korrigiert, so dass dann das GoToRow in's Leere laeuft (da wir uns ja angeblich schon an der richtigen Position + // befinden) + SetCurrent(nNewPos); + } + else + { + SetCurrent(nNewPos); + RowModified(nNewPos); + } + m_bInAdjustDataSource = FALSE; + + // Wird der DatenCursor von aussen bewegt, wird die selektion aufgehoben + SetNoSelection(); + m_aBar.InvalidateAll(m_nCurrentPos, m_xCurrentRow.Is()); +} + +//------------------------------------------------------------------------------ +sal_Int32 DbGridControl::AlignSeekCursor() +{ + DBG_CHKTHIS( DbGridControl, NULL ); + // Positioniert den SeekCursor auf den DatenCursor, Daten werden nicht uebertragen + + if (!m_pSeekCursor) + return -1; + + Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet(); + + // jetzt den seekcursor an den DatenCursor angleichen + 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 + DBG_WARNING( "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() ) + { + DBG_WARNING( "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())) + // dummerweise kann das moveToBookmark indirekt dazu fuehren, dass der Seek-Cursor wieder neu positoniert wird (wenn + // naemlich das mit all seinen zu feuernden Events relativ komplexe moveToBookmark irgendwo ein Update ausloest), + // also muss ich es nochmal versuchen + m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark()); + // Nicht dass das jetzt nicht auch schief gegangen sein koennte, aber es ist zumindest unwahrscheinlicher geworden. + // Und die Alternative waere eine Schleife so lange bis es stimmt, und das kann auch nicht die Loesung sein + m_nSeekPos = m_pSeekCursor->getRow() - 1; + } + } + catch(Exception&) + { + } + } + return m_nSeekPos; +} +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::SeekCursor(long nRow, sal_Bool bAbsolute) +{ + DBG_CHKTHIS( DbGridControl, NULL ); + // Positioniert den SeekCursor, Daten werden nicht uebertragen + + // additions for the filtermode + if (IsFilterRow(nRow)) + { + m_nSeekPos = 0; + return sal_True; + } + + if (!m_pSeekCursor) + return sal_False; + + // Befinden wir uns gerade beim Einfuegen + if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() && + nRow >= m_nCurrentPos) + { + // dann darf auf alle Faelle nicht weiter nach unten gescrollt werden + // da der letzte Datensatz bereits erreicht wurde! + if (nRow == m_nCurrentPos) + { + // auf die aktuelle Zeile bewegt, dann muß kein abgleich gemacht werden, wenn + // gerade ein Datensatz eingefuegt wird + m_nSeekPos = nRow; + } + else if (IsInsertionRow(nRow)) // Leerzeile zum Einfuegen von Datensaetzen + m_nSeekPos = nRow; + } + else if (IsInsertionRow(nRow)) // Leerzeile zum Einfuegen von Datensaetzen + m_nSeekPos = nRow; + else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & OPT_INSERT) ? 1 : 0)) && m_pSeekCursor->isAfterLast()) + m_nSeekPos = nRow; + else + { + + sal_Bool bSuccess=sal_False; + 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 = sal_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 = bAbsolute || (abs(nSteps) > 100); + } + + if ( bAbsolute ) + { + bSuccess = m_pSeekCursor->absolute(nRow + 1); + if (bSuccess) + m_nSeekPos = nRow; + } + else + { + if (nSteps > 0) // auf den letzten benoetigten Datensatz positionieren + { + if (m_pSeekCursor->isAfterLast()) + bSuccess = sal_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 = sal_False; + else if (m_pSeekCursor->isAfterLast()) + bSuccess = m_pSeekCursor->absolute(nSteps); + else + bSuccess = m_pSeekCursor->relative(nSteps); + } + else + { + m_nSeekPos = nRow; + return sal_True; + } + } + } + catch(Exception&) + { + DBG_ERROR("DbGridControl::SeekCursor : failed ..."); + } + + try + { + if (!bSuccess) + { + if (bAbsolute || nSteps > 0) + bSuccess = m_pSeekCursor->last(); + else + bSuccess = m_pSeekCursor->first(); + } + + if (bSuccess) + m_nSeekPos = m_pSeekCursor->getRow() - 1; + else + m_nSeekPos = -1; + } + catch(Exception&) + { + DBG_ERROR("DbGridControl::SeekCursor : failed ..."); + m_nSeekPos = -1; // kein Datensatz mehr vorhanden + } + } + 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) // RecordCount steht noch nicht fest + { + try + { + sal_Bool bRes = m_pSeekCursor->last(); + + if (bRes) + { + m_nSeekPos = m_pSeekCursor->getRow() - 1; + AdjustRows(); + } + } + catch(Exception&) + { + } + } + + // auf den letzen Datensatz positionieren, nicht auf die Leerzeile + if (m_nOptions & OPT_INSERT) + { + if ((GetRowCount() - 1) > 0) + MoveToPosition(GetRowCount() - 2); + } + else if (GetRowCount()) + MoveToPosition(GetRowCount() - 1); +} + +//------------------------------------------------------------------------------ +void DbGridControl::MoveToPrev() +{ + long nNewRow = std::max(GetCurRow() - 1L, 0L); + if (GetCurRow() != nNewRow) + MoveToPosition(nNewRow); +} + +//------------------------------------------------------------------------------ +void DbGridControl::MoveToNext() +{ + if (!m_pSeekCursor) + return; + + if (m_nTotalCount > 0) + { + // move the data cursor to the right position + long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1); + if (GetCurRow() != nNewRow) + MoveToPosition(nNewRow); + } + else + { + sal_Bool bOk = sal_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(); + } + + if(!bOk) + { + AdjustRows(); + if (m_nTotalCount > 0) // only to avoid infinte recursion + MoveToNext(); + } + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::MoveToPosition(sal_uInt32 nPos) +{ + if (!m_pSeekCursor) + return; + + if (m_nTotalCount < 0 && (long)nPos >= GetRowCount()) + { + try + { + if (!m_pSeekCursor->absolute(nPos + 1)) + { + AdjustRows(); + Sound::Beep(); + return; + } + else + { + m_nSeekPos = m_pSeekCursor->getRow() - 1; + AdjustRows(); + } + } + catch(Exception&) + { + return; + } + } + DbGridControl_Base::GoToRow(nPos); + m_aBar.InvalidateAll(m_nCurrentPos); +} + +//------------------------------------------------------------------------------ +void DbGridControl::AppendNew() +{ + if (!m_pSeekCursor || !(m_nOptions & OPT_INSERT)) + return; + + if (m_nTotalCount < 0) // RecordCount steht noch nicht fest + { + try + { + sal_Bool bRes = m_pSeekCursor->last(); + + if (bRes) + { + m_nSeekPos = m_pSeekCursor->getRow() - 1; + AdjustRows(); + } + } + catch(Exception&) + { + return; + } + } + + long nNewRow = m_nTotalCount + 1; + if (nNewRow > 0 && GetCurRow() != nNewRow) + MoveToPosition(nNewRow - 1); +} + +//------------------------------------------------------------------------------ +void DbGridControl::SetDesignMode(sal_Bool bMode) +{ + if (IsDesignMode() != bMode) + { + // Enable/Disable f�r den Designmode anpassen damit die Headerbar konfigurierbar bleibt + if (bMode) + { + if (!IsEnabled()) + { + Enable(); + GetDataWindow().Disable(); + } + } + else + { + // komplett disablen + if (!GetDataWindow().IsEnabled()) + Disable(); + } + + m_bDesignMode = bMode; + GetDataWindow().SetMouseTransparent(bMode); + SetMouseTransparent(bMode); + + m_aBar.InvalidateAll(m_nCurrentPos, sal_True); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::SetFilterMode(sal_Bool bMode) +{ + if (IsFilterMode() != bMode) + { + m_bFilterMode = bMode; + + if (bMode) + { + SetUpdateMode(sal_False); + + // es gibt kein Cursor mehr + if (IsEditing()) + DeactivateCell(); + RemoveRows(sal_False); + + m_xEmptyRow = new DbGridRow(); + + // setting the new filter controls + for (sal_uInt16 i = 0; i<m_aColumns.Count(); ++i) + { + DbGridColumn* pCurCol = m_aColumns.GetObject(i); + if (!pCurCol->IsHidden()) + pCurCol->UpdateControl(); + } + + // one row for filtering + RowInserted(0, 1, sal_True); + SetUpdateMode(sal_True); + } + else + setDataSource(Reference< XRowSet > ()); + } +} +// ----------------------------------------------------------------------------- +String DbGridControl::GetCellText(long _nRow, USHORT _nColId) const +{ + DbGridColumn* pColumn = m_aColumns.GetObject( GetModelColumnPos( _nColId ) ); + String sRet; + if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) ) + sRet = GetCurrentRowCellText(pColumn, m_xPaintRow); + return sRet; +} +//------------------------------------------------------------------------------ +XubString DbGridControl::GetCurrentRowCellText(DbGridColumn* pColumn,const DbGridRowRef& _rRow) const +{ + // Ausgabe des Textes fuer eine Zelle + XubString aText; + if ( pColumn && IsValid(m_xPaintRow) ) + aText = pColumn->GetCellText(_rRow, m_xFormatter); + return aText; +} + +//------------------------------------------------------------------------------ +sal_uInt32 DbGridControl::GetTotalCellWidth(long nRow, sal_uInt16 nColId) +{ + if (SeekRow(nRow)) + { + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColId)); + return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow)); + } + else + return 30; //xxxx +} + +//------------------------------------------------------------------------------ +void DbGridControl::PreExecuteRowContextMenu(sal_uInt16 /*nRow*/, PopupMenu& rMenu) +{ + sal_Bool bDelete = (m_nOptions & OPT_DELETE) && GetSelectRowCount() && !IsCurrentAppending(); + // ist nur die Leerzeile selektiert, dann nicht loeschen + bDelete = bDelete && !((m_nOptions & OPT_INSERT) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1)); + + rMenu.EnableItem(SID_FM_DELETEROWS, bDelete); + rMenu.EnableItem(SID_FM_RECORD_SAVE, IsModified()); + + // the undo is more difficult + sal_Bool bCanUndo = IsModified(); + long nState = -1; + if (m_aMasterStateProvider.IsSet()) + nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_UNDO); + bCanUndo &= ( 0 != nState ); + + rMenu.EnableItem(SID_FM_RECORD_UNDO, bCanUndo); +} + +//------------------------------------------------------------------------------ +void DbGridControl::PostExecuteRowContextMenu(sal_uInt16 /*nRow*/, const PopupMenu& /*rMenu*/, sal_uInt16 nExecutionResult) +{ + switch (nExecutionResult) + { + case SID_FM_DELETEROWS: + // delete asynchron + if (m_nDeleteEvent) + Application::RemoveUserEvent(m_nDeleteEvent); + m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete)); + break; + case SID_FM_RECORD_UNDO: + Undo(); + break; + case SID_FM_RECORD_SAVE: + SaveRow(); + break; + default: + break; + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt) throw( RuntimeException ) +{ + TRACE_RANGE("DbGridControl::DataSourcePropertyChanged"); + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + // prop "IsModified" changed ? + // during update don't care about the modified state + if (!IsUpdating() && evt.PropertyName.compareTo(FM_PROP_ISMODIFIED) == COMPARE_EQUAL) + { + Reference< XPropertySet > xSource(evt.Source, UNO_QUERY); + DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" ); + sal_Bool bIsNew = sal_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 a insert row + // -> we've to add a new grid row + if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew()) + { + RowInserted(GetRowCount(), 1, sal_True); + InvalidateStatusCell(m_nCurrentPos); + m_aBar.InvalidateAll(m_nCurrentPos); + } + } + else + { // modified state changed from sal_True to sal_False and we're on a 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 obsolet now. + if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2)) + { + RowRemoved(GetRowCount() - 1, 1, sal_True); + InvalidateStatusCell(m_nCurrentPos); + m_aBar.InvalidateAll(m_nCurrentPos); + } + } + } + if (m_xCurrentRow.Is()) + { + m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN); + m_xCurrentRow->SetNew( bIsNew ); + InvalidateStatusCell(m_nCurrentPos); + TRACE_RANGE_MESSAGE1("modified flag changed, new state : %s", ROWSTATUS(m_xCurrentRow)); + } + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel ) +{ + if (!m_pSeekCursor || IsResizing()) + return; + + sal_uInt16 nColId = GetColumnAtXPosPixel(rPosPixel.X()); + long nRow = GetRowAtYPosPixel(rPosPixel.Y()); + if (nColId != HANDLE_ID && nRow >= 0) + { + if (GetDataWindow().IsMouseCaptured()) + GetDataWindow().ReleaseMouse(); + + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColId)); + OStringTransferable* pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow)); + Reference< XTransferable > xEnsureDelete(pTransferable); + pTransferable->StartDrag(this, DND_ACTION_COPY); + } +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_Int16 _nColId) +{ + return (_nRow >= 0) + && (_nRow < GetRowCount()) + && (_nColId > HANDLE_ID) + && (_nColId <= ColCount()); +} + +//------------------------------------------------------------------------------ +void DbGridControl::copyCellText(sal_Int32 _nRow, sal_Int16 _nColId) +{ + DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!"); + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(_nColId)); + SeekRow(_nRow); + OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this ); +} + +//------------------------------------------------------------------------------ +void DbGridControl::executeRowContextMenu( long _nRow, const Point& _rPreferredPos ) +{ + PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_ROWS ) ); + + PreExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu ); + aContextMenu.RemoveDisabledEntries( sal_True, sal_True ); + PostExecuteRowContextMenu( (sal_uInt16)_nRow, aContextMenu, aContextMenu.Execute( this, _rPreferredPos ) ); + + // TODO: why this weird cast to sal_uInt16? What if we really have more than 65535 lines? + // -> change this to sal_uInt32 +} + +//------------------------------------------------------------------------------ +void DbGridControl::Command(const CommandEvent& rEvt) +{ + switch (rEvt.GetCommand()) + { + case COMMAND_CONTEXTMENU: + { + if ( !m_pSeekCursor ) + { + DbGridControl_Base::Command(rEvt); + return; + } + + if ( !rEvt.IsMouseEvent() ) + { // context menu requested by keyboard + if ( GetSelectRowCount() ) + { + long nRow = FirstSelectedRow( ); + + ::Rectangle aRowRect( GetRowRectPixel( nRow, sal_True ) ); + executeRowContextMenu( nRow, aRowRect.LeftCenter() ); + + // handled + return; + } + } + + sal_uInt16 nColId = GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X()); + long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y()); + + if (nColId == HANDLE_ID) + { + executeRowContextMenu( nRow, rEvt.GetMousePosPixel() ); + } + else if (canCopyCellText(nRow, nColId)) + { + PopupMenu aContextMenu(SVX_RES(RID_SVXMNU_CELL)); + aContextMenu.RemoveDisabledEntries(sal_True, sal_True); + switch (aContextMenu.Execute(this, rEvt.GetMousePosPixel())) + { + case SID_COPY: + copyCellText(nRow, nColId); + break; + } + } + else + { + DbGridControl_Base::Command(rEvt); + return; + } + } + default: + DbGridControl_Base::Command(rEvt); + } +} + +//------------------------------------------------------------------------------ +IMPL_LINK(DbGridControl, OnDelete, void*, /*EMPTYTAG*/ ) +{ + DBG_CHKTHIS(DbGridControl, NULL ); + m_nDeleteEvent = 0; + DeleteSelectedRows(); + return 0; +} + +//------------------------------------------------------------------------------ +void DbGridControl::DeleteSelectedRows() +{ + DBG_ASSERT(GetSelection(), "keine selection!!!"); + + if (!m_pSeekCursor) + return; + +/* Application::EnterWait(); + Reference< XPropertySet > xSet = (XPropertySet*)xSeekCursor->queryInterface(XPropertySet::getSmartUik()); + + // wenn mehr als 25 Datensaetze geloescht werden, wird der Cache abgeschaltet + // da das loeschen ansonsten zu langsam wird + sal_uInt16 nCacheSize = 0; + if (GetSelectRowCount() > 25) + { + // CacheSize merken und Cache zuruecksetzen + nCacheSize = xSet->getPropertyValue(L"CacheSize").getUINT16(); + if (nCacheSize) + xSet->setPropertyValue(L"CacheSize", Any(sal_uInt16(0))); + } */ + + + /* + // mu� der Cache wiederhergestellt werden? + if (nCacheSize) + { + // Cache wieder einschalten + xSet->setPropertyValue(L"CacheSize", Any(sal_uInt16(nCacheSize))); + + // Browser neu einstellen + RecalcRows(GetTopRow(), GetVisibleRows(), sal_True); + + // aktuelle Zeile aktualisieren + SeekCursor(GetCurRow()); + if (IsAppendRow(m_nSeekPos)) + xDataCursor->addRecord(); + else + { + Any aBookmark = xSeekCursor->getBookmark(); + xDataCursor->moveToBookmark(aBookmark); + } + m_xCurrentRow = new DbGridRow(xDataCursor); + m_nCurrentPos = m_nSeekPos; + + // complett invalidieren + Invalidate(); + } + else + // Browser neu einstellen + RecalcRows(GetTopRow(), GetVisibleRows(), sal_True); + + // gibt es keine Selection mehr? + if (!GetSelectRowCount()) + ActivateCell(); + + m_aBar.InvalidateAll(); + Application::LeaveWait(); + + m_bUpdating = sal_False; +*/ +} + +//------------------------------------------------------------------------------ +CellController* DbGridControl::GetController(long /*nRow*/, sal_uInt16 nColumnId) +{ + if (!IsValid(m_xCurrentRow) || !IsEnabled()) + return NULL; + + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId)); + if (!pColumn) + return NULL; + + CellController* pReturn = NULL; + if (IsFilterMode()) + pReturn = &pColumn->GetController(); + else + { + if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel())) + { + if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED))) + return NULL; + } + + sal_Bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & OPT_INSERT)); + sal_Bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & OPT_UPDATE)); + + if ((bInsert && !pColumn->IsAutoValue()) || bUpdate || m_bForceROController) + { + pReturn = &pColumn->GetController(); + if (pReturn) + { + // wenn es eine Edit-Zeile ist, kann ich ihr das forced read-only mitgeben + if (!pReturn->ISA(EditCellController) && !pReturn->ISA(SpinCellController)) + // ich konnte den Controller in forceROController nicht auf ReadOnly setzen + if (!bInsert && !bUpdate) + // ich bin nur hier, da m_bForceROController gesetzt war + // -> lieber kein Controller als einer ohne RO + pReturn = NULL; + } + } + } + return pReturn; +} + +//------------------------------------------------------------------------------ +void DbGridControl::InitController(CellControllerRef& /*rController*/, long /*nRow*/, sal_uInt16 nColumnId) +{ + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId)); + if (pColumn) + pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter); +} + +//------------------------------------------------------------------------------ +void DbGridControl::CellModified() +{ + TRACE_RANGE("DbGridControl::CellModified"); + + { + ::osl::MutexGuard aGuard(m_aAdjustSafety); + if (m_nAsynAdjustEvent) + { + TRACE_RANGE_MESSAGE1("forcing a synchron call to ", m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"); + RemoveUserEvent(m_nAsynAdjustEvent); + m_nAsynAdjustEvent = 0; + + // 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()) + { + // Einschalten des Editiermodus + // Datensatz soll eingefuegt werden + if (m_xCurrentRow->IsNew()) + { + m_xCurrentRow->SetStatus(GRS_MODIFIED); + TRACE_RANGE_MESSAGE("current row is new, new state : MODIFIED"); + // wenn noch keine Zeile hinzugefuegt wurde, dann neue hinzunehmen + if (m_nCurrentPos == GetRowCount() - 1) + { + // RowCount um einen erhoehen + RowInserted(GetRowCount(), 1, sal_True); + InvalidateStatusCell(m_nCurrentPos); + m_aBar.InvalidateAll(m_nCurrentPos); + } + } + else if (m_xCurrentRow->GetStatus() != GRS_MODIFIED) + { + m_xCurrentRow->SetState(m_pDataCursor, sal_False); + TRACE_RANGE_MESSAGE1("current row is not new, after SetState, new state : %s", ROWSTATUS(m_xCurrentRow)); + m_xCurrentRow->SetStatus(GRS_MODIFIED); + TRACE_RANGE_MESSAGE("current row is not new, new state : MODIFIED"); + InvalidateStatusCell(m_nCurrentPos); + } + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::Dispatch(sal_uInt16 nId) +{ + if (nId == BROWSER_CURSORENDOFFILE) + { + if (m_nOptions & OPT_INSERT) + AppendNew(); + else + MoveToLast(); + } + else + DbGridControl_Base::Dispatch(nId); +} + +//------------------------------------------------------------------------------ +void DbGridControl::Undo() +{ + if (!IsFilterMode() && IsValid(m_xCurrentRow) && IsModified()) + { + // check if we have somebody doin' the UNDO for us + long nState = -1; + if (m_aMasterStateProvider.IsSet()) + nState = m_aMasterStateProvider.Call((void*)SID_FM_RECORD_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 ?"); + long lResult = m_aMasterSlotExecutor.Call((void*)SID_FM_RECORD_UNDO); + if (lResult) + // handled + return; + } + else if (nState == 0) + // yes, we have, and the slot is disabled + return; + + BeginCursorAction(); + + sal_Bool bAppending = m_xCurrentRow->IsNew(); + sal_Bool bDirty = m_xCurrentRow->IsModified(); + + try + { + // Editieren abbrechen + 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(); + } + + EndCursorAction(); + + m_xDataRow->SetState(m_pDataCursor, sal_False); + if (&m_xPaintRow == &m_xCurrentRow) + m_xPaintRow = m_xCurrentRow = m_xDataRow; + else + m_xCurrentRow = m_xDataRow; + + if (bAppending && (DbGridControl_Base::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, 1, sal_True); + 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 obsolet "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, 1, sal_True); + m_aBar.InvalidateAll(m_nCurrentPos); + } + } + } + + // update the rows + m_xDataRow->SetState(m_pDataCursor, sal_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( long nRow, sal_uInt16 /*nColId*/ ) +{ + if (nRow == m_nCurrentPos && IsEditing()) + { + CellControllerRef aTmpRef = Controller(); + aTmpRef->ClearModified(); + InitController(aTmpRef, m_nCurrentPos, GetCurColumnId()); + } + DbGridControl_Base::RowModified(nRow); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::IsModified() const +{ + return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || DbGridControl_Base::IsModified()); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::IsCurrentAppending() const +{ + return m_xCurrentRow.Is() && m_xCurrentRow->IsNew(); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::IsInsertionRow(long nRow) const +{ + return (m_nOptions & OPT_INSERT) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1); +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::SaveModified() +{ + TRACE_RANGE("DbGridControl::SaveModified"); + DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row"); + if (!IsValid(m_xCurrentRow)) + return sal_True; + + // Uebernimmt die Dateneingabe fuer das Feld + // Hat es aenderungen im aktuellen Eingabefeld gegeben ? + if (!DbGridControl_Base::IsModified()) + return sal_True; + + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(GetCurColumnId())); + sal_Bool bOK = 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()->ClearModified(); + + if ( IsValid(m_xCurrentRow) ) + { + m_xCurrentRow->SetState(m_pDataCursor, sal_False); + TRACE_RANGE_MESSAGE1("explicit SetState, new state : %s", ROWSTATUS(m_xCurrentRow)); + InvalidateStatusCell( m_nCurrentPos ); + } +#ifdef DBG_UTIL + else + { + TRACE_RANGE_MESSAGE1("no SetState, new state : %s", ROWSTATUS(m_xCurrentRow)); + } +#endif + } + else + { + // reset the modified flag .... + Controller()->SetModified(); + } + + return bOK; +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::SaveRow() +{ + TRACE_RANGE("DbGridControl::SaveRow"); + // gueltige Zeile + if (!IsValid(m_xCurrentRow) || !IsModified()) + return sal_True; + // Wert des Controllers noch nicht gespeichert + else if (Controller().Is() && Controller()->IsModified()) + { + if (!SaveModified()) + return sal_False; + } + m_bUpdating = sal_True; + + BeginCursorAction(); + sal_Bool bAppending = m_xCurrentRow->IsNew(); + sal_Bool bSuccess = sal_False; + try + { + Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY); + if (bAppending) + xUpdateCursor->insertRow(); + else + xUpdateCursor->updateRow(); + bSuccess = sal_True; + } + catch(SQLException& e) + { + (void)e; // make compiler happy + EndCursorAction(); + m_bUpdating = sal_False; + return sal_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, sal_False); + TRACE_RANGE_MESSAGE1("explicit SetState after a successfull update, new state : %s", ROWSTATUS(m_xCurrentRow)); + m_xCurrentRow->SetNew(sal_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, sal_True); + m_nSeekPos = m_pSeekCursor->getRow() - 1; + } + } + // and repaint the row + RowModified(m_nCurrentPos); + } + catch(Exception&) + { + } + + m_bUpdating = sal_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 sal_True; +} + +//------------------------------------------------------------------------------ +long DbGridControl::PreNotify(NotifyEvent& rEvt) +{ + // keine Events der Navbar behandeln + if (m_aBar.IsWindowOrChild(rEvt.GetWindow())) + return BrowseBox::PreNotify(rEvt); + + switch (rEvt.GetType()) + { + case EVENT_KEYINPUT: + { + const KeyEvent* pKeyEvent = rEvt.GetKeyEvent(); + + sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode(); + sal_Bool bShift = pKeyEvent->GetKeyCode().IsShift(); + sal_Bool bCtrl = pKeyEvent->GetKeyCode().IsMod1(); + sal_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 + KeyCode aNewCode( KEY_TAB, bShift, sal_False, sal_False, sal_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 1; + } + + if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) ) + { + if (IsModified()) + { + Undo(); + return 1; + } + } + else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows + { + if ((m_nOptions & OPT_DELETE) && GetSelectRowCount()) + { + // delete asynchron + if (m_nDeleteEvent) + Application::RemoveUserEvent(m_nDeleteEvent); + m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete)); + return 1; + } + } + } // kein break! + default: + return DbGridControl_Base::PreNotify(rEvt); + } +} + +//------------------------------------------------------------------------------ +sal_Bool DbGridControl::IsTabAllowed(sal_Bool bRight) const +{ + if (bRight) + // Tab nur wenn nicht auf der letzten Zelle + return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal || + GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1); + else + { + // Tab nur wenn nicht auf der ersten Zelle + return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::KeyInput( const KeyEvent& rEvt ) +{ + if (rEvt.GetKeyCode().GetFunction() == KEYFUNC_COPY) + { + long nRow = GetCurRow(); + sal_uInt16 nColId = GetCurColumnId(); + if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount()) + { + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColId)); + OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this ); + return; + } + } + DbGridControl_Base::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 + + long lCurrentWidth = GetColumnWidth(nId); + DbGridControl_Base::RemoveColumn(nId); + // don't use my own RemoveColumn, this would remove it from m_aColumns, too + + // update my model + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nId)); + DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !"); + if (pColumn) + { + pColumn->m_bHidden = sal_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 != (sal_uInt16)-1, "DbGridControl::ShowColumn : invalid argument !"); + if (nPos == (sal_uInt16)-1) + return; + + DbGridColumn* pColumn = m_aColumns.GetObject(nPos); + if (!pColumn->IsHidden()) + { + DBG_ASSERT(GetViewColumnPos(nId) != (sal_uInt16)-1, "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) == (sal_uInt16)-1, "DbGridControl::ShowColumn : inconsistent internal state !"); + // the opposite situation ... + + // to determine the new view position we need an adjacent non-hidden column + sal_uInt16 nNextNonHidden = (sal_uInt16)-1; + // first search the cols to the right + for (sal_uInt16 i = nPos + 1; i<m_aColumns.Count(); ++i) + { + DbGridColumn* pCurCol = m_aColumns.GetObject(i); + if (!pCurCol->IsHidden()) + { + nNextNonHidden = i; + break; + } + } + if ((nNextNonHidden == (sal_uInt16)-1) && (nPos > 0)) + { + // then to the left + for (sal_uInt16 i = nPos; i>0; --i) + { + DbGridColumn* pCurCol = m_aColumns.GetObject(i-1); + if (!pCurCol->IsHidden()) + { + nNextNonHidden = i-1; + break; + } + } + } + sal_uInt16 nNewViewPos = (nNextNonHidden == (sal_uInt16)-1) + ? 1 // there is no visible column -> insert behinde the handle col + : GetViewColumnPos(m_aColumns.GetObject(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 != (sal_uInt16)-1, "DbGridControl::ShowColumn : inconsistent internal state !"); + // we found a col marked as visible but got no view pos for it ... + + if ((nNextNonHidden<nPos) && (nNextNonHidden != (sal_uInt16)-1)) + // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos + ++nNewViewPos; + + DeactivateCell(); + + ::rtl::OUString aName; + pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName; + InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HIB_CENTER | HIB_VCENTER | HIB_CLICKABLE, nNewViewPos); + pColumn->m_bHidden = sal_False; + + ActivateCell(); + Invalidate(); +} + +//------------------------------------------------------------------------------ +sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const +{ + if (nPos >= m_aColumns.Count()) + { + DBG_ERROR("DbGridControl::GetColumnIdFromModelPos : invalid argument !"); + return (sal_uInt16)-1; + } + + DbGridColumn* pCol = m_aColumns.GetObject(nPos); +#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL + // in der Debug-Version rechnen wir die ModelPos in eine ViewPos um und vergleichen das mit dem Wert, + // den wir zurueckliefern werden (nId an der entsprechenden Col in m_aColumns) + + if (!pCol->IsHidden()) + { // macht nur Sinn, wenn die Spalte sichtbar ist + sal_uInt16 nViewPos = nPos; + for (sal_uInt16 i=0; i<m_aColumns.Count() && i<nPos; ++i) + if (m_aColumns.GetObject(i)->IsHidden()) + --nViewPos; + + DBG_ASSERT(pCol && 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 (sal_uInt16 i=0; i<m_aColumns.Count(); ++i) + if (m_aColumns.GetObject(i)->GetId() == nId) + return i; + + return GRID_COLUMN_NOT_FOUND; +} + +//------------------------------------------------------------------------------ +void DbGridControl::implAdjustInSolarThread(BOOL _bRows) +{ + TRACE_RANGE("DbGridControl::implAdjustInSolarThread"); + ::osl::MutexGuard aGuard(m_aAdjustSafety); + if (::vos::OThread::getCurrentIdentifier() != Application::GetMainThreadIdentifier()) + { + m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows )); + m_bPendingAdjustRows = _bRows; +#ifdef DBG_UTIL + if (_bRows) + TRACE_RANGE_MESSAGE("posting an AdjustRows") + else + TRACE_RANGE_MESSAGE("posting an AdjustDataSource") +#endif + } + else + { +#ifdef DBG_UTIL + if (_bRows) + TRACE_RANGE_MESSAGE("doing an AdjustRows") + else + TRACE_RANGE_MESSAGE("doing an AdjustDataSource") +#endif + // 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) +{ + m_nAsynAdjustEvent = 0; + + AdjustRows(); + // see implAdjustInSolarThread for a comment why we do this every time + + if ( !pAdjustWhat ) + AdjustDataSource(); + + return 0L; +} + +//------------------------------------------------------------------------------ +void DbGridControl::BeginCursorAction() +{ + if (m_pFieldListeners) + { + ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners; + ConstColumnFieldValueListenersIterator aIter = pListeners->begin(); + while (aIter != pListeners->end()) + { + GridFieldValueListener* pCurrent = (*aIter).second; + if (pCurrent) + pCurrent->suspend(); + ++aIter; + } + } + + if (m_pDataSourcePropListener) + m_pDataSourcePropListener->suspend(); +} + +//------------------------------------------------------------------------------ +void DbGridControl::EndCursorAction() +{ + if (m_pFieldListeners) + { + ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners; + ConstColumnFieldValueListenersIterator aIter = pListeners->begin(); + while (aIter != pListeners->end()) + { + GridFieldValueListener* pCurrent = (*aIter).second; + if (pCurrent) + pCurrent->resume(); + ++aIter; + } + } + + if (m_pDataSourcePropListener) + m_pDataSourcePropListener->resume(); +} + +//------------------------------------------------------------------------------ +void DbGridControl::ConnectToFields() +{ + ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners; + DBG_ASSERT(!pListeners || pListeners->size() == 0, "DbGridControl::ConnectToFields : please call DisconnectFromFields first !"); + + if (!pListeners) + { + pListeners = new ColumnFieldValueListeners; + m_pFieldListeners = pListeners; + } + + for (sal_Int32 i=0; i<(sal_Int32)m_aColumns.Count(); ++i) + { + DbGridColumn* pCurrent = m_aColumns.GetObject(i); + sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : (sal_uInt16)-1; + if ((sal_uInt16)-1 == nViewPos) + continue; + + Reference< XPropertySet > xField = pCurrent->GetField(); + if (!xField.is()) + continue; + + // column is visible and bound here + GridFieldValueListener*& rpListener = (*pListeners)[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_pFieldListeners) + return; + + ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners; + while (pListeners->size()) + { +#ifdef DBG_UTIL + sal_Int32 nOldSize = pListeners->size(); +#endif + pListeners->begin()->second->dispose(); + DBG_ASSERT(nOldSize > (sal_Int32)pListeners->size(), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !"); + } + + delete pListeners; + m_pFieldListeners = NULL; +} + +//------------------------------------------------------------------------------ +void DbGridControl::FieldValueChanged(sal_uInt16 _nId, const PropertyChangeEvent& /*_evt*/) +{ + osl::MutexGuard aPreventDestruction(m_aDestructionSafety); + // needed as this may run in a thread other than the main one + if (GetRowStatus(GetCurRow()) != DbGridControl_Base::MODIFIED) + // all other cases are handled elsewhere + return; + + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(_nId)); + if (pColumn) + { + sal_Bool bAcquiredPaintSafety = sal_False; + while (!m_bWantDestruction && !bAcquiredPaintSafety) + bAcquiredPaintSafety = Application::GetSolarMutex().tryToAcquire(); + + 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 + if (bAcquiredPaintSafety) + // though the above while-loop suggests that (m_bWantDestruction && bAcquiredPaintSafety) is impossible, + // it isnt't, as m_bWantDestruction isn't protected with any mutex + Application::GetSolarMutex().release(); + return; + } + // here we got the solar mutex, transfer it to a guard for safety reasons + ::vos::OGuard aPaintSafety(Application::GetSolarMutex()); + Application::GetSolarMutex().release(); + + // and finally do the update ... + pColumn->UpdateFromField(m_xCurrentRow, m_xFormatter); + RowModified(GetCurRow(), _nId); + } +} + +//------------------------------------------------------------------------------ +void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId) +{ + ColumnFieldValueListeners* pListeners = (ColumnFieldValueListeners*)m_pFieldListeners; + if (!pListeners) + { + DBG_ERROR("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !"); + return; + } + + ColumnFieldValueListenersIterator aPos = pListeners->find(_nId); + if (aPos == pListeners->end()) + { + DBG_ERROR("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !"); + return; + } + + delete aPos->second; + + pListeners->erase(aPos); +} + +//------------------------------------------------------------------------------ +void DbGridControl::disposing(sal_uInt16 _nId, const EventObject& /*_rEvt*/) +{ + if (_nId == 0) + { // the seek cursor is beeing disposed + ::osl::MutexGuard aGuard(m_aAdjustSafety); + setDataSource(NULL,0); // our clone was disposed so we set our datasource to null to avoid later acces to it + if (m_nAsynAdjustEvent) + { + RemoveUserEvent(m_nAsynAdjustEvent); + m_nAsynAdjustEvent = 0; + } + } +} +// ----------------------------------------------------------------------------- +sal_Int32 DbGridControl::GetAccessibleControlCount() const +{ + return DbGridControl_Base::GetAccessibleControlCount() + 1; // the navigation control +} +// ----------------------------------------------------------------------------- +Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex ) +{ + Reference<XAccessible > xRet; + if ( _nIndex == DbGridControl_Base::GetAccessibleControlCount() ) + { + xRet = m_aBar.GetAccessible(); + } + else + xRet = DbGridControl_Base::CreateAccessibleControl( _nIndex ); + return xRet; +} +// ----------------------------------------------------------------------------- +Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) +{ + USHORT nColumnId = GetColumnId( _nColumnPos ); + DbGridColumn* pColumn = m_aColumns.GetObject(GetModelColumnPos(nColumnId)); + if ( pColumn ) + { + Reference< ::com::sun::star::awt::XControl> xInt(pColumn->GetCell()); + Reference< ::com::sun::star::awt::XCheckBox> xBox(xInt,UNO_QUERY); + if ( xBox.is() ) + { + TriState eValue = STATE_NOCHECK; + switch( xBox->getState() ) + { + case 0: + eValue = STATE_NOCHECK; + break; + case 1: + eValue = STATE_CHECK; + break; + case 2: + eValue = STATE_DONTKNOW; + break; + } + return DbGridControl_Base::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue,TRUE ); + } + } + return DbGridControl_Base::CreateAccessibleCell( _nRow, _nColumnPos ); +} +// ----------------------------------------------------------------------------- + + |