/* -*- 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 .
*/
using ::com::sun::star::sdbcx::XColumnsSupplier; using ::com::sun::star::frame::XDispatchProviderInterceptor; using ::com::sun::star::frame::XDispatchProvider; using ::com::sun::star::accessibility::XAccessible; using ::com::sun::star::accessibility::XAccessibleContext; using ::com::sun::star::sdb::XRowSetSupplier; using ::com::sun::star::awt::XVclWindowPeer;
DBG_ASSERT(/*(0 == m_nPeerCreationLevel) && */!mbCreatingPeer, "FmXGridControl::createPeer : recursion!"); // I think this should never assert, now that we're using the base class' mbCreatingPeer in addition to // our own m_nPeerCreationLevel // But I'm not sure as I don't _fully_ understand the underlying toolkit implementations... // (if this asserts, we still need m_nPeerCreationLevel. If not, we could omit it...) // 14.05.2001 - 86836 - frank.schoenheit@germany.sun.com
// TODO: why the hell this whole class does not use any mutex?
if (getPeer().is()) return;
mbCreatingPeer = true; // mbCreatingPeer is virtually the same as m_nPeerCreationLevel, but it's the base class' method // to prevent recursion.
vcl::Window* pParentWin = nullptr; if (rParentPeer.is())
{
VCLXWindow* pParent = dynamic_cast<VCLXWindow*>(rParentPeer.get()); if (pParent)
pParentWin = pParent->GetWindow();
}
// reading the properties from the model // ++m_nPeerCreationLevel;
updateFromModel();
// consider the following ugly scenario: updateFromModel leads to a propertiesChanges on the Control, // which determines, dat a "critical" property has changed (e.g. "Border") and therefore starts a new // Peer, which lands again here in createPeer we also start a second FmXGridPeer and initialise it. // Then we exit from the first incarnation's updateFromModel and continue working with the pPeer, // that is in fact now already obsolete (as another peer is being started in the second incarnation). // Therefore the effort with the PeerCreationLevel, which ensures that we really use the Peer // created at the deepest level, but first initialise it in the top-level. // if (--m_nPeerCreationLevel == 0)
{
DBG_ASSERT(getPeer().is(), "FmXGridControl::createPeer : something went wrong ... no top level peer !");
pPeer = dynamic_cast<FmXGridPeer*>(getPeer().get());
Reference< XIndexContainer > xColumns(getModel(), UNO_QUERY); if (xColumns.is())
pPeer->setColumns(xColumns);
if (maComponentInfos.bVisible)
pPeer->setVisible(true);
if (!maComponentInfos.bEnable)
pPeer->setEnable(false);
if (maWindowListeners.getLength())
pPeer->addWindowListener( &maWindowListeners );
if (maFocusListeners.getLength())
pPeer->addFocusListener( &maFocusListeners );
if (maKeyListeners.getLength())
pPeer->addKeyListener( &maKeyListeners );
if (maMouseListeners.getLength())
pPeer->addMouseListener( &maMouseListeners );
if (maMouseMotionListeners.getLength())
pPeer->addMouseMotionListener( &maMouseMotionListeners );
if (maPaintListeners.getLength())
pPeer->addPaintListener( &maPaintListeners );
if (m_aModifyListeners.getLength())
pPeer->addModifyListener( &m_aModifyListeners );
if (m_aUpdateListeners.getLength())
pPeer->addUpdateListener( &m_aUpdateListeners );
if (m_aContainerListeners.getLength())
pPeer->addContainerListener( &m_aContainerListeners );
// forward the design mode bool bForceAlivePeer = m_bInDraw && !maComponentInfos.bVisible; // (we force an alive-mode peer if we're in "draw", cause in this case the peer will be used for drawing in // foreign devices. We ensure this with the visibility check as a living peer is assumed to be noncritical // only if invisible)
Any aOldCursorBookmark; if (!mbDesignMode || bForceAlivePeer)
{
Reference< XFormComponent > xComp(getModel(), UNO_QUERY); if (xComp.is())
{
Reference< XRowSet > xForm(xComp->getParent(), UNO_QUERY); // is the form alive? // we can see that if the form contains columns
Reference< css::sdbcx::XColumnsSupplier > xColumnsSupplier(xForm, UNO_QUERY); if (xColumnsSupplier.is())
{ if (Reference< XIndexAccess > (xColumnsSupplier->getColumns(),UNO_QUERY_THROW)->getCount())
{ // we get only a new bookmark if the resultset is not forwardonly if (::comphelper::getINT32(Reference< XPropertySet > (xForm, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_RESULTSET_TYPE)) != ResultSetType::FORWARD_ONLY)
{ // as the FmGridControl touches the data source it is connected to we have to remember the current // cursor position (and restore afterwards) // OJ: but only when we stand on a valid row if ( !xForm->isBeforeFirst() && !xForm->isAfterLast() )
{ try
{
aOldCursorBookmark = Reference< css::sdbcx::XRowLocate > (xForm, UNO_QUERY_THROW)->getBookmark();
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
}
}
}
}
pPeer->setRowSet(xForm);
}
}
pPeer->setDesignMode(mbDesignMode && !bForceAlivePeer);
try
{ if (aOldCursorBookmark.hasValue())
{ // we have a valid bookmark, so we have to restore the cursor's position
Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
Reference< css::sdbcx::XRowLocate > xLocate(xComp->getParent(), UNO_QUERY);
xLocate->moveToBookmark(aOldCursorBookmark);
}
} catch( const Exception& )
{
DBG_UNHANDLED_EXCEPTION("svx");
}
// dispose our current AccessibleContext, if we have one // (changing the design mode implies having a new implementation for this context, // so the old one must be declared DEFUNC)
DisposeAccessibleContext(
Reference<XComponent>(maAccessibleContext, UNO_QUERY));
maAccessibleContext.clear();
void SAL_CALL FmXGridControl::setFocus()
{
rtl::Reference<FmXGridPeer> pPeer = dynamic_cast<FmXGridPeer*>(getPeer().get()); if (pPeer)
{
VclPtr<FmGridControl> xGrid = pPeer->GetAs<FmGridControl>(); bool bAlreadyHasFocus = xGrid->HasChildPathFocus() || xGrid->ControlHasFocus(); // if the focus is already in the control don't grab focus again which // would grab focus away from any native widgets hosted in the control if (bAlreadyHasFocus) return;
}
UnoControl::setFocus();
}
// helper class which prevents that in the peer's header the FmGridListener must be known class FmXGridPeer::GridListenerDelegator : public FmGridListener
{ protected:
FmXGridPeer* m_pPeer;
FmXGridPeer::FmXGridPeer(const Reference< XComponentContext >& _rxContext)
:m_xContext(_rxContext)
,m_aMode(u"DataMode"_ustr)
,m_nCursorListening(0)
,m_bInterceptingDispatch(false)
{ // Create must be called after this constructor
m_pGridListener.reset( new GridListenerDelegator( this ) );
}
void FmXGridPeer::propertyChange(const PropertyChangeEvent& evt)
{
SolarMutexGuard aGuard; // want to do a lot of VCL stuff here ... // this should not be (deadlock) critical, as by definition, every component should release // any own mutexes before notifying
if (!bCancel)
m_aUpdateListeners.notifyEach( g, &XUpdateListener::updated, aEvt ); return !bCancel;
}
void FmXGridPeer::cursorMoved(const EventObject& _rEvent)
{
VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >(); // we are not interested in moving to insert row only in the reset event // which is fired after positioning and the insert row if (pGrid && pGrid->IsOpen() && !::comphelper::getBOOL(Reference< XPropertySet > (_rEvent.Source, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_ISNEW)))
pGrid->positioned();
}
void FmXGridPeer::rowSetChanged(const EventObject& /*event*/)
{ // not interested in ... // (our parent is a form which means we get a loaded or reloaded after this rowSetChanged)
}
// as not all properties have to be supported by all columns we have to check this // before adding a listener
Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo(); for (size_t i=0; i<SAL_N_ELEMENTS(aPropsListenedTo); ++i)
{ if ( xInfo->hasPropertyByName( aPropsListenedTo[i] ) )
{
Property aPropDesc = xInfo->getPropertyByName( aPropsListenedTo[i] ); if ( 0 != ( aPropDesc.Attributes & PropertyAttribute::BOUND ) )
xCol->addPropertyChangeListener( aPropsListenedTo[i], this );
}
}
}
void FmXGridPeer::removeColumnListeners(const Reference< XPropertySet >& xCol)
{ // the same props as in addColumnListeners... linux has problems with global static UStrings, so // we have to do it this way... static constexpr OUString aPropsListenedTo[] =
{
FM_PROP_LABEL, FM_PROP_WIDTH, FM_PROP_HIDDEN, FM_PROP_ALIGN,
FM_PROP_FORMATKEY
};
Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo(); for (constauto & i : aPropsListenedTo) if (xInfo->hasPropertyByName(i))
xCol->removePropertyChangeListener(i, this);
}
// set the model of the new column
DbGridColumn* pCol = pGrid->GetColumns()[ nNewPos ].get();
// for initializing this grid column, we need the fields of the grid's data source
Reference< XColumnsSupplier > xSuppColumns;
CursorWrapper* pGridDataSource = pGrid->getDataSource(); if ( pGridDataSource )
xSuppColumns.set(Reference< XInterface >( *pGridDataSource ), css::uno::UNO_QUERY);
Reference< XNameAccess > xColumnsByName; if ( xSuppColumns.is() )
xColumnsByName = xSuppColumns->getColumns();
Reference< XIndexAccess > xColumnsByIndex( xColumnsByName, UNO_QUERY );
if ( xColumnsByIndex.is() )
FmGridControl::InitColumnByField( pCol, xNewColumn, xColumnsByName, xColumnsByIndex ); else // the simple version, applies when the grid is not yet connected to a data source
pCol->setModel(xNewColumn);
if ( PropertyName == FM_PROP_TEXTLINECOLOR )
{
::Color aTextLineColor( bVoid ? COL_TRANSPARENT : ::Color(ColorTransparency, ::comphelper::getINT32( Value )) ); if (bVoid)
{
pGrid->SetTextLineColor();
pGrid->GetDataWindow().SetTextLineColor();
} else
{
pGrid->SetTextLineColor(aTextLineColor);
pGrid->GetDataWindow().SetTextLineColor(aTextLineColor);
}
// need to forward this to the columns
std::vector< std::unique_ptr<DbGridColumn> > const & rColumns = pGrid->GetColumns(); for (autoconst & pLoop : rColumns)
{
FmXGridCell* pXCell = pLoop->GetCell(); if (pXCell)
{ if (bVoid)
pXCell->SetTextLineColor(); else
pXCell->SetTextLineColor(aTextLineColor);
}
}
// need to add relief and emphasis (they're stored in a VCL-Font, but not in a FontDescriptor
vcl::Font aOldVclFont = pGrid->GetControlFont();
aNewVclFont.SetRelief( aOldVclFont.GetRelief() );
aNewVclFont.SetEmphasisMark( aOldVclFont.GetEmphasisMark() );
// now set it ...
pGrid->SetControlFont( aNewVclFont );
// if our row-height property is void (which means "calculate it font-dependent") we have // to adjust the control's row height
Reference< XPropertySet > xModelSet(getColumns(), UNO_QUERY); if (xModelSet.is() && ::comphelper::hasProperty(FM_PROP_ROWHEIGHT, xModelSet))
{
Any aHeight = xModelSet->getPropertyValue(FM_PROP_ROWHEIGHT); if (!aHeight.hasValue())
pGrid->SetDataRowHeight(0);
}
// In design mode, disable only the data window. // Else the control cannot be configured anymore. if (isDesignMode())
pGrid->GetDataWindow().Enable( bValue ); else
pGrid->Enable( bValue );
} else
VCLXWindow::setProperty( PropertyName, Value );
}
Any FmXGridPeer::getProperty( const OUString& _rPropertyName )
{
Any aProp; if (GetWindow())
{
VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
vcl::Window* pDataWindow = &pGrid->GetDataWindow();
void FmXGridPeer::dispose()
{
{
std::unique_lock g(m_aMutex);
EventObject aEvt;
aEvt.Source = getXWeak();
m_aModifyListeners.disposeAndClear(g, aEvt);
m_aUpdateListeners.disposeAndClear(g, aEvt);
m_aContainerListeners.disposeAndClear(g, aEvt);
} // release all interceptors
Reference< XDispatchProviderInterceptor > xInterceptor( m_xFirstDispatchInterceptor );
m_xFirstDispatchInterceptor.clear(); while ( xInterceptor.is() )
{ // tell the interceptor it has a new (means no) predecessor
xInterceptor->setMasterDispatchProvider( nullptr );
// ask for its successor
Reference< XDispatchProvider > xSlave = xInterceptor->getSlaveDispatchProvider(); // and give it the new (means no) successoert
xInterceptor->setSlaveDispatchProvider( nullptr );
// start over with the next chain element
xInterceptor.set(xSlave, css::uno::UNO_QUERY);
}
DisConnectFromDispatcher();
// unregister all listeners if (m_xCursor.is())
{
m_xCursor->removeRowSetListener(this);
void FmXGridPeer::setRowSet(const Reference< XRowSet >& _rDatabaseCursor)
{
VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >(); if (!pGrid || !m_xColumns.is() || !m_xColumns->getCount()) return; // unregister all listeners if (m_xCursor.is())
{
Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY); // only if the form is loaded we set the rowset if (xLoadable.is())
{
stopCursorListening();
xLoadable->removeLoadListener(this);
}
}
m_xCursor = _rDatabaseCursor;
if (!pGrid) return;
Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY); // only if the form is loaded we set the rowset if (xLoadable.is() && xLoadable->isLoaded())
pGrid->setDataSource(m_xCursor); else
pGrid->setDataSource(Reference< XRowSet > ());
if (xLoadable.is())
{
startCursorListening();
xLoadable->addLoadListener(this);
}
}
for (; i < nColCount; ++i)
{
m_xColumns->getByIndex(i) >>= xCol; if ( xCol == xSelection )
{
pGrid->markColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i))); break;
}
} // The columns have to be 1-based for the VCL control. // If necessary, pass on the selection to the VCL control if ( i != pGrid->GetSelectedColumn() )
{ // (if this does not take effect, the selectionChanged was implicitly triggered by the control itself) if ( i < nColCount )
{
pGrid->SelectColumnPos(pGrid->GetViewColumnPos(pGrid->GetColumnIdFromModelPos( static_cast<sal_uInt16>(i) )) + 1); // SelectColumnPos has led to an implicit ActivateCell again if (pGrid->IsEditing())
pGrid->DeactivateCell();
} else
pGrid->SetNoSelection();
}
} else
pGrid->markColumn(USHRT_MAX);
}
Any aElement; // get the columnid
sal_uInt16 nId = pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(_nIndex)); // get the list position
sal_uInt16 nPos = pGrid->GetModelColumnPos(nId);
if ( nPos == GRID_COLUMN_NOT_FOUND ) return aElement;
// first ask our interceptor chain if (m_xFirstDispatchInterceptor.is() && !m_bInterceptingDispatch)
{
m_bInterceptingDispatch = true; // safety against recursion : as we are master of the first chain element and slave of the last one we would // have an infinite loop without this if no dispatcher can fulfill the request
xResult = m_xFirstDispatchInterceptor->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
m_bInterceptingDispatch = false;
}
// then ask ourself : we don't have any dispatches return xResult;
}
// then ask ourself : we don't have any dispatches return Sequence< Reference< css::frame::XDispatch > >();
}
void FmXGridPeer::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
{ if (!_xInterceptor.is()) return;
if (m_xFirstDispatchInterceptor.is())
{ // there is already an interceptor; the new one will become its master
_xInterceptor->setSlaveDispatchProvider(m_xFirstDispatchInterceptor);
m_xFirstDispatchInterceptor->setMasterDispatchProvider(m_xFirstDispatchInterceptor);
} else
{ // it is the first interceptor; set ourself as slave
_xInterceptor->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
}
// we are the master of the chain's first interceptor
m_xFirstDispatchInterceptor = _xInterceptor;
m_xFirstDispatchInterceptor->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
// we have a new interceptor and we're alive ? if (!isDesignMode()) // -> check for new dispatchers
UpdateDispatches();
}
void FmXGridPeer::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
{ if (!_xInterceptor.is()) return;
if (m_xFirstDispatchInterceptor == _xInterceptor)
{ // our chain will have a new first element
m_xFirstDispatchInterceptor.set(m_xFirstDispatchInterceptor->getSlaveDispatchProvider(), UNO_QUERY);
} // do this before removing the interceptor from the chain as we won't know it's slave afterwards)
while (xChainWalk.is())
{ // walk along the chain of interceptors and look for the interceptor that has to be removed
Reference< css::frame::XDispatchProviderInterceptor > xSlave(xChainWalk->getSlaveDispatchProvider(), UNO_QUERY);
if (xChainWalk == _xInterceptor)
{ // old master may be an interceptor too
Reference< css::frame::XDispatchProviderInterceptor > xMaster(xChainWalk->getMasterDispatchProvider(), UNO_QUERY);
// unchain the interceptor that has to be removed
xChainWalk->setSlaveDispatchProvider(Reference< css::frame::XDispatchProvider > ());
xChainWalk->setMasterDispatchProvider(Reference< css::frame::XDispatchProvider > ());
// reconnect the chain if (xMaster.is())
{ if (xSlave.is())
xMaster->setSlaveDispatchProvider(xSlave); else // it's the first interceptor of the chain, set ourself as slave
xMaster->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
} else
{ // the chain's first element was removed, set ourself as new master of the second one if (xSlave.is())
xSlave->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
}
}
xChainWalk = std::move(xSlave);
} // our interceptor chain has changed and we're alive ? if (!isDesignMode()) // -> check the dispatchers
UpdateDispatches();
}
// TODO: // speaking strictly, we would have to adjust our model, as our ColumnSelection may have changed. // Our model is a XSelectionSupplier, too, it handles the selection of single columns. // This is somewhat strange, as selection should be a view (not a model) aspect. // So for a clean solution, we should handle column selection ourself, and the model shouldn't // deal with selection at all.
}
void FmXGridPeer::resetted(const EventObject& rEvent)
{ if (m_xColumns == rEvent.Source)
{ // my model was reset -> refresh the grid content
SolarMutexGuard aGuard;
VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >(); if (!pGrid) return;
pGrid->resetCurrentRow();
} // if the cursor fired a reset event we seem to be on the insert row elseif (m_xCursor == rEvent.Source)
{
SolarMutexGuard aGuard;
VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >(); if (pGrid && pGrid->IsOpen())
pGrid->positioned();
}
}
for ( sal_Int32 i = 0; i < tmp.getLength(); ++i, ++pSupported)
pSupported->Complete = sSupported[i];
// let a css::util::URL-transformer normalize the URLs
Reference< css::util::XURLTransformer > xTransformer(
util::URLTransformer::create(::comphelper::getProcessComponentContext()) ); for (css::util::URL & rURL : asNonConstRange(tmp))
xTransformer->parseStrict(rURL); return tmp;
}();
return aSupported;
}
void FmXGridPeer::UpdateDispatches()
{ if (!m_pStateCache)
{ // we don't have any dispatchers yet -> do the initial connect
ConnectToDispatcher(); return;
}
// _before_ adding the status listeners (as the add should result in a statusChanged-call) !
m_pStateCache.reset(newbool[aSupportedURLs.getLength()]);
m_pDispatchers.reset(new Reference< css::frame::XDispatch > [aSupportedURLs.getLength()]);
// search the given slot with our supported sequence const std::vector<DbGridControlNavigationBarState>& aSupported = getSupportedGridSlots(); for (size_t i=0; i<aSupported.size(); ++i)
{ if (aSupported[i] == nSlot)
{ if (!m_pDispatchers[i].is()) return -1; // nothing known about this slot else return m_pStateCache[i] ? 1 : 0;
}
}
return -1;
}
IMPL_LINK(FmXGridPeer, OnExecuteGridSlot, DbGridControlNavigationBarState, nSlot, bool)
{ if (!m_pDispatchers) returnfalse; // not handled
DBG_ASSERT(aSlots.size() == o3tl::make_unsigned(aUrls.getLength()), "FmXGridPeer::OnExecuteGridSlot : inconsistent data returned by getSupportedURLs/getSupportedGridSlots!");
for (size_t i=0; i<aSlots.size(); ++i, ++pUrls)
{ if (aSlots[i] == nSlot)
{ if (m_pDispatchers[i].is())
{ // commit any changes done so far, if it's not the undoRecord URL if ( pUrls->Complete == FMURL_RECORD_UNDO || commit() )
m_pDispatchers[i]->dispatch(*pUrls, Sequence< PropertyValue>());
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.