/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
void NavigationBar::PositionDataSource(sal_Int32 nRecord)
{ if (m_bPositioning) return; // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition, // so protect against this recursion
m_bPositioning = true; static_cast<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1);
m_bPositioning = false;
}
// set handlers for buttons
m_xFirstBtn->connect_clicked(LINK(this,NavigationBar,OnClick));
m_xLastBtn->connect_clicked(LINK(this,NavigationBar,OnClick));
m_xNewBtn->connect_clicked(LINK(this,NavigationBar,OnClick));
if ( bAll )
{
m_nCurrentPos = nCurrentPos; int i = 0; while (ControlMap[i] != DbGridControlNavigationBarState::NONE)
SetState(ControlMap[i++]);
} else// is in the center
{
m_nCurrentPos = nCurrentPos;
SetState(DbGridControlNavigationBarState::Count);
SetState(DbGridControlNavigationBarState::Absolute);
}
}
if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
|| pParent->IsFilterMode() ) returnfalse; else
{ // check if we have a master state provider if (pParent->m_aMasterStateProvider.IsSet())
{
tools::Long nState = pParent->m_aMasterStateProvider.Call( nWhich ); if (nState>=0) return (nState>0);
}
void NavigationBar::SetState(DbGridControlNavigationBarState nWhich)
{ bool bAvailable = GetState(nWhich);
DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
weld::Widget* pWnd = nullptr; switch (nWhich)
{ case DbGridControlNavigationBarState::First:
pWnd = m_xFirstBtn.get(); break; case DbGridControlNavigationBarState::Prev:
pWnd = m_xPrevBtn.get(); break; case DbGridControlNavigationBarState::Next:
pWnd = m_xNextBtn.get(); break; case DbGridControlNavigationBarState::Last:
pWnd = m_xLastBtn.get(); break; case DbGridControlNavigationBarState::New:
pWnd = m_xNewBtn.get(); break; case DbGridControlNavigationBarState::Absolute:
pWnd = m_xAbsolute->GetWidget(); if (bAvailable)
m_xAbsolute->set_text(OUString::number(m_nCurrentPos + 1)); else
m_xAbsolute->set_text(OUString()); break; case DbGridControlNavigationBarState::Text:
pWnd = m_xRecordText.get(); break; case DbGridControlNavigationBarState::Of:
pWnd = m_xRecordOf.get(); break; case DbGridControlNavigationBarState::Count:
{
pWnd = m_xRecordCount.get();
OUString aText; if (bAvailable)
{ if (pParent->GetOptions() & DbGridControlOptions::Insert)
{ if (pParent->IsCurrentAppending() && !pParent->IsModified())
aText = OUString::number(pParent->GetRowCount()); else
aText = OUString::number(pParent->GetRowCount() - 1);
} else
aText = OUString::number(pParent->GetRowCount()); if(!pParent->m_bRecordCountFinal)
aText += " *";
} else
aText.clear();
// add the number of selected rows, if applicable if (pParent->GetSelectRowCount())
{
OUString aExtendedInfo = aText + " (" +
OUString::number(pParent->GetSelectRowCount()) + ")";
m_xRecordCount->set_label(aExtendedInfo);
} else
m_xRecordCount->set_label(aText);
pParent->SetRealRowCount(aText);
} break; default: break;
}
DBG_ASSERT(pWnd, "no window"); if (!(pWnd && (pWnd->get_sensitive() != bAvailable))) return;
// this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we // do this check. // For further explanation see Bug 69900.
pWnd->set_sensitive(bAvailable); if (!bAvailable)
{ if (pWnd == m_xNextBtn.get())
m_xNextRepeater->Stop(); elseif (pWnd == m_xPrevBtn.get())
m_xPrevRepeater->Stop();
}
}
void DbGridControl::InsertHandleColumn()
{ // BrowseBox has problems when painting without a handleColumn (hide it here) if (HasHandle())
BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString())); else
BrowseBox::InsertHandleColumn(0);
}
m_bWantDestruction = true;
osl::MutexGuard aGuard(m_aDestructionSafety); if (!m_aFieldListeners.empty())
DisconnectFromFields();
m_pCursorDisposeListener.reset();
if (m_nDeleteEvent)
Application::RemoveUserEvent(m_nDeleteEvent);
if (m_pDataSourcePropMultiplexer.is())
{
m_pDataSourcePropMultiplexer->dispose();
m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer delete m_pDataSourcePropListener;
m_pDataSourcePropListener = nullptr;
}
m_xRowSetListener.clear();
m_pDataCursor.reset();
m_pSeekCursor.reset();
m_aBar.disposeAndClear();
m_aRearrangeIdle.Stop();
EditBrowseBox::dispose();
}
void DbGridControl::RearrangeAtIdle()
{ if (isDisposed()) return;
m_aRearrangeIdle.Start();
}
// as the selected rows may have changed, update the according display in our navigation bar
m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
if (m_pGridListener)
m_pGridListener->selectionChanged();
}
// reset number of sentences to zero in the browser
EditBrowseBox::RemoveRows();
m_aBar->InvalidateAll(m_nCurrentPos, true);
}
void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
{ // positioning of the controls if (m_bNavigationBar)
{
tools::Rectangle aRect(GetControlArea());
nX = m_aBar->GetPreferredWidth();
m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(nX, aRect.GetSize().Height() - 1));
}
}
void DbGridControl::EnableHandle(bool bEnable)
{ if (m_bHandle == bEnable) return;
// HandleColumn is only hidden because there are a lot of problems while painting otherwise
RemoveColumn( HandleColumnId );
m_bHandle = bEnable;
InsertHandleColumn();
}
// note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular, // _bHideScrollbars is ignored then if ( _bNavigationBar )
{
_rMode |= BrowserMode::AUTO_HSCROLL;
_rMode &= ~BrowserMode::NO_HSCROLL;
}
return nOldMode != _rMode;
}
}
void DbGridControl::EnableNavigationBar(bool bEnable)
{ if (m_bNavigationBar == bEnable) return;
m_bNavigationBar = bEnable;
if (bEnable)
{
m_aBar->Show();
m_aBar->Enable();
m_aBar->InvalidateAll(m_nCurrentPos, true);
DbGridControlOptions DbGridControl::SetOptions(DbGridControlOptions nOpt)
{
DBG_ASSERT(!m_xCurrentRow.is() || !m_xCurrentRow->IsModified(), "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
// for the next setDataSource (which is triggered by a refresh, for instance)
m_nOptionMask = nOpt;
// normalize the new options
Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet(); if (xDataSourceSet.is())
{ // check what kind of options are available
sal_Int32 nPrivileges = 0;
xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges; if ((nPrivileges & Privilege::INSERT) == 0)
nOpt &= ~DbGridControlOptions::Insert; if ((nPrivileges & Privilege::UPDATE) == 0)
nOpt &= ~DbGridControlOptions::Update; if ((nPrivileges & Privilege::DELETE) == 0)
nOpt &= ~DbGridControlOptions::Delete;
} else
nOpt = DbGridControlOptions::Readonly;
// need to do something after that ? if (nOpt == m_nOptions) return m_nOptions;
// the 'update' option only affects our BrowserMode (with or w/o focus rect)
BrowserMode nNewMode = m_nMode; if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS))
{ if (nOpt & DbGridControlOptions::Update)
nNewMode |= BrowserMode::HIDECURSOR; else
nNewMode &= ~BrowserMode::HIDECURSOR;
} else
nNewMode &= ~BrowserMode::HIDECURSOR; // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
if (nNewMode != m_nMode)
{
SetMode(nNewMode);
m_nMode = nNewMode;
}
// _after_ setting the mode because this results in an ActivateCell
DeactivateCell();
bool bInsertChanged = (nOpt & DbGridControlOptions::Insert) != (m_nOptions & DbGridControlOptions::Insert);
m_nOptions = nOpt; // we need to set this before the code below because it indirectly uses m_nOptions
// the 'insert' option affects our empty row if (bInsertChanged)
{ if (m_nOptions & DbGridControlOptions::Insert)
{ // the insert option is to be set
m_xEmptyRow = new DbGridRow();
RowInserted(GetRowCount());
} else
{ // the insert option is to be reset
m_xEmptyRow = nullptr; if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
RowRemoved(GetRowCount());
}
}
// the 'delete' options has no immediate consequences
void DbGridControl::EnablePermanentCursor(bool bEnable)
{ if (IsPermanentCursorEnabled() == bEnable) return;
if (bEnable)
{
m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect
m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
} else
{ if (m_nOptions & DbGridControlOptions::Update)
m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all else
m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor
void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
{ if ((GetCurColumnId() == _nColId) && IsEditing())
{ // the controller which is currently active needs to be refreshed
DeactivateCell();
ActivateCell();
}
}
if (m_pDataSourcePropMultiplexer.is())
{
m_pDataSourcePropMultiplexer->dispose();
m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer delete m_pDataSourcePropListener;
m_pDataSourcePropListener = nullptr;
}
m_xRowSetListener.clear();
// is the new cursor valid ? // the cursor is only valid if it contains some columns // if there is no cursor or the cursor is not valid we have to clean up and leave if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY_THROW)->getColumns()->hasElements())
{
RemoveRows(); return;
}
// did the data cursor change?
sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
{
::osl::MutexGuard aGuard(m_aAdjustSafety); if (m_nAsynAdjustEvent)
{ // the adjust was thought to work with the old cursor which we don't have anymore
RemoveUserEvent(m_nAsynAdjustEvent);
m_nAsynAdjustEvent = nullptr;
}
}
// get a new formatter and data cursor
m_xFormatter = nullptr;
Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true); if (xSupplier.is())
{
m_xFormatter = css::util::NumberFormatter::create(m_xContext);
m_xFormatter->attachNumberFormatsSupplier(xSupplier);
// retrieve the datebase of the Numberformatter try
{
xSupplier->getNumberFormatSettings()->getPropertyValue(u"NullDate"_ustr) >>= m_aNullDate;
} catch(Exception&)
{
}
}
m_pDataCursor.reset(new CursorWrapper(_xCursor));
// now create a cursor for painting rows // we need that cursor only if we are not in insert only mode
Reference< XResultSet > xClone;
Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY ); try
{
xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
} catch(Exception&)
{
} if (xClone.is())
m_pSeekCursor.reset(new CursorWrapper(xClone));
// property listening on the data source // (Normally one class would be sufficient : the multiplexer which could forward the property change to us. // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported. // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class) // and forwards the property changes to our special method "DataSourcePropertyChanged".) if (m_pDataCursor)
{
m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
}
BrowserMode nOldMode = m_nMode; if (m_pSeekCursor)
{ try
{
Reference< XPropertySet > xSet(_xCursor, UNO_QUERY); if (xSet.is())
{ // check what kind of options are available
sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
m_xRowSetListener = new RowSetEventListener(this);
Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY); if ( xChangeBroad.is( ) )
xChangeBroad->addRowsChangeListener(m_xRowSetListener);
// insert the currently known rows // and one row if we are able to insert rows if (m_nOptions & DbGridControlOptions::Insert)
{ // insert the empty row for insertion
m_xEmptyRow = new DbGridRow();
++nRecordCount;
} if (nRecordCount)
{
m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor.get(), true);
m_xDataRow = new DbGridRow(m_pDataCursor.get(), false);
RowInserted(0, nRecordCount, false);
if (m_xSeekRow->IsValid()) try
{
m_nSeekPos = m_pSeekCursor->getRow() - 1;
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
m_nSeekPos = -1;
}
} else
{ // no rows so we don't need a seekcursor
m_pSeekCursor.reset();
}
}
// go to the old column if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
nCurPos = 0;
// Column zero is a valid choice and guaranteed to exist, // but invisible to the user; if we have at least one // user-visible column, go to that one. if (nCurPos == 0 && ColCount() > 1)
nCurPos = 1;
// there are rows so go to the selected current column if (nRecordCount)
GoToRowColumnId(0, GetColumnId(nCurPos)); // else stop the editing if necessary elseif (IsEditing())
DeactivateCell();
// now reset the mode if (m_nMode != nOldMode)
SetMode(m_nMode);
// RecalcRows was already called while resizing if (!IsResizing() && GetRowCount())
RecalcRows(GetTopRow(), GetVisibleRows(), true);
sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
{
DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
sal_uInt16 nRealPos = nModelPos; if (nModelPos != HEADERBAR_APPEND)
{ // calc the view pos. we can't use our converting functions because the new column // has no VCL-representation, yet.
sal_Int16 nViewPos = nModelPos; while (nModelPos--)
{ if ( m_aColumns[ nModelPos ]->IsHidden() )
--nViewPos;
} // restore nModelPos, we need it later
nModelPos = nRealPos; // the position the base class gets is the view pos + 1 (because of the handle column)
nRealPos = nViewPos + 1;
}
// calculate the new id for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && size_t(nId) <= m_aColumns.size(); ++nId)
;
DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !"); // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
// remove the col from the model
sal_uInt16 nOldModelPos = GetModelColumnPos(nId); #ifdef DBG_UTIL
DbGridColumn* pCol = m_aColumns[ nOldModelPos ].get();
DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?"); #endif
// for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment // so the method won't work (in fact it would return the old model pos)
// the new view pos is calculated easily
sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
// from that we can compute the new model pos
size_t nNewModelPos; for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
{ if (!m_aColumns[ nNewModelPos ]->IsHidden())
{ if (!nNewViewPos) break; else
--nNewViewPos;
}
}
DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
// this will work. of course the model isn't fully consistent with our view right now, but let's // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the // other case we can use analogue arguments). // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n. // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols // within this range is constant, so we may calculate the view pos from the model pos in the above way.
// for instance, let's look at a grid with six columns where the third one is hidden. this will // initially look like this :
// 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 :
// Now, all is consistent again. // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe // the user expected the latter but there really is no good argument against our method ;) ...)
// And no, this large explanation isn't just because I wanted to play a board game or something like // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
bool DbGridControl::SeekRow(sal_Int32 nRow)
{ // in filter mode or in insert only mode we don't have any cursor! if ( !SeekCursor( nRow ) ) returnfalse;
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 elseif ( IsInsertionRow( nRow ) )
m_xPaintRow = m_xEmptyRow; else
{
m_xSeekRow->SetState( m_pSeekCursor.get(), true );
m_xPaintRow = m_xSeekRow;
}
}
EditBrowseBox::SeekRow(nRow);
return m_nSeekPos >= 0;
}
// Is called whenever the visible amount of data changes void DbGridControl::VisibleRowsChanged( sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen )
{
RecalcRows(nNewTopRow, nLinesOnScreen, false);
}
void DbGridControl::RecalcRows(sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
{ // If no cursor -> no rows in the browser. if (!m_pSeekCursor)
{
DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there"); return;
}
// ignore any implicitly made updates bool bDisablePaint = !bUpdateCursor && IsPaintEnabled(); if (bDisablePaint)
EnablePaint(false);
// adjust cache to the visible area
Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
sal_Int32 nCacheSize = 0;
xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize; bool bCacheAligned = false; // no further cursor movements after initializing (m_nSeekPos < 0) because it is already // positioned on the first sentence
tools::Long nDelta = nNewTopRow - GetTopRow(); // limit for relative positioning
tools::Long nLimit = nCacheSize ? nCacheSize / 2 : 0;
// more lines on screen than in cache if (nLimit < nLinesOnScreen)
{
Any aCacheSize;
aCacheSize <<= sal_Int32(nLinesOnScreen*2);
xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize); // here we need to update the cursor for sure
bUpdateCursor = true;
bCacheAligned = true;
nLimit = nLinesOnScreen;
}
// In the following, all positionings are done as it is // ensured that there are enough lines in the data cache
// window goes downwards with less than two windows difference or // the cache was updated and no rowcount yet if (nDelta < nLimit && (nDelta > 0
|| (bCacheAligned && m_nTotalCount < 0)) )
SeekCursor(nNewTopRow + nLinesOnScreen - 1); elseif (nDelta < 0 && std::abs(nDelta) < nLimit)
SeekCursor(nNewTopRow); elseif (nDelta != 0 || bUpdateCursor)
SeekCursor(nNewTopRow, true);
AdjustRows();
// ignore any updates implicit made
EnablePaint(true);
}
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;
} elseif (m_nTotalCount >= 0)
m_nTotalCount += 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;
} elseif (m_nTotalCount >= 0)
m_nTotalCount -= nNumRows;
// Did the number of rows change? // Here we need to consider that there might be an additional row for adding new data sets
// add additional AppendRow for insertion if (m_nOptions & DbGridControlOptions::Insert)
++nRecordCount;
// If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
m_xCurrentRow->IsNew())
++nRecordCount; // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount // and a second time here (60787 - FS)
if (nRecordCount != GetRowCount())
{
tools::Long nDelta = GetRowCount() - static_cast<tools::Long>(nRecordCount); if (nDelta > 0) // too many
{
RowRemoved(GetRowCount() - nDelta, nDelta, false); // some rows are gone, thus, repaint starting at the current position
Invalidate();
sal_Int32 nNewPos = AlignSeekCursor(); if (m_bSynchDisplay)
EditBrowseBox::GoToRow(nNewPos);
SetCurrent(nNewPos); // there are rows so go to the selected current column if (nRecordCount)
GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId())); if (!IsResizing() && GetRowCount())
RecalcRows(GetTopRow(), GetVisibleRows(), true);
m_aBar->InvalidateAll(m_nCurrentPos, true);
} else// too few
RowInserted(GetRowCount(), -nDelta);
}
bool DbGridControl::SetCurrent(sal_Int32 nNewRow)
{ // Each movement of the datacursor must start with BeginCursorAction and end with // EndCursorAction to block all notifications during the movement
BeginCursorAction();
try
{ // compare positions if (SeekCursor(nNewRow))
{ if (IsFilterRow(nNewRow)) // special mode for filtering
{
m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
m_nCurrentPos = nNewRow;
} else
{ bool bNewRowInserted = false; // Should we go to the insertrow ? if (IsInsertionRow(nNewRow))
{ // to we need to move the cursor to the insert row? // we need to insert the if the current row isn't the insert row or if the // cursor triggered the move by itself and we need a reinitialization of the row
Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet(); if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
{
Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
xUpdateCursor->moveToInsertRow();
}
bNewRowInserted = true;
} else
{
if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
{
Any aBookmark = m_pSeekCursor->getBookmark(); if (!m_xCurrentRow.is() || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
{ // adjust the cursor to the new desired row if (!m_pDataCursor->moveToBookmark(aBookmark))
{
EndCursorAction(); returnfalse;
}
}
}
}
m_xDataRow->SetState(m_pDataCursor.get(), false);
m_xCurrentRow = m_xDataRow;
tools::Long nPaintPos = -1; // do we have to repaint the last regular row in case of setting defaults or autovalues if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
nPaintPos = m_nCurrentPos;
m_nCurrentPos = nNewRow;
// repaint the new row to display all defaults if (bNewRowInserted)
RowModified(m_nCurrentPos); if (nPaintPos >= 0)
RowModified(nPaintPos);
}
} else
{
OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
EndCursorAction(); returnfalse;
}
} catch ( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
EndCursorAction(); returnfalse;
}
EndCursorAction(); returntrue;
}
void DbGridControl::CursorMoved()
{
// cursor movement due to deletion or insertion of rows if (m_pDataCursor && m_nCurrentPos != GetCurRow())
{
DeactivateCell();
SetCurrent(GetCurRow());
}
// select the new column when they moved if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
{
SelectColumnId( GetCurColumnId() );
}
if ( m_nLastColId != GetCurColumnId() )
onColumnChange();
m_nLastColId = GetCurColumnId();
void DbGridControl::onRowChange()
{ // not interested in
}
void DbGridControl::onColumnChange()
{ if ( m_pGridListener )
m_pGridListener->columnChanged();
}
void DbGridControl::setDisplaySynchron(bool bSync)
{ if (bSync != m_bSynchDisplay)
{
m_bSynchDisplay = bSync; if (m_bSynchDisplay)
AdjustDataSource();
}
}
void DbGridControl::AdjustDataSource(bool bFull)
{
SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
SolarMutexGuard aGuard; // If the current row is recalculated at the moment, do not adjust
if (bFull)
m_xCurrentRow = nullptr; // if we are on the same row only repaint // but this is only possible for rows which are not inserted, in that case the comparison result // may not be correct else if ( m_xCurrentRow.is()
&& !m_xCurrentRow->IsNew()
&& !m_pDataCursor->isBeforeFirst()
&& !m_pDataCursor->isAfterLast()
&& !m_pDataCursor->rowDeleted()
)
{ bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
if ( bEqualBookmarks && !bDataCursorIsOnNew )
{ // position of my data cursor is the same as the position our current row points tpo // sync the status, repaint, done
DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Errors in the data row");
SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
RowModified(m_nCurrentPos); return;
}
}
// away from the data cursor's row if (m_xPaintRow == m_xCurrentRow)
m_xPaintRow = m_xSeekRow;
// not up-to-date row, thus, adjust completely if (!m_xCurrentRow.is())
AdjustRows();
sal_Int32 nNewPos = AlignSeekCursor(); if (nNewPos < 0)// could not find any position return;
if (nNewPos != m_nCurrentPos)
{ if (m_bSynchDisplay)
EditBrowseBox::GoToRow(nNewPos);
if (!m_xCurrentRow.is()) // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what // CurrentRow is corrected to point two rows down, so that GoToRow will point into // emptiness (since we are - purportedly - at the correct position)
SetCurrent(nNewPos);
} else
{
SetCurrent(nNewPos);
RowModified(nNewPos);
}
// if the data cursor was moved from outside, this section is voided
SetNoSelection();
m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.is());
}
sal_Int32 DbGridControl::AlignSeekCursor()
{ // position SeekCursor onto the data cursor, no data transmission
// now align the seek cursor and the data cursor if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
m_nSeekPos = GetRowCount() - 1; else
{ try
{ if ( m_pDataCursor->isBeforeFirst() )
{ // this is somewhat strange, but can nevertheless happen
SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
m_pSeekCursor->first();
m_pSeekCursor->previous();
m_nSeekPos = -1;
} elseif ( m_pDataCursor->isAfterLast() )
{
SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
m_pSeekCursor->last();
m_pSeekCursor->next();
m_nSeekPos = -1;
} else
{
m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark()); if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark())) // unfortunately, moveToBookmark might lead to a re-positioning of the seek // cursor (if the complex moveToBookmark with all its events fires an update // somewhere) -> retry
m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark()); // Now there is still the chance of a failure but it is less likely. // The alternative would be a loop until everything is fine - no good solution...
m_nSeekPos = m_pSeekCursor->getRow() - 1;
}
} catch(Exception&)
{
}
} return m_nSeekPos;
}
bool DbGridControl::SeekCursor(sal_Int32 nRow, bool bAbsolute)
{ // position SeekCursor onto the data cursor, no data transmission
// additions for the filtermode if (IsFilterRow(nRow))
{
m_nSeekPos = 0; returntrue;
}
if (!m_pSeekCursor) returnfalse;
// is this an insertion? if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
nRow >= m_nCurrentPos)
{ // if so, scrolling down must be prevented as this is already the last data set! if (nRow == m_nCurrentPos)
{ // no adjustment necessary
m_nSeekPos = nRow;
} elseif (IsInsertionRow(nRow)) // blank row for data insertion
m_nSeekPos = nRow;
} elseif (IsInsertionRow(nRow)) // blank row for data insertion
m_nSeekPos = nRow; elseif ((-1 == nRow) && (GetRowCount() == ((m_nOptions & DbGridControlOptions::Insert) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
m_nSeekPos = nRow; else
{ bool bSuccess = false;
tools::Long nSteps = 0; try
{ if ( m_pSeekCursor->rowDeleted() )
{ // somebody deleted the current row of the seek cursor. Move it away from this row.
m_pSeekCursor->next(); if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
bAbsolute = true;
}
if ( !bAbsolute )
{
DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(), "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
nSteps = nRow - (m_pSeekCursor->getRow() - 1);
bAbsolute = std::abs(nSteps) > 100;
}
if ( bAbsolute )
{
bSuccess = m_pSeekCursor->absolute(nRow + 1); if (bSuccess)
m_nSeekPos = nRow;
} else
{ if (nSteps > 0) // position onto the last needed data set
{ if (m_pSeekCursor->isAfterLast())
bSuccess = false; elseif (m_pSeekCursor->isBeforeFirst())
bSuccess = m_pSeekCursor->absolute(nSteps); else
bSuccess = m_pSeekCursor->relative(nSteps);
} elseif (nSteps < 0)
{ if (m_pSeekCursor->isBeforeFirst())
bSuccess = false; elseif (m_pSeekCursor->isAfterLast())
bSuccess = m_pSeekCursor->absolute(nSteps); else
bSuccess = m_pSeekCursor->relative(nSteps);
} else
{
m_nSeekPos = nRow; returntrue;
}
}
} catch(Exception&)
{
OSL_FAIL("DbGridControl::SeekCursor : failed ...");
}
try
{ if (!bSuccess)
{ if (bAbsolute || nSteps > 0)
{ if (m_pSeekCursor->isLast())
bSuccess = true; else
bSuccess = m_pSeekCursor->last();
} else
{ if (m_pSeekCursor->isFirst())
bSuccess = true; else
bSuccess = m_pSeekCursor->first();
}
}
if (bSuccess)
m_nSeekPos = m_pSeekCursor->getRow() - 1; else
m_nSeekPos = -1;
} catch(Exception&)
{
DBG_UNHANDLED_EXCEPTION("svx");
OSL_FAIL("DbGridControl::SeekCursor : failed ...");
m_nSeekPos = -1; // no further data set available
}
} return m_nSeekPos == nRow;
}
// position onto the last data set not on a blank row if (m_nOptions & DbGridControlOptions::Insert)
{ if ((GetRowCount() - 1) > 0)
MoveToPosition(GetRowCount() - 2);
} elseif (GetRowCount())
MoveToPosition(GetRowCount() - 1);
}
void DbGridControl::MoveToNext()
{ if (!m_pSeekCursor) return;
if (m_nTotalCount > 0)
{ // move the data cursor to the right position
tools::Long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1); if (GetCurRow() != nNewRow)
MoveToPosition(nNewRow);
} else
{ bool bOk = false; try
{ // try to move to next row // when not possible our paint cursor is already on the last row // then we must be sure that the data cursor is on the position // we call ourself again
bOk = m_pSeekCursor->next(); if (bOk)
{
m_nSeekPos = m_pSeekCursor->getRow() - 1;
MoveToPosition(GetCurRow() + 1);
}
} catch(SQLException &)
{
DBG_UNHANDLED_EXCEPTION("svx");
}
if(!bOk)
{
AdjustRows(); if (m_nTotalCount > 0) // only to avoid infinite recursion
MoveToNext();
}
}
}
void DbGridControl::MoveToPosition(sal_uInt32 nPos)
{ if (!m_pSeekCursor) return;
void DbGridControl::SetDesignMode(bool bMode)
{ if (IsDesignMode() == bMode) return;
// adjust Enable/Disable for design mode so that the headerbar remains configurable if (bMode)
{ if (!IsEnabled())
{
Enable();
GetDataWindow().Disable();
}
} else
{ // disable completely if (!GetDataWindow().IsEnabled())
Disable();
}
void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt)
{
SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
SolarMutexGuard aGuard; // prop "IsModified" changed ? // during update don't care about the modified state if (IsUpdating() || evt.PropertyName != FM_PROP_ISMODIFIED) return;
if (bIsNew && m_xCurrentRow.is())
{
DBG_ASSERT(::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ROWCOUNTFINAL)), "DbGridControl::DataSourcePropertyChanged : somebody moved the form to a new record before the row count was final !");
sal_Int32 nRecordCount = 0;
xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; if (::comphelper::getBOOL(evt.NewValue))
{ // modified state changed from sal_False to sal_True and we're on an insert row // -> we've to add a new grid row if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
{
RowInserted(GetRowCount());
InvalidateStatusCell(m_nCurrentPos);
m_aBar->InvalidateAll(m_nCurrentPos);
}
} else
{ // modified state changed from sal_True to sal_False and we're on an insert row // we have two "new row"s at the moment : the one we're editing currently (where the current // column is the only dirty element) and a "new new" row which is completely clean. As the first // one is about to be cleaned, too, the second one is obsolete now. if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
{
RowRemoved(GetRowCount() - 1);
InvalidateStatusCell(m_nCurrentPos);
m_aBar->InvalidateAll(m_nCurrentPos);
}
}
} if (m_xCurrentRow.is())
{
m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean);
m_xCurrentRow->SetNew( bIsNew );
InvalidateStatusCell(m_nCurrentPos);
SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
}
}
IMPL_LINK_NOARG(DbGridControl, RearrangeHdl, Timer*, void)
{ if (isDisposed()) return;
// and give it a chance to rearrange
Point aPoint = GetControlArea().TopLeft();
sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
ArrangeControls(nX, aPoint.Y()); // tdf#155364 like tdf#97731 if the reserved area changed size, give // the controls a chance to adapt to the new size bool bChanged = ReserveControlArea(nX); if (bChanged)
{
ArrangeControls(nX, aPoint.Y());
Invalidate();
}
}
void DbGridControl::DeleteSelectedRows()
{
DBG_ASSERT(GetSelection(), "no selection!!!");
{
::osl::MutexGuard aGuard(m_aAdjustSafety); if (m_nAsynAdjustEvent)
{
SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows": "AdustDataSource"));
RemoveUserEvent(m_nAsynAdjustEvent);
m_nAsynAdjustEvent = nullptr;
// force the call : this should be no problem as we're probably running in the solar thread here // (cell modified is triggered by user actions) if (m_bPendingAdjustRows)
AdjustRows(); else
AdjustDataSource();
}
}
if (IsFilterMode() || !IsValid(m_xCurrentRow) || m_xCurrentRow->IsModified()) return;
// enable edit mode // a data set should be inserted if (m_xCurrentRow->IsNew())
{
m_xCurrentRow->SetStatus(GridRowStatus::Modified);
SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED"); // if no row was added yet, do it now if (m_nCurrentPos == GetRowCount() - 1)
{ // increment RowCount
RowInserted(GetRowCount());
InvalidateStatusCell(m_nCurrentPos);
m_aBar->InvalidateAll(m_nCurrentPos);
}
} elseif (m_xCurrentRow->GetStatus() != GridRowStatus::Modified)
{
m_xCurrentRow->SetState(m_pDataCursor.get(), false);
SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
m_xCurrentRow->SetStatus(GridRowStatus::Modified);
SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
InvalidateStatusCell(m_nCurrentPos);
}
}
void DbGridControl::Dispatch(BrowserDispatchId eId)
{ if (eId == BrowserDispatchId::CURSORENDOFFILE)
{ if (m_nOptions & DbGridControlOptions::Insert)
AppendNew(); else
MoveToLast();
} else
EditBrowseBox::Dispatch(eId);
}
void DbGridControl::Undo()
{ if (IsFilterMode() || !IsValid(m_xCurrentRow) || !IsModified()) return;
// check if we have somebody doin' the UNDO for us int nState = -1; if (m_aMasterStateProvider.IsSet())
nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo); if (nState>0)
{ // yes, we have, and the slot is enabled
DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?"); bool lResult = m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Undo); if (lResult) // handled return;
} elseif (nState == 0) // yes, we have, and the slot is disabled return;
try
{ // cancel editing
Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY); // no effects if we're not updating currently if (bAppending) // just refresh the row
xUpdateCursor->moveToInsertRow(); else
xUpdateCursor->cancelRowUpdates();
if (bAppending && (EditBrowseBox::IsModified() || bDirty)) // remove the row if (m_nCurrentPos == GetRowCount() - 2)
{ // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow // caused our data source form to be reset - which should be the usual case...)
RowRemoved(GetRowCount() - 1);
m_aBar->InvalidateAll(m_nCurrentPos);
}
RowModified(m_nCurrentPos);
}
void DbGridControl::resetCurrentRow()
{ if (IsModified())
{ // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we // would never delete the obsolete "second insert row". Thus in this special case this method here // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet(); if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
{ // are we on a new row currently ? if (m_xCurrentRow->IsNew())
{ if (m_nCurrentPos == GetRowCount() - 2)
{
RowRemoved(GetRowCount() - 1);
m_aBar->InvalidateAll(m_nCurrentPos);
}
}
}
// update the rows
m_xDataRow->SetState(m_pDataCursor.get(), false); if (m_xPaintRow == m_xCurrentRow)
m_xPaintRow = m_xCurrentRow = m_xDataRow; else
m_xCurrentRow = m_xDataRow;
}
RowModified(GetCurRow()); // will update the current controller if affected
}
// accept input for this field // Where there changes at the current input field? if (!EditBrowseBox::IsModified()) returntrue;
size_t Location = GetModelColumnPos( GetCurColumnId() );
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr; bool bOK = pColumn && pColumn->Commit();
DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" ); if ( !Controller().is() ) // this might happen if the callbacks implicitly triggered by Commit // fiddled with the form or the control ... // (Note that this here is a workaround, at most. We need a general concept how // to treat this, you can imagine an arbitrary number of scenarios where a callback // triggers something which leaves us in an expected state.) // #i67147# / 2006-07-17 / frank.schoenheit@sun.com return bOK;
if (bOK)
{
Controller()->SaveValue();
if ( IsValid(m_xCurrentRow) )
{
m_xCurrentRow->SetState(m_pDataCursor.get(), false);
SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
InvalidateStatusCell( m_nCurrentPos );
} else
{
SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
}
}
return bOK;
}
bool DbGridControl::SaveRow()
{
SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow"); // valid row if (!IsValid(m_xCurrentRow) || !IsModified()) returntrue; // value of the controller was not saved, yet elseif (Controller().is() && Controller()->IsValueChangedFromSaved())
{ if (!SaveModified()) returnfalse;
}
m_bUpdating = true;
try
{ if (bSuccess)
{ // if we are appending we still sit on the insert row // we don't move just clear the flags not to move on the current row
m_xCurrentRow->SetState(m_pDataCursor.get(), false);
SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
m_xCurrentRow->SetNew(false);
// adjust the seekcursor if it is on the same position as the datacursor if (m_nSeekPos == m_nCurrentPos || bAppending)
{ // get the bookmark to refetch the data // in insert mode we take the new bookmark of the data cursor
Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
m_pSeekCursor->moveToBookmark(aBookmark); // get the data
m_xSeekRow->SetState(m_pSeekCursor.get(), true);
m_nSeekPos = m_pSeekCursor->getRow() - 1;
}
} // and repaint the row
RowModified(m_nCurrentPos);
} catch(Exception&)
{
}
m_bUpdating = false;
EndCursorAction();
// The old code returned (nRecords != 0) here. // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown, // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords // is zero, this simply means all fields had their original values. // FS - 06.12.99 - 70502 returntrue;
}
bool DbGridControl::PreNotify(NotifyEvent& rEvt)
{ // do not handle events of the Navbar if (m_aBar->IsWindowOrChild(rEvt.GetWindow())) return BrowseBox::PreNotify(rEvt);
sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode(); bool bShift = pKeyEvent->GetKeyCode().IsShift(); bool bCtrl = pKeyEvent->GetKeyCode().IsMod1(); bool bAlt = pKeyEvent->GetKeyCode().IsMod2(); if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
{ // Ctrl-Tab is used to step out of the control, without traveling to the // remaining cells first // -> build a new key event without the Ctrl-key, and let the very base class handle it
vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
// call the Control - our direct base class will interpret this in a way we do not want (and do // a cell traveling)
Control::KeyInput( aNewEvent ); returntrue;
}
bool DbGridControl::IsTabAllowed(bool bRight) const
{ if (bRight) // Tab only if not on the _last_ row return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1); else
{ // Tab only if not on the _first_ row return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
}
}
// determine the col for the focus to set to after removal
sal_uInt16 nPos = GetViewColumnPos(nId);
sal_uInt16 nNewColId = nPos == (ColCount()-1)
? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
: GetColumnIdFromViewPos(nPos+1); // take the next
tools::Long lCurrentWidth = GetColumnWidth(nId);
EditBrowseBox::RemoveColumn(nId); // don't use my own RemoveColumn, this would remove it from m_aColumns, too
// update my model
size_t Location = GetModelColumnPos( nId );
DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !"); if (pColumn)
{
pColumn->m_bHidden = true;
pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
}
// and reset the focus if ( nId == GetCurColumnId() )
GoToColumnId( nNewColId );
}
DbGridColumn* pColumn = m_aColumns[ nPos ].get(); if (!pColumn->IsHidden())
{
DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !"); // if the column isn't marked as hidden, it should be visible, shouldn't it ? return;
}
DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !"); // the opposite situation ...
// to determine the new view position we need an adjacent non-hidden column
sal_uInt16 nNextNonHidden = BROWSER_INVALIDID; // first search the cols to the right for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
{
DbGridColumn* pCurCol = m_aColumns[ i ].get(); if (!pCurCol->IsHidden())
{
nNextNonHidden = i; break;
}
} if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
{ // then to the left for ( size_t i = nPos; i > 0; --i )
{
DbGridColumn* pCurCol = m_aColumns[ i-1 ].get(); if (!pCurCol->IsHidden())
{
nNextNonHidden = i-1; break;
}
}
}
sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
? 1 // there is no visible column -> insert behind the handle col
: GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1; // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects // a position 1 for the first non-handle col -> +1
DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !"); // we found a col marked as visible but got no view pos for it ...
if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID)) // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
++nNewViewPos;
DbGridColumn* pCol = m_aColumns[ nPos ].get(); #if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL // in the debug version, we convert the ModelPos into a ViewPos and compare this with the // value we will return (nId at the corresponding Col in m_aColumns)
if (!pCol->IsHidden())
{ // makes sense only if the column is visible
sal_uInt16 nViewPos = nPos; for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i) if ( m_aColumns[ i ]->IsHidden())
--nViewPos;
DBG_ASSERT(GetColumnIdFromViewPos(nViewPos) == pCol->GetId(), "DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?");
} #endif return pCol->GetId();
}
sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
{ for ( size_t i = 0; i < m_aColumns.size(); ++i ) if ( m_aColumns[ i ]->GetId() == nId ) return i;
return GRID_COLUMN_NOT_FOUND;
}
void DbGridControl::implAdjustInSolarThread(bool _bRows)
{
SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
::osl::MutexGuard aGuard(m_aAdjustSafety); if (!Application::IsMainThread())
{
m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true);
m_bPendingAdjustRows = _bRows; if (_bRows)
SAL_INFO("svx.fmcomp", "posting an AdjustRows"); else
SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
} else
{ if (_bRows)
SAL_INFO("svx.fmcomp", "doing an AdjustRows"); else
SAL_INFO("svx.fmcomp", "doing an AdjustDataSource"); // always adjust the rows before adjusting the data source // If this is not necessary (because the row count did not change), nothing is done // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved // to a position behind row count know 'til now, the cursorMoved notification may come before the // RowCountChanged notification // 94093 - 02.11.2001 - frank.schoenheit@sun.com
AdjustRows();
for (autoconst & pCurrent : m_aColumns)
{
sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND; if (GRID_COLUMN_NOT_FOUND == nViewPos) continue;
Reference< XPropertySet > xField = pCurrent->GetField(); if (!xField.is()) continue;
// column is visible and bound here
GridFieldValueListener*& rpListener = m_aFieldListeners[pCurrent->GetId()];
DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
}
}
void DbGridControl::DisconnectFromFields()
{ if (m_aFieldListeners.empty()) return;
while (!m_aFieldListeners.empty())
{
sal_Int32 nOldSize = m_aFieldListeners.size();
m_aFieldListeners.begin()->second->dispose();
DBG_ASSERT(nOldSize > static_cast<sal_Int32>(m_aFieldListeners.size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
}
}
void DbGridControl::FieldValueChanged(sal_uInt16 _nId)
{
osl::MutexGuard aPreventDestruction(m_aDestructionSafety); // needed as this may run in a thread other than the main one if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED) // all other cases are handled elsewhere return;
std::unique_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard; while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired()))
pGuard.reset(new vcl::SolarMutexTryAndBuyGuard);
if (m_bWantDestruction)
{ // at this moment, within another thread, our destructor tries to destroy the listener which called this method // => don't do anything // 73365 - 23.02.00 - FS return;
}
// and finally do the update ...
pColumn->UpdateFromField(m_xCurrentRow.get(), m_xFormatter);
RowModified(GetCurRow());
}
void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
{ auto aPos = m_aFieldListeners.find(_nId); if (aPos == m_aFieldListeners.end())
{
OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !"); return;
}
delete aPos->second;
m_aFieldListeners.erase(aPos);
}
void DbGridControl::disposing(sal_uInt16 _nId)
{ if (_nId == 0)
{ // the seek cursor is being disposed
::osl::MutexGuard aGuard(m_aAdjustSafety);
setDataSource(nullptr, DbGridControlOptions::Readonly); // our clone was disposed so we set our datasource to null to avoid later access to it if (m_nAsynAdjustEvent)
{
RemoveUserEvent(m_nAsynAdjustEvent);
m_nAsynAdjustEvent = nullptr;
}
}
}
sal_Int32 DbGridControl::GetAccessibleControlCount() const
{ return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.