diff options
Diffstat (limited to 'svx/source/fmcomp/fmgridcl.cxx')
-rw-r--r-- | svx/source/fmcomp/fmgridcl.cxx | 2144 |
1 files changed, 2144 insertions, 0 deletions
diff --git a/svx/source/fmcomp/fmgridcl.cxx b/svx/source/fmcomp/fmgridcl.cxx new file mode 100644 index 000000000000..d64b0c9ab958 --- /dev/null +++ b/svx/source/fmcomp/fmgridcl.cxx @@ -0,0 +1,2144 @@ +/************************************************************************* + * + * 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" + +#include "dbexch.hrc" +#include "fmgridif.hxx" +#include "fmitems.hxx" +#include "fmprop.hrc" +#include "svx/fmtools.hxx" +#include "fmresids.hrc" +#include "fmservs.hxx" +#include "fmurl.hxx" +#include "formcontrolfactory.hxx" +#include "gridcell.hxx" +#include "gridcols.hxx" +#include "svx/dbaexchange.hxx" +#include "svx/dialmgr.hxx" +#include "svx/dialogs.hrc" +#include "svx/fmgridcl.hxx" +#include "svx/svxdlg.hxx" +#include "svx/svxids.hrc" +#include "trace.hxx" + +#include <com/sun/star/form/XConfirmDeleteListener.hpp> +#include <com/sun/star/form/XFormComponent.hpp> +#include <com/sun/star/form/XGridColumnFactory.hpp> +#include <com/sun/star/io/XPersistObject.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/RowChangeAction.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XPreparedStatement.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XDeleteRows.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/uno/XNamingService.hpp> +#include <com/sun/star/util/XNumberFormats.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> + +#ifndef _SVSTDARR_STRINGSDTOR +#define _SVSTDARR_STRINGSDTOR +#define _SVSTDARR_ULONGS +#include <svl/svstdarr.hxx> +#endif + +#include <comphelper/extract.hxx> +#include <comphelper/numbers.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/property.hxx> +#include <connectivity/dbtools.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/eitem.hxx> +#include <svtools/fmtfield.hxx> +#include <svl/numuno.hxx> +#include <tools/multisel.hxx> +#include <tools/shl.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/help.hxx> +#include <vcl/image.hxx> +#include <vcl/longcurr.hxx> +#include <vcl/menu.hxx> + +#include <math.h> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::cppu; +using namespace ::svxform; +using namespace ::svx; + +//============================================================================== +//------------------------------------------------------------------------------ +::rtl::OUString FieldServiceFromId(sal_Int32 nID) +{ + switch (nID) + { + case SID_FM_EDIT : return FM_COL_TEXTFIELD; + case SID_FM_COMBOBOX : return FM_COL_COMBOBOX; + case SID_FM_LISTBOX : return FM_COL_LISTBOX; + case SID_FM_CHECKBOX : return FM_COL_CHECKBOX; + case SID_FM_DATEFIELD : return FM_COL_DATEFIELD; + case SID_FM_TIMEFIELD : return FM_COL_TIMEFIELD; + case SID_FM_NUMERICFIELD : return FM_COL_NUMERICFIELD; + case SID_FM_CURRENCYFIELD : return FM_COL_CURRENCYFIELD; + case SID_FM_PATTERNFIELD : return FM_COL_PATTERNFIELD; + case SID_FM_FORMATTEDFIELD : return FM_COL_FORMATTEDFIELD; + } + return ::rtl::OUString(); +} + +//============================================================================== +struct FmGridHeaderData +{ + ODataAccessDescriptor aDropData; + Point aDropPosPixel; + sal_Int8 nDropAction; + Reference< XInterface > xDroppedStatement; + Reference< XInterface > xDroppedResultSet; +}; + +//============================================================================== +//------------------------------------------------------------------------------ +const sal_Int16 nChangeTypeOffset = 1000; +void SetMenuItem(const ImageList& rList, sal_uInt16 nID, Menu* pMenu, Menu& rNewMenu, sal_Bool bDesignMode = sal_True, sal_Int16 nOffset = nChangeTypeOffset) +{ + pMenu->SetItemImage(nID, rList.GetImage(nID)); + pMenu->EnableItem(nID, bDesignMode); + rNewMenu.InsertItem(nID + nOffset, pMenu->GetItemText(nID)); + rNewMenu.SetItemImage(nID + nOffset, rList.GetImage(nID)); + rNewMenu.SetHelpId(nID + nOffset, pMenu->GetHelpId(nID)); + rNewMenu.EnableItem(nID + nOffset, bDesignMode); +} + +//------------------------------------------------------------------------------ +FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits) + :EditBrowserHeader(pParent, nWinBits) + ,DropTargetHelper(this) + ,m_pImpl(new FmGridHeaderData) +{ +} + +//------------------------------------------------------------------------------ +FmGridHeader::~FmGridHeader() +{ + delete m_pImpl; +} + +//------------------------------------------------------------------------------ +sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const +{ + return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId); +} +//--------------------------------------------------------------------------------------- +void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId) +{ + sal_uInt16 nPos = GetModelColumnPos(nColumnId); + Reference< XIndexAccess > xColumns(((FmGridControl*)GetParent())->GetPeer()->getColumns(), UNO_QUERY); + if ( nPos < xColumns->getCount() ) + { + Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY); + if ( xSelSupplier.is() ) + { + Reference< XPropertySet > xColumn; + xColumns->getByIndex(nPos) >>= xColumn; + xSelSupplier->select(makeAny(xColumn)); + } + } +} +//------------------------------------------------------------------------------ +void FmGridHeader::Select() +{ + EditBrowserHeader::Select(); + notifyColumnSelect(GetCurItemId()); +} + +//------------------------------------------------------------------------------ +void FmGridHeader::RequestHelp( const HelpEvent& rHEvt ) +{ + sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) ); + if ( nItemId ) + { + if ( rHEvt.GetMode() & (HELPMODE_QUICK | HELPMODE_BALLOON) ) + { + Rectangle aItemRect = GetItemRect( nItemId ); + Point aPt = OutputToScreenPixel( aItemRect.TopLeft() ); + aItemRect.Left() = aPt.X(); + aItemRect.Top() = aPt.Y(); + aPt = OutputToScreenPixel( aItemRect.BottomRight() ); + aItemRect.Right() = aPt.X(); + aItemRect.Bottom() = aPt.Y(); + + sal_uInt16 nPos = GetModelColumnPos(nItemId); + Reference< ::com::sun::star::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns()); + try + { + Reference< ::com::sun::star::beans::XPropertySet > xColumn(xColumns->getByIndex(nPos),UNO_QUERY); + ::rtl::OUString aHelpText; + xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText; + if ( !aHelpText.getLength() ) + xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText; + if ( aHelpText.getLength() ) + { + if ( rHEvt.GetMode() & HELPMODE_BALLOON ) + Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText ); + else + Help::ShowQuickHelp( this, aItemRect, aHelpText ); + return; + } + } + catch(Exception&) + { + return; + } + } + } + EditBrowserHeader::RequestHelp( rHEvt ); +} + +//------------------------------------------------------------------------------ +sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + // drop allowed in design mode only + if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode()) + return DND_ACTION_NONE; + + // search for recognized formats + const DataFlavorExVector& rFlavors = GetDataFlavorExVector(); + if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, CTF_COLUMN_DESCRIPTOR | CTF_FIELD_DESCRIPTOR)) + return rEvt.mnAction; + + return DND_ACTION_NONE; +} + +//------------------------------------------------------------------------------ +sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt ) +{ + if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode()) + return DND_ACTION_NONE; + + TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable); + + // check the formats + sal_Bool bColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), CTF_COLUMN_DESCRIPTOR); + sal_Bool bFieldDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), CTF_FIELD_DESCRIPTOR); + if (!bColumnDescriptor && !bFieldDescriptor) + { + DBG_ERROR("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!"); + return DND_ACTION_NONE; + } + + // extract the descriptor + ::rtl::OUString sDatasouce, sCommand, sFieldName,sDatabaseLocation,sConnnectionResource; + sal_Int32 nCommandType = CommandType::COMMAND; + Reference< XPreparedStatement > xStatement; + Reference< XResultSet > xResultSet; + Reference< XPropertySet > xField; + Reference< XConnection > xConnection; + + ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData); + if (aColumn.has(daDataSource)) aColumn[daDataSource] >>= sDatasouce; + if (aColumn.has(daDatabaseLocation)) aColumn[daDatabaseLocation] >>= sDatabaseLocation; + if (aColumn.has(daConnectionResource)) aColumn[daConnectionResource] >>= sConnnectionResource; + if (aColumn.has(daCommand)) aColumn[daCommand] >>= sCommand; + if (aColumn.has(daCommandType)) aColumn[daCommandType] >>= nCommandType; + if (aColumn.has(daColumnName)) aColumn[daColumnName] >>= sFieldName; + if (aColumn.has(daColumnObject))aColumn[daColumnObject] >>= xField; + if (aColumn.has(daConnection)) aColumn[daConnection] >>= xConnection; + + if ( !sFieldName.getLength() + || !sCommand.getLength() + || ( !sDatasouce.getLength() + && !sDatabaseLocation.getLength() + && !xConnection.is() + ) + ) + { + DBG_ERROR( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" ); + return DND_ACTION_NONE; + } + + try + { + // need a connection + if (!xConnection.is()) + { // the transferable did not contain the connection -> build an own one + try + { + ::rtl::OUString sSignificantSource( sDatasouce.getLength() ? sDatasouce : sDatabaseLocation ); + xConnection = OStaticDataAccessTools().getConnection_withFeedback(sSignificantSource, ::rtl::OUString(),::rtl::OUString(),static_cast<FmGridControl*>(GetParent())->getServiceManager()); + } + catch(NoSuchElementException&) + { // allowed, means sDatasouce isn't a valid data source name .... + } + catch(Exception&) + { + DBG_ERROR("FmGridHeader::ExecuteDrop: could not retrieve the database access object !"); + } + + if (!xConnection.is()) + { + DBG_ERROR("FmGridHeader::ExecuteDrop: could not retrieve the database access object !"); + return DND_ACTION_NONE; + } + } + + // try to obtain the column object + if (!xField.is()) + { +#ifdef DBG_UTIL + Reference< XServiceInfo > xServiceInfo(xConnection, UNO_QUERY); + DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)"); +#endif + + Reference< XNameAccess > xFields; + switch (nCommandType) + { + case CommandType::TABLE: + { + Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY); + Reference< XColumnsSupplier > xSupplyColumns; + xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns; + xFields = xSupplyColumns->getColumns(); + } + break; + case CommandType::QUERY: + { + Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY); + Reference< XColumnsSupplier > xSupplyColumns; + xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns; + xFields = xSupplyColumns->getColumns(); + } + break; + default: + { + xStatement = xConnection->prepareStatement(sCommand); + // not interested in any results + + Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY); + xStatProps->setPropertyValue(rtl::OUString::createFromAscii("MaxRows"), makeAny(sal_Int32(0))); + + xResultSet = xStatement->executeQuery(); + Reference< XColumnsSupplier > xSupplyCols(xResultSet, UNO_QUERY); + if (xSupplyCols.is()) + xFields = xSupplyCols->getColumns(); + } + } + + if (xFields.is() && xFields->hasByName(sFieldName)) + xFields->getByName(sFieldName) >>= xField; + + if (!xField.is()) + { + ::comphelper::disposeComponent(xStatement); + return DND_ACTION_NONE; + } + } + + // do the drop asynchronously + // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu) + m_pImpl->aDropData = aColumn; + m_pImpl->aDropData[daConnection] <<= xConnection; + m_pImpl->aDropData[daColumnObject] <<= xField; + + m_pImpl->nDropAction = _rEvt.mnAction; + m_pImpl->aDropPosPixel = _rEvt.maPosPixel; + m_pImpl->xDroppedStatement = xStatement; + m_pImpl->xDroppedResultSet = xResultSet; + + PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop)); + } + catch (Exception&) + { + DBG_ERROR("FmGridHeader::ExecuteDrop: caught an exception while creatin' the column !"); + ::comphelper::disposeComponent(xStatement); + return sal_False; + } + + return DND_ACTION_LINK; +} + +//------------------------------------------------------------------------------ +IMPL_LINK( FmGridHeader, OnAsyncExecuteDrop, void*, /*NOTINTERESTEDIN*/ ) +{ + ::rtl::OUString sCommand, sFieldName,sURL; + sal_Int32 nCommandType = CommandType::COMMAND; + Reference< XPropertySet > xField; + Reference< XConnection > xConnection; + + ::rtl::OUString sDatasouce = m_pImpl->aDropData.getDataSource(); + if ( !sDatasouce.getLength() && m_pImpl->aDropData.has(daConnectionResource) ) + m_pImpl->aDropData[daConnectionResource] >>= sURL; + m_pImpl->aDropData[daCommand] >>= sCommand; + m_pImpl->aDropData[daCommandType] >>= nCommandType; + m_pImpl->aDropData[daColumnName] >>= sFieldName; + m_pImpl->aDropData[daConnection] >>= xConnection; + m_pImpl->aDropData[daColumnObject] >>= xField; + + try + { + // need number formats + Reference< XNumberFormatsSupplier > xSupplier = OStaticDataAccessTools().getNumberFormats(xConnection, sal_True); + Reference< XNumberFormats > xNumberFormats; + if (xSupplier.is()) + xNumberFormats = xSupplier->getNumberFormats(); + if (!xNumberFormats.is()) + { + ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet); + ::comphelper::disposeComponent(m_pImpl->xDroppedStatement); + return 0L; + } + + // Vom Feld werden nun zwei Informationen benoetigt: + // a.) Name des Feldes fuer Label und ControlSource + // b.) FormatKey, um festzustellen, welches Feld erzeugt werden soll + sal_Int32 nDataType = 0; + xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType; + // diese Datentypen koennen im Gridcontrol nicht verarbeitet werden + switch (nDataType) + { + case DataType::BLOB: + case DataType::LONGVARBINARY: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::OTHER: + ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet); + ::comphelper::disposeComponent(m_pImpl->xDroppedStatement); + return 0L; + } + + // Erstellen der Column + Reference< XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns()); + Reference< XGridColumnFactory > xFactory(xCols, UNO_QUERY); + + Point aPos = OutputToScreenPixel(m_pImpl->aDropPosPixel); + sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel); + // EinfuegePosition, immer vor der aktuellen Spalte + sal_uInt16 nPos = GetModelColumnPos(nColId); + Reference< XPropertySet > xCol, xSecondCol; + + // erzeugen der Column in abhaengigkeit vom type, default textfeld + SvULongs aPossibleTypes; + switch (nDataType) + { + case DataType::BIT: + case DataType::BOOLEAN: + aPossibleTypes.Insert(SID_FM_CHECKBOX, aPossibleTypes.Count()); + break; + case DataType::TINYINT: + case DataType::SMALLINT: + case DataType::INTEGER: + aPossibleTypes.Insert(SID_FM_NUMERICFIELD, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_FORMATTEDFIELD, aPossibleTypes.Count()); + break; + case DataType::REAL: + case DataType::DOUBLE: + case DataType::NUMERIC: + case DataType::DECIMAL: + aPossibleTypes.Insert(SID_FM_FORMATTEDFIELD, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_NUMERICFIELD, aPossibleTypes.Count()); + break; + case DataType::TIMESTAMP: + aPossibleTypes.Insert(SID_FM_TWOFIELDS_DATE_N_TIME, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_DATEFIELD, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_TIMEFIELD, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_FORMATTEDFIELD, aPossibleTypes.Count()); + break; + case DataType::DATE: + aPossibleTypes.Insert(SID_FM_DATEFIELD, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_FORMATTEDFIELD, aPossibleTypes.Count()); + break; + case DataType::TIME: + aPossibleTypes.Insert(SID_FM_TIMEFIELD, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_FORMATTEDFIELD, aPossibleTypes.Count()); + break; + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + default: + aPossibleTypes.Insert(SID_FM_EDIT, aPossibleTypes.Count()); + aPossibleTypes.Insert(SID_FM_FORMATTEDFIELD, aPossibleTypes.Count()); + break; + } + // if it's a currency field, a a "currency field" option + try + { + if ( ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField) + && ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY))) + aPossibleTypes.Insert(SID_FM_CURRENCYFIELD, 0); + } + catch(Exception&) + { + DBG_ERROR("FmGridHeader::ExecuteDrop: Exception occured!"); + } + + sal_Int32 nPreferedType = -1; + sal_Bool bDateNTimeCol = sal_False; + if (aPossibleTypes.Count() != 0) + { + nPreferedType = aPossibleTypes[0]; + if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.Count() > 1)) + { + ImageList aImageList( SVX_RES(RID_SVXIMGLIST_FMEXPL) ); + + PopupMenu aInsertMenu(SVX_RES(RID_SVXMNU_COLS)); + PopupMenu aTypeMenu; + PopupMenu* pMenu = aInsertMenu.GetPopupMenu(SID_FM_INSERTCOL); + for (sal_uInt32 i=0; i<aPossibleTypes.Count(); ++i) + SetMenuItem(aImageList, sal_uInt16(aPossibleTypes[(sal_uInt16)i]), pMenu, aTypeMenu, sal_True, 0); + nPreferedType = aTypeMenu.Execute(this, m_pImpl->aDropPosPixel); + } + + bDateNTimeCol = nPreferedType == SID_FM_TWOFIELDS_DATE_N_TIME; + sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1; + ::rtl::OUString sFieldService; + while (nColCount--) + { + if (bDateNTimeCol) + nPreferedType = nColCount ? SID_FM_DATEFIELD : SID_FM_TIMEFIELD; + + sFieldService = FieldServiceFromId(nPreferedType); + Reference< XPropertySet > xThisRoundCol; + if ( sFieldService.getLength() ) + xThisRoundCol = xFactory->createColumn(sFieldService); + if (nColCount) + xSecondCol = xThisRoundCol; + else + xCol = xThisRoundCol; + } + } + + if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is())) + { + ::comphelper::disposeComponent(xCol); // in case only the creation of the second column failed + ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet); + ::comphelper::disposeComponent(m_pImpl->xDroppedStatement); + return 0L; + } + + if (bDateNTimeCol) + { + String sTimePostfix( SVX_RES( RID_STR_POSTFIX_TIME ) ); + xCol->setPropertyValue(FM_PROP_LABEL, makeAny( ::rtl::OUString( sFieldName + sTimePostfix ) ) ); + + String sDatePostfix( SVX_RES( RID_STR_POSTFIX_DATE ) ); + xSecondCol->setPropertyValue(FM_PROP_LABEL, makeAny( ::rtl::OUString( sFieldName + sDatePostfix ) ) ); + } + else + xCol->setPropertyValue(FM_PROP_LABEL, makeAny(sFieldName)); + + FormControlFactory aControlFactory( ::comphelper::getProcessServiceFactory() ); + aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol ); + aControlFactory.initializeFieldDependentProperties( xField, xCol, xNumberFormats ); + + xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName)); + if ( xSecondCol.is() ) + xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName)); + + if (bDateNTimeCol) + { + String sRealName,sPurePostfix; + + String aPostfix[] = { + String( SVX_RES( RID_STR_POSTFIX_DATE ) ), + String( SVX_RES( RID_STR_POSTFIX_TIME ) ) + }; + + for ( size_t i=0; i<2; ++i ) + { + sPurePostfix = aPostfix[i]; + sPurePostfix.EraseLeadingChars(' '); + sPurePostfix.EraseLeadingChars('('); + sPurePostfix.EraseTrailingChars(')'); + sRealName = sFieldName; + sRealName += '_'; + sRealName += sPurePostfix; + if (i) + xSecondCol->setPropertyValue(FM_PROP_NAME, makeAny(::rtl::OUString(sRealName))); + else + xCol->setPropertyValue(FM_PROP_NAME, makeAny(::rtl::OUString(sRealName))); + } + } + else + xCol->setPropertyValue(FM_PROP_NAME, makeAny(sFieldName)); + + // jetzt einfuegen + Any aElement; + aElement <<= xCol; + xCols->insertByIndex(nPos, aElement); + + if (bDateNTimeCol) + { + aElement <<= xSecondCol; + xCols->insertByIndex(nPos == (sal_uInt16)-1 ? nPos : ++nPos, aElement); + } + + // ist die component::Form an die Datenbankangebunden? + Reference< XFormComponent > xFormCp(xCols, UNO_QUERY); + Reference< XPropertySet > xForm(xFormCp->getParent(), UNO_QUERY); + if (xForm.is()) + { + if (!::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).getLength()) + { + if ( sDatasouce.getLength() ) + xForm->setPropertyValue(FM_PROP_DATASOURCE, makeAny(sDatasouce)); + else + xForm->setPropertyValue(FM_PROP_URL, makeAny(sURL)); + } + + if (!::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).getLength()) + { + xForm->setPropertyValue(FM_PROP_COMMAND, makeAny(sCommand)); + Any aCommandType; + switch (nCommandType) + { + case CommandType::TABLE: + aCommandType <<= (sal_Int32)CommandType::TABLE; + break; + case CommandType::QUERY: + aCommandType <<= (sal_Int32)CommandType::QUERY; + break; + default: + aCommandType <<= (sal_Int32)CommandType::COMMAND; + xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, bool2any((sal_Bool)(2 == nCommandType))); + break; + } + xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType); + } + } + } + catch (Exception&) + { + DBG_ERROR("FmGridHeader::OnAsyncExecuteDrop: caught an exception while creatin' the column !"); + ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet); + ::comphelper::disposeComponent(m_pImpl->xDroppedStatement); + return 0L; + } + + ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet); + ::comphelper::disposeComponent(m_pImpl->xDroppedStatement); + return 1L; +} + +//------------------------------------------------------------------------------ +void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, PopupMenu& rMenu) +{ + sal_Bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode(); + + Reference< ::com::sun::star::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns()); + // Aufbau des Insert Menues + // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND + if(nColId > 0) + { + sal_uInt16 nPos2 = GetModelColumnPos(nColId); + + Reference< ::com::sun::star::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns()); + Reference< ::com::sun::star::beans::XPropertySet> xColumn; + ::cppu::extractInterface(xColumn, xColumns->getByIndex(nPos2)); + Reference< ::com::sun::star::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY); + if (xSelSupplier.is()) + xSelSupplier->select(makeAny(xColumn)); + } + + // EinfuegePosition, immer vor der aktuellen Spalte + sal_uInt16 nPos = GetModelColumnPos(nColId); + sal_Bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId); + + ImageList aImageList( SVX_RES(RID_SVXIMGLIST_FMEXPL) ); + PopupMenu* pControlMenu = new PopupMenu; + + PopupMenu* pMenu = rMenu.GetPopupMenu(SID_FM_INSERTCOL); + if (pMenu) + { + SetMenuItem(aImageList, SID_FM_EDIT, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_CHECKBOX, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_COMBOBOX, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_LISTBOX, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_DATEFIELD, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_TIMEFIELD, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_NUMERICFIELD, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_CURRENCYFIELD, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_PATTERNFIELD, pMenu, *pControlMenu, bDesignMode); + SetMenuItem(aImageList, SID_FM_FORMATTEDFIELD, pMenu, *pControlMenu, bDesignMode); + } + + if (pMenu && xCols.is() && nColId) + { + Reference< ::com::sun::star::beans::XPropertySet > xSet; + ::cppu::extractInterface(xSet, xCols->getByIndex(nPos)); + sal_Int16 nClassId; + xSet->getPropertyValue(FM_PROP_CLASSID) >>= nClassId; + + Reference< ::com::sun::star::io::XPersistObject > xServiceQuestion(xSet, UNO_QUERY); + sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0; + if (nColType == TYPE_TEXTFIELD) + { // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD + // in both cases. And as columns don't have an ::com::sun::star::lang::XServiceInfo interface, we have to distinguish both + // types via the existence of special properties + Reference< ::com::sun::star::beans::XPropertySet > xProps(xSet, UNO_QUERY); + if (xProps.is()) + { + Reference< ::com::sun::star::beans::XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo(); + if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER)) + nColType = TYPE_FORMATTEDFIELD; + } + } + + pControlMenu->EnableItem(SID_FM_EDIT + nChangeTypeOffset, bDesignMode && (nColType != TYPE_TEXTFIELD)); + pControlMenu->EnableItem(SID_FM_COMBOBOX + nChangeTypeOffset, bDesignMode && (nColType != TYPE_COMBOBOX)); + pControlMenu->EnableItem(SID_FM_LISTBOX + nChangeTypeOffset, bDesignMode && (nColType != TYPE_LISTBOX)); + pControlMenu->EnableItem(SID_FM_CHECKBOX + nChangeTypeOffset, bDesignMode && (nColType != TYPE_CHECKBOX)); + pControlMenu->EnableItem(SID_FM_DATEFIELD + nChangeTypeOffset, bDesignMode && (nColType != TYPE_DATEFIELD)); + pControlMenu->EnableItem(SID_FM_NUMERICFIELD + nChangeTypeOffset, bDesignMode && (nColType != TYPE_NUMERICFIELD)); + pControlMenu->EnableItem(SID_FM_TIMEFIELD + nChangeTypeOffset, bDesignMode && (nColType != TYPE_TIMEFIELD)); + pControlMenu->EnableItem(SID_FM_CURRENCYFIELD + nChangeTypeOffset, bDesignMode && (nColType != TYPE_CURRENCYFIELD)); + pControlMenu->EnableItem(SID_FM_PATTERNFIELD + nChangeTypeOffset, bDesignMode && (nColType != TYPE_PATTERNFIELD)); + pControlMenu->EnableItem(SID_FM_FORMATTEDFIELD + nChangeTypeOffset, bDesignMode && (nColType != TYPE_FORMATTEDFIELD)); + rMenu.SetPopupMenu(SID_FM_CHANGECOL, pControlMenu); + } + + rMenu.EnableItem(SID_FM_INSERTCOL, bDesignMode && xCols.is()); + rMenu.EnableItem(SID_FM_DELETECOL, bDesignMode && bMarked && xCols.is()); + rMenu.EnableItem(SID_FM_CHANGECOL, bDesignMode && bMarked && xCols.is()); + rMenu.EnableItem(SID_FM_SHOW_PROPERTY_BROWSER, bDesignMode && bMarked && xCols.is()); + + PopupMenu* pShowColsMenu = rMenu.GetPopupMenu(SID_FM_SHOWCOLS); + sal_uInt16 nHiddenCols = 0; + if (pShowColsMenu) + { + if (xCols.is()) + { + // check for hidden cols + Reference< ::com::sun::star::beans::XPropertySet > xCurCol; + Any aHidden,aName; + for (sal_uInt16 i=0; i<xCols->getCount(); ++i) + { + ::cppu::extractInterface(xCurCol, xCols->getByIndex(i)); + DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !"); + aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN); + DBG_ASSERT(aHidden.getValueType().getTypeClass() == TypeClass_BOOLEAN, + "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !"); + if (::comphelper::getBOOL(aHidden)) + { + // put the column name into the 'show col' menu + if (nHiddenCols < 16) + { // (only the first 16 items to keep the menu rather small) + aName = xCurCol->getPropertyValue(FM_PROP_LABEL); + pShowColsMenu->InsertItem(nHiddenCols + 1, ::comphelper::getString(aName), 0, nHiddenCols); + // the ID is arbitrary, but should be unique within the whole menu + } + ++nHiddenCols; + } + } + } + pShowColsMenu->EnableItem(SID_FM_SHOWCOLS_MORE, xCols.is() && (nHiddenCols > 16)); + pShowColsMenu->EnableItem(SID_FM_SHOWALLCOLS, xCols.is() && (nHiddenCols > 0)); + } + + // allow the 'hide column' item ? + sal_Bool bAllowHide = bMarked; // a column is marked + bAllowHide = bAllowHide || (!bDesignMode && (nPos != (sal_uInt16)-1)); // OR we are in alive mode and have hit a column + bAllowHide = bAllowHide && xCols.is(); // AND we have a column container + bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1); // AND there are at least two visible columns + rMenu.EnableItem(SID_FM_HIDECOL, bAllowHide); + + sal_Bool bChecked = sal_False; + if (bMarked) + { + + SfxViewFrame* pCurrentFrame = SfxViewFrame::Current(); + SfxItemState eState = SFX_ITEM_UNKNOWN; + // ask the bindings of the current view frame (which should be the one we're residing in) for the state + if (pCurrentFrame) + { + SfxPoolItem* pItem = NULL; + eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem); + + if (eState >= SFX_ITEM_AVAILABLE && pItem ) + { + bChecked = pItem->ISA(SfxBoolItem) && ((SfxBoolItem*)pItem)->GetValue(); + rMenu.CheckItem(SID_FM_SHOW_PROPERTY_BROWSER,bChecked); + } + delete pItem; + } + } +} + +enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone }; + +//------------------------------------------------------------------------------ +void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const PopupMenu& rMenu, sal_uInt16 nExecutionResult) +{ + Reference< ::com::sun::star::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns()); + sal_uInt16 nPos = GetModelColumnPos(nColId); + + // remove and delet the menu we inserted in PreExecuteColumnContextMenu + PopupMenu* pControlMenu = rMenu.GetPopupMenu(SID_FM_CHANGECOL); + delete pControlMenu; + + ::rtl::OUString aFieldType; + sal_Bool bReplace = sal_False; + InspectorAction eInspectorAction = eNone; + Reference< XPropertySet > xColumnToInspect; + switch (nExecutionResult) + { + case SID_FM_DELETECOL: + { + Reference< XInterface > xCol; + ::cppu::extractInterface(xCol, xCols->getByIndex(nPos)); + xCols->removeByIndex(nPos); + ::comphelper::disposeComponent(xCol); + } break; + case SID_FM_SHOW_PROPERTY_BROWSER: + eInspectorAction = rMenu.IsItemChecked( SID_FM_SHOW_PROPERTY_BROWSER ) ? eOpenInspector : eCloseInspector; + xColumnToInspect.set( xCols->getByIndex( nPos ), UNO_QUERY ); + break; + case SID_FM_EDIT + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_EDIT: + aFieldType = FM_COL_TEXTFIELD; + break; + case SID_FM_COMBOBOX + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_COMBOBOX: + aFieldType = FM_COL_COMBOBOX; + break; + case SID_FM_LISTBOX + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_LISTBOX: + aFieldType = FM_COL_LISTBOX; + break; + case SID_FM_CHECKBOX + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_CHECKBOX: + aFieldType = FM_COL_CHECKBOX; + break; + case SID_FM_DATEFIELD + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_DATEFIELD: + aFieldType = FM_COL_DATEFIELD; + break; + case SID_FM_TIMEFIELD + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_TIMEFIELD: + aFieldType = FM_COL_TIMEFIELD; + break; + case SID_FM_NUMERICFIELD + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_NUMERICFIELD: + aFieldType = FM_COL_NUMERICFIELD; + break; + case SID_FM_CURRENCYFIELD + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_CURRENCYFIELD: + aFieldType = FM_COL_CURRENCYFIELD; + break; + case SID_FM_PATTERNFIELD + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_PATTERNFIELD: + aFieldType = FM_COL_PATTERNFIELD; + break; + case SID_FM_FORMATTEDFIELD + nChangeTypeOffset: + bReplace = sal_True; + case SID_FM_FORMATTEDFIELD: + aFieldType = FM_COL_FORMATTEDFIELD; + break; + case SID_FM_HIDECOL: + { + Reference< ::com::sun::star::beans::XPropertySet > xCurCol; + ::cppu::extractInterface(xCurCol, xCols->getByIndex(nPos)); + xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny((sal_Bool)sal_True)); + } + break; + case SID_FM_SHOWCOLS_MORE: + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + if(pFact) + { + AbstractFmShowColsDialog* pDlg = pFact->CreateFmShowColsDialog(NULL); + DBG_ASSERT(pDlg, "Dialogdiet fail!");//CHINA001 + pDlg->SetColumns(xCols); + pDlg->Execute(); + delete pDlg; + } + + } + break; + case SID_FM_SHOWALLCOLS: + { + // just iterate through all the cols ... + Reference< ::com::sun::star::beans::XPropertySet > xCurCol; + for (sal_uInt16 i=0; i<xCols->getCount(); ++i) + { + ::cppu::extractInterface(xCurCol, xCols->getByIndex(i)); + xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny((sal_Bool)sal_False)); + } + // TODO : there must be a more clever way to do this .... + // with the above the view is updated after every single model update ... + } + break; + default: + if (nExecutionResult>0 && nExecutionResult<=16) + { // it was a "show column/<colname>" command (there are at most 16 such items) + // search the nExecutionResult'th hidden col + Reference< ::com::sun::star::beans::XPropertySet > xCurCol; + for (sal_uInt16 i=0; i<xCols->getCount() && nExecutionResult; ++i) + { + ::cppu::extractInterface(xCurCol, xCols->getByIndex(i)); + Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN); + if (::comphelper::getBOOL(aHidden)) + if (!--nExecutionResult) + { + xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny((sal_Bool)sal_False)); + break; + } + } + } + break; + } + + if ( aFieldType.getLength() ) + { + try + { + Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW ); + Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW ); + + if ( bReplace ) + { + // ein paar Properties hinueberretten + Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY ); + + OStaticDataAccessTools().TransferFormComponentProperties( + xReplaced, xNewCol, Application::GetSettings().GetUILocale() ); + + xCols->replaceByIndex( nPos, makeAny( xNewCol ) ); + ::comphelper::disposeComponent( xReplaced ); + + eInspectorAction = eUpdateInspector; + xColumnToInspect = xNewCol; + } + else + { + FormControlFactory factory( ::comphelper::getProcessServiceFactory() ); + + ::rtl::OUString sLabel = factory.getDefaultUniqueName_ByComponentType( + Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol ); + xNewCol->setPropertyValue( FM_PROP_LABEL, makeAny( sLabel ) ); + xNewCol->setPropertyValue( FM_PROP_NAME, makeAny( sLabel ) ); + + factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol ); + + xCols->insertByIndex( nPos, makeAny( xNewCol ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + } + + SfxViewFrame* pCurrentFrame = SfxViewFrame::Current(); + OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" ); + if ( pCurrentFrame ) + { + if ( eInspectorAction == eUpdateInspector ) + { + if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) ) + eInspectorAction = eNone; + } + + if ( eInspectorAction != eNone ) + { + FmInterfaceItem aIFaceItem( SID_FM_SHOW_PROPERTY_BROWSER, xColumnToInspect ); + SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction == eCloseInspector ? FALSE : TRUE ); + + pCurrentFrame->GetBindings().GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SFX_CALLMODE_ASYNCHRON, + &aIFaceItem, &aShowItem, 0L ); + } + } +} + +//------------------------------------------------------------------------------ +void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos ) +{ + // the affected col + sal_uInt16 nColId = GetItemId( _rPreferredPos ); + + // the menu + PopupMenu aContextMenu( SVX_RES( RID_SVXMNU_COLS ) ); + + // let derivees modify the menu + PreExecuteColumnContextMenu( nColId, aContextMenu ); + aContextMenu.RemoveDisabledEntries( sal_True, sal_True ); + + // execute the menu + sal_uInt16 nResult = aContextMenu.Execute( this, _rPreferredPos ); + + // let derivees handle the result + PostExecuteColumnContextMenu( nColId, aContextMenu, nResult ); +} + +//------------------------------------------------------------------------------ +void FmGridHeader::Command(const CommandEvent& rEvt) +{ + switch (rEvt.GetCommand()) + { + case COMMAND_CONTEXTMENU: + { + if (!rEvt.IsMouseEvent()) + return; + + triggerColumnContextMenu( rEvt.GetMousePosPixel() ); + } + break; + default: + EditBrowserHeader::Command(rEvt); + } +} + +//------------------------------------------------------------------------------ +FmGridControl::FmGridControl( + Reference< ::com::sun::star::lang::XMultiServiceFactory > _rxFactory, + Window* pParent, + FmXGridPeer* _pPeer, + WinBits nBits) + :DbGridControl(_rxFactory, pParent, nBits) + ,m_pPeer(_pPeer) + ,m_nCurrentSelectedColumn(-1) + ,m_nMarkedColumnId(BROWSER_INVALIDID) + ,m_bSelecting(sal_False) + ,m_bInColumnMove(sal_False) +{ + EnableInteractiveRowHeight( ); +} + +//------------------------------------------------------------------------------ +void FmGridControl::Command(const CommandEvent& _rEvt) +{ + if ( COMMAND_CONTEXTMENU == _rEvt.GetCommand() ) + { + FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() ); + if ( pMyHeader && !_rEvt.IsMouseEvent() ) + { // context menu requested by keyboard + if ( 1 == GetSelectColumnCount() || IsDesignMode() ) + { + sal_uInt16 nSelId = GetColumnId( + sal::static_int_cast< USHORT >( FirstSelectedColumn() ) ); + ::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, sal_False ) ); + + Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) ); + pMyHeader->triggerColumnContextMenu( aRelativePos, FmGridHeader::AccessControl() ); + + // handled + return; + } + } + } + + DbGridControl::Command( _rEvt ); +} + +// ::com::sun::star::beans::XPropertyChangeListener +//------------------------------------------------------------------------------ +void FmGridControl::propertyChange(const ::com::sun::star::beans::PropertyChangeEvent& evt) +{ + if (evt.PropertyName == FM_PROP_ROWCOUNT) + { + // if we're not in the main thread call AdjustRows asynchronously + implAdjustInSolarThread(sal_True); + return; + } + + const DbGridRowRef& xRow = GetCurrentRow(); + // waehrend Positionierung wird kein abgleich der Properties vorgenommen + Reference<XPropertySet> xSet(evt.Source,UNO_QUERY); + if (xRow.Is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark()))) + { + if (evt.PropertyName == FM_PROP_ISMODIFIED) + { + // modified or clean ? + GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GRS_MODIFIED : GRS_CLEAN; + if (eStatus != xRow->GetStatus()) + { + xRow->SetStatus(eStatus); + vos::OGuard aGuard( Application::GetSolarMutex() ); + RowModified(GetCurrentPos()); + } + } + } +} + +//------------------------------------------------------------------------------ +void FmGridControl::SetDesignMode(sal_Bool bMode) +{ + sal_Bool bOldMode = IsDesignMode(); + DbGridControl::SetDesignMode(bMode); + if (bOldMode != bMode) + { + if (!bMode) + { + // selection aufheben + markColumn(USHRT_MAX); + } + else + { + Reference< ::com::sun::star::container::XIndexContainer > xColumns(GetPeer()->getColumns()); + Reference< ::com::sun::star::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY); + if (xSelSupplier.is()) + { + Any aSelection = xSelSupplier->getSelection(); + Reference< ::com::sun::star::beans::XPropertySet > xColumn; + if (aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE) + ::cppu::extractInterface(xColumn, aSelection); + Reference< XInterface > xCurrent; + for (sal_uInt16 i=0; i<xColumns->getCount(); ++i) + { + ::cppu::extractInterface(xCurrent, xColumns->getByIndex(i)); + if (xCurrent == xColumn) + { + markColumn(GetColumnIdFromModelPos(i)); + break; + } + } + } + } + } +} + +//------------------------------------------------------------------------------ +void FmGridControl::DeleteSelectedRows() +{ + if (!m_pSeekCursor) + return; + + // how many rows are selected? + sal_Int32 nSelectedRows = GetSelectRowCount(); + + // the current line should be deleted but it is currently in edit mode + if ( IsCurrentAppending() ) + return; + // is the insert row selected + if (GetEmptyRow().Is() && IsRowSelected(GetRowCount() - 1)) + nSelectedRows -= 1; + + // nothing to do + if (nSelectedRows <= 0) + return; + + // try to confirm the delete + Reference< ::com::sun::star::frame::XDispatchProvider > xDispatcher = (::com::sun::star::frame::XDispatchProvider*)GetPeer(); + if (xDispatcher.is()) + { + ::com::sun::star::util::URL aUrl; + aUrl.Complete = FMURL_CONFIRM_DELETION; + // #100312# ------------ + Reference< ::com::sun::star::util::XURLTransformer > xTransformer( + ::comphelper::getProcessServiceFactory()->createInstance( + ::rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer")), UNO_QUERY); + if( xTransformer.is() ) + xTransformer->parseStrict( aUrl ); + + Reference< ::com::sun::star::frame::XDispatch > xDispatch = xDispatcher->queryDispatch(aUrl, rtl::OUString(), 0); + Reference< ::com::sun::star::form::XConfirmDeleteListener > xConfirm(xDispatch, UNO_QUERY); + if (xConfirm.is()) + { + ::com::sun::star::sdb::RowChangeEvent aEvent; + aEvent.Source = (Reference< XInterface > )(*getDataSource()); + aEvent.Rows = nSelectedRows; + aEvent.Action = ::com::sun::star::sdb::RowChangeAction::DELETE; + if (!xConfirm->confirmDelete(aEvent)) + return; + } + } + + const MultiSelection* pRowSelection = GetSelection(); + if ( pRowSelection && pRowSelection->IsAllSelected() ) + { + BeginCursorAction(); + CursorWrapper* pCursor = getDataSource(); + Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*pCursor, UNO_QUERY); + try + { + pCursor->beforeFirst(); + while( pCursor->next() ) + xUpdateCursor->deleteRow(); + + SetUpdateMode(sal_False); + SetNoSelection(); + + xUpdateCursor->moveToInsertRow(); + } + catch(const Exception&) + { + OSL_ENSURE(0,"Exception caught while deleting rows!"); + } + // An den DatenCursor anpassen + AdjustDataSource(sal_True); + EndCursorAction(); + SetUpdateMode(sal_True); + } + else + { + Reference< ::com::sun::star::sdbcx::XDeleteRows > xDeleteThem((Reference< XInterface >)*getDataSource(), UNO_QUERY); + + // colect the bookmarks of the selected rows + Sequence < Any> aBookmarks = getSelectionBookmarks(); + + // determine the next row to position after deletion + Any aBookmark; + sal_Bool bNewPos = sal_False; + // if the current row isn't selected we take the row as row after deletion + OSL_ENSURE( GetCurrentRow().Is(), "FmGridControl::DeleteSelectedRows: no current row here?" ); + // crash reports suggest it can happen we don't have a current row - how? + // #154303# / 2008-04-23 / frank.schoenheit@sun.com + if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().Is() ) + { + aBookmark = GetCurrentRow()->GetBookmark(); + bNewPos = sal_True; + } + else + { + // we look for the first row after the selected block for selection + long nIdx = LastSelectedRow() + 1; + if (nIdx < GetRowCount() - 1) + { + // there is a next row to position on + if (SeekCursor(nIdx)) + { + GetSeekRow()->SetState(m_pSeekCursor, sal_True); + + bNewPos = sal_True; + // if it's not the row for inserting we keep the bookmark + if (!IsInsertionRow(nIdx)) + aBookmark = m_pSeekCursor->getBookmark(); + } + } + else + { + // we look for the first row before the selected block for selection after deletion + nIdx = FirstSelectedRow() - 1; + if (nIdx >= 0 && SeekCursor(nIdx)) + { + GetSeekRow()->SetState(m_pSeekCursor, sal_True); + + bNewPos = sal_True; + aBookmark = m_pSeekCursor->getBookmark(); + } + } + } + + // Sind alle Zeilen Selectiert + // Zweite bedingung falls keine einguegeZeile existiert + sal_Bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows; + + BeginCursorAction(); + + // now delete the row + Sequence <sal_Int32> aDeletedRows; + SetUpdateMode( FALSE ); + try + { + aDeletedRows = xDeleteThem->deleteRows(aBookmarks); + } + catch(SQLException&) + { + } + SetUpdateMode( TRUE ); + + // how many rows are deleted? + sal_Int32 nDeletedRows = 0; + const sal_Int32* pSuccess = aDeletedRows.getConstArray(); + for (sal_Int32 i = 0; i < aDeletedRows.getLength(); i++) + { + if (pSuccess[i]) + ++nDeletedRows; + } + + // sind Zeilen geloescht worden? + if (nDeletedRows) + { + SetUpdateMode(sal_False); + SetNoSelection(); + try + { + // did we delete all the rows than try to move to the next possible row + if (nDeletedRows == aDeletedRows.getLength()) + { + // there exists a new position to move on + if (bNewPos) + { + if (aBookmark.hasValue()) + getDataSource()->moveToBookmark(aBookmark); + // no valid bookmark so move to the insert row + else + { + Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY); + xUpdateCursor->moveToInsertRow(); + } + } + else + { + Reference< ::com::sun::star::beans::XPropertySet > xSet((Reference< XInterface >)*m_pDataCursor, UNO_QUERY); + + sal_Int32 nRecordCount(0); + xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount; + if ( m_pDataCursor->rowDeleted() ) + --nRecordCount; + + // there are no rows left and we have an insert row + if (!nRecordCount && GetEmptyRow().Is()) + { + Reference< XResultSetUpdate > xUpdateCursor((Reference< XInterface >)*m_pDataCursor, UNO_QUERY); + xUpdateCursor->moveToInsertRow(); + } + else if (nRecordCount) + // move to the first row + getDataSource()->first(); + } + } + // not all the rows where deleted, so move to the first row which remained in the resultset + else + { + for (sal_Int32 i = 0; i < aDeletedRows.getLength(); i++) + { + if (!pSuccess[i]) + { + getDataSource()->moveToBookmark(aBookmarks.getConstArray()[i]); + break; + } + } + } + } + catch(const Exception&) + { + try + { + // positioning went wrong so try to move to the first row + getDataSource()->first(); + } + catch(const Exception&) + { + } + } + + // An den DatenCursor anpassen + AdjustDataSource(sal_True); + + // es konnten nicht alle Zeilen geloescht werden + // da nie nicht geloeschten wieder selektieren + if (nDeletedRows < nSelectedRows) + { + // waren alle selektiert + if (bAllSelected) + { + SelectAll(); + if (IsInsertionRow(GetRowCount() - 1)) // einfuegeZeile nicht + SelectRow(GetRowCount() - 1, sal_False); + } + else + { + // select the remaining rows + for (sal_Int32 i = 0; i < aDeletedRows.getLength(); i++) + { + try + { + if (!pSuccess[i]) + { + m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark()); + SetSeekPos(m_pSeekCursor->getRow() - 1); + SelectRow(GetSeekPos()); + } + } + catch(const Exception&) + { + // keep the seekpos in all cases + SetSeekPos(m_pSeekCursor->getRow() - 1); + } + } + } + } + + EndCursorAction(); + SetUpdateMode(sal_True); + } + else // Zeile konnte nicht geloescht werden + { + EndCursorAction(); + try + { + // currentrow is the insert row? + if (!IsCurrentAppending()) + getDataSource()->refreshRow(); + } + catch(const Exception&) + { + } + } + } + + // if there is no selection anymore we can start editing + if (!GetSelectRowCount()) + ActivateCell(); +} + + +// XCurrentRecordListener +//------------------------------------------------------------------------------ +void FmGridControl::positioned(const ::com::sun::star::lang::EventObject& /*rEvent*/) +{ + TRACE_RANGE("FmGridControl::positioned"); + // position on the data source (force it to be done in the main thread) + implAdjustInSolarThread(sal_False); +} + +//------------------------------------------------------------------------------ +sal_Bool FmGridControl::commit() +{ + // Commit nur ausfuehren, wenn nicht bereits ein Update vom ::com::sun::star::form::component::GridControl ausgefuehrt + // wird + if (!IsUpdating()) + { + if (Controller().Is() && Controller()->IsModified()) + { + if (!SaveModified()) + return sal_False; + } + } + return sal_True; +} + +//------------------------------------------------------------------------------ +void FmGridControl::inserted(const ::com::sun::star::lang::EventObject& /*rEvent*/) +{ + const DbGridRowRef& xRow = GetCurrentRow(); + if (!xRow.Is()) + return; + + // Zeile ist eingefuegt worden, dann den status und mode zuruecksetzen + xRow->SetState(m_pDataCursor, sal_False); + xRow->SetNew(sal_False); + +} + +// XCancelUpdateRecordListener +//------------------------------------------------------------------------------ +void FmGridControl::restored(const ::com::sun::star::lang::EventObject& rEvent) +{ + if (!GetCurrentRow().Is()) + return; + + sal_Bool bAppending = GetCurrentRow()->IsNew(); + sal_Bool bDirty = GetCurrentRow()->IsModified(); + if (bAppending && (EditBrowseBox::IsModified() || bDirty)) + { + if (Controller().Is()) + Controller()->ClearModified(); + + // jetzt die Zeile herausnehmen + RowRemoved(GetRowCount() - 1, 1, sal_True); + GetNavigationBar().InvalidateAll(); + } + + positioned(rEvent); +} + +//------------------------------------------------------------------------------ +BrowserHeader* FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent) +{ + DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" ); + return new FmGridHeader( pParent ); +} + +//------------------------------------------------------------------------------ +void FmGridControl::markColumn(sal_uInt16 nId) +{ + if (GetHeaderBar() && m_nMarkedColumnId != nId) + { + // deselektieren + if (m_nMarkedColumnId != BROWSER_INVALIDID) + { + HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HIB_FLAT; + GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits); + } + + + if (nId != BROWSER_INVALIDID) + { + HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HIB_FLAT; + GetHeaderBar()->SetItemBits(nId, aBits); + } + m_nMarkedColumnId = nId; + } +} + +//------------------------------------------------------------------------------ +sal_Bool FmGridControl::isColumnMarked(sal_uInt16 nId) const +{ + return m_nMarkedColumnId == nId; +} + +//------------------------------------------------------------------------------ +long FmGridControl::QueryMinimumRowHeight() +{ + long nMinimalLogicHeight = 20; // 0.2 cm + long nMinimalPixelHeight = LogicToPixel( Point( 0, nMinimalLogicHeight ), MAP_10TH_MM ).Y(); + return CalcZoom( nMinimalPixelHeight ); +} + +//------------------------------------------------------------------------------ +void FmGridControl::RowHeightChanged() +{ + DbGridControl::RowHeightChanged(); + + Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY ); + DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" ); + if ( xModel.is() ) + { + try + { + sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() ); + Any aProperty = makeAny( (sal_Int32)PixelToLogic( Point( 0, nUnzoomedPixelHeight ), MAP_10TH_MM ).Y() ); + xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty ); + } + catch( const Exception& ) + { + OSL_ENSURE( sal_False, "FmGridControl::RowHeightChanged: caught an exception!" ); + } + } +} + +//------------------------------------------------------------------------------ +void FmGridControl::ColumnResized(sal_uInt16 nId) +{ + DbGridControl::ColumnResized(nId); + + // Wert ans model uebergeben + DbGridColumn* pCol = DbGridControl::GetColumns().GetObject(GetModelColumnPos(nId)); + Reference< ::com::sun::star::beans::XPropertySet > xColModel(pCol->getModel()); + if (xColModel.is()) + { + Any aWidth; + sal_Int32 nColumnWidth = GetColumnWidth(nId); + nColumnWidth = CalcReverseZoom(nColumnWidth); + // Umrechnen in 10THMM + aWidth <<= (sal_Int32)PixelToLogic(Point(nColumnWidth,0),MAP_10TH_MM).X(); + xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth); + } +} + +//------------------------------------------------------------------------------ +void FmGridControl::CellModified() +{ + DbGridControl::CellModified(); + GetPeer()->CellModified(); +} + +//------------------------------------------------------------------------------ +void FmGridControl::BeginCursorAction() +{ + DbGridControl::BeginCursorAction(); + m_pPeer->stopCursorListening(); +} + +//------------------------------------------------------------------------------ +void FmGridControl::EndCursorAction() +{ + m_pPeer->startCursorListening(); + DbGridControl::EndCursorAction(); +} + +//------------------------------------------------------------------------------ +void FmGridControl::ColumnMoved(sal_uInt16 nId) +{ + m_bInColumnMove = sal_True; + + DbGridControl::ColumnMoved(nId); + Reference< ::com::sun::star::container::XIndexContainer > xColumns(GetPeer()->getColumns()); + + if (xColumns.is()) + { + // suchen der Spalte und verschieben im Model + // ColumnPos holen + DbGridColumn* pCol = DbGridControl::GetColumns().GetObject(GetModelColumnPos(nId)); + Reference< ::com::sun::star::beans::XPropertySet > xCol; + + // Einfuegen muß sich an den Column Positionen orientieren + sal_Int32 i; + Reference< XInterface > xCurrent; + for (i = 0; !xCol.is() && i < xColumns->getCount(); i++) + { + ::cppu::extractInterface(xCurrent, xColumns->getByIndex(i)); + if (xCurrent == pCol->getModel()) + { + xCol = pCol->getModel(); + break; + } + } + + DBG_ASSERT(i < xColumns->getCount(), "Falscher ::com::sun::star::sdbcx::Index"); + xColumns->removeByIndex(i); + Any aElement; + aElement <<= xCol; + xColumns->insertByIndex(GetModelColumnPos(nId), aElement); + pCol->setModel(xCol); + // if the column which is shown here is selected ... + if ( isColumnSelected(nId,pCol) ) + markColumn(nId); // ... -> mark it + } + + m_bInColumnMove = sal_False; +} + +//------------------------------------------------------------------------------ +void FmGridControl::InitColumnsByModels(const Reference< ::com::sun::star::container::XIndexContainer >& xColumns) +{ + // Spalten wieder neu setzen + // wenn es nur eine HandleColumn gibt, dann nicht + if (GetModelColCount()) + { + RemoveColumns(); + InsertHandleColumn(); + } + + if (!xColumns.is()) + return; + + SetUpdateMode(sal_False); + + // Einfuegen mu� sich an den Column Positionen orientieren + sal_Int32 i; + String aName; + Any aWidth; + for (i = 0; i < xColumns->getCount(); ++i) + { + Reference< ::com::sun::star::beans::XPropertySet > xCol; + ::cppu::extractInterface(xCol, xColumns->getByIndex(i)); + + aName = (const sal_Unicode*)::comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)); + + aWidth = xCol->getPropertyValue(FM_PROP_WIDTH); + sal_Int32 nWidth = 0; + if (aWidth >>= nWidth) + nWidth = LogicToPixel(Point(nWidth,0),MAP_10TH_MM).X(); + + AppendColumn(aName, (sal_uInt16)nWidth); + DbGridColumn* pCol = DbGridControl::GetColumns().GetObject(i); + pCol->setModel(xCol); + } + + // und jetzt noch die hidden columns rausnehmen + // (wir haben das nicht gleich in der oberen Schleife gemacht, da wir dann Probleme mit den + // IDs der Spalten bekommen haetten : AppendColumn vergibt die automatisch, die Spalte _nach_ + // einer versteckten braucht aber eine um eine erhoehte ID .... + Any aHidden; + for (i = 0; i < xColumns->getCount(); ++i) + { + Reference< ::com::sun::star::beans::XPropertySet > xCol; + ::cppu::extractInterface(xCol, xColumns->getByIndex(i)); + aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN); + if (::comphelper::getBOOL(aHidden)) + HideColumn(GetColumnIdFromModelPos((sal_uInt16)i)); + } + + SetUpdateMode(sal_True); +} + +//------------------------------------------------------------------------------ +void FmGridControl::InitColumnByField( + DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel, + const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex ) +{ + DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" ); + + // lookup the column which belongs to the control source + ::rtl::OUString sFieldName; + _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName; + Reference< XPropertySet > xField; + _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField; + + + if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length + _rxFieldsByNames->getByName( sFieldName ) >>= xField; + + // determine the position of this column + sal_Int32 nFieldPos = -1; + if ( xField.is() ) + { + Reference< XPropertySet > xCheck; + sal_Int32 nFieldCount = _rxFieldsByIndex->getCount(); + for ( sal_Int32 i = 0; i < nFieldCount; ++i) + { + _rxFieldsByIndex->getByIndex( i ) >>= xCheck; + if ( xField.get() == xCheck.get() ) + { + nFieldPos = i; + break; + } + } + } + + if ( xField.is() && ( nFieldPos >= 0 ) ) + { + // some data types are not allowed + sal_Int32 nDataType = DataType::OTHER; + xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType; + + sal_Bool bIllegalType = sal_False; + switch ( nDataType ) + { + case DataType::BLOB: + case DataType::LONGVARBINARY: + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::OTHER: + bIllegalType = sal_True; + break; + } + + if ( bIllegalType ) + { + _pColumn->SetObject( (sal_Int16)nFieldPos ); + return; + } +/* + // handle readonly columns + sal_Bool bReadOnly = sal_True; + xField->getPropertyValue( FM_PROP_ISREADONLY ) >>= bReadOnly; + _pColumn->SetReadOnly( bReadOnly ); +*/ + } + + // the control type is determined by the ColumnServiceName + static ::rtl::OUString s_sPropColumnServiceName( RTL_CONSTASCII_USTRINGPARAM( "ColumnServiceName" ) ); + if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) ) + return; + + _pColumn->setModel( _rxColumnModel ); + + ::rtl::OUString sColumnServiceName; + _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName; + + sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName ); + _pColumn->CreateControl( nFieldPos, xField, nTypeId ); +} + +//------------------------------------------------------------------------------ +void FmGridControl::InitColumnsByFields(const Reference< ::com::sun::star::container::XIndexAccess >& _rxFields) +{ + if ( !_rxFields.is() ) + return; + + // Spalten initialisieren + Reference< XIndexContainer > xColumns( GetPeer()->getColumns() ); + Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY ); + + // Einfuegen muss sich an den Column Positionen orientieren + for (sal_Int32 i = 0; i < xColumns->getCount(); i++) + { + DbGridColumn* pCol = GetColumns().GetObject(i); + OSL_ENSURE(pCol,"No grid column!"); + if ( pCol ) + { + Reference< XPropertySet > xColumnModel; + ::cppu::extractInterface( xColumnModel, xColumns->getByIndex( i ) ); + + InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields ); + } + } +} + +//------------------------------------------------------------------------------ +void FmGridControl::HideColumn(sal_uInt16 nId) +{ + DbGridControl::HideColumn(nId); + + sal_uInt16 nPos = GetModelColumnPos(nId); + if (nPos == (sal_uInt16)-1) + return; + + DbGridColumn* pColumn = GetColumns().GetObject(nPos); + if (pColumn->IsHidden()) + GetPeer()->columnHidden(pColumn); + + if (nId == m_nMarkedColumnId) + m_nMarkedColumnId = (sal_uInt16)-1; +} +// ----------------------------------------------------------------------------- +sal_Bool FmGridControl::isColumnSelected(sal_uInt16 /*nId*/,DbGridColumn* _pColumn) +{ + OSL_ENSURE(_pColumn,"Column can not be null!"); + sal_Bool bSelected = sal_False; + // if the column which is shown here is selected ... + Reference< ::com::sun::star::view::XSelectionSupplier > xSelSupplier(GetPeer()->getColumns(), UNO_QUERY); + if ( xSelSupplier.is() ) + { + Reference< ::com::sun::star::beans::XPropertySet > xColumn; + xSelSupplier->getSelection() >>= xColumn; + bSelected = (xColumn.get() == _pColumn->getModel().get()); + } + return bSelected; +} + +//------------------------------------------------------------------------------ +void FmGridControl::ShowColumn(sal_uInt16 nId) +{ + DbGridControl::ShowColumn(nId); + + sal_uInt16 nPos = GetModelColumnPos(nId); + if (nPos == (sal_uInt16)-1) + return; + + DbGridColumn* pColumn = GetColumns().GetObject(nPos); + if (!pColumn->IsHidden()) + GetPeer()->columnVisible(pColumn); + + // if the column which is shown here is selected ... + if ( isColumnSelected(nId,pColumn) ) + markColumn(nId); // ... -> mark it +} + +//------------------------------------------------------------------------------ +sal_Bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + // need to lock the SolarMutex so that no paint call disturbs us ... + + if ( !m_pSeekCursor ) + { + DBG_ERROR( "FmGridControl::selectBookmarks: no seek cursor!" ); + return sal_False; + } + + const Any* pBookmark = _rBookmarks.getConstArray(); + const Any* pBookmarkEnd = pBookmark + _rBookmarks.getLength(); + + SetNoSelection(); + + sal_Bool bAllSuccessfull = sal_True; + try + { + for (; pBookmark != pBookmarkEnd; ++pBookmark) + { + // move the seek cursor to the row given + if (m_pSeekCursor->moveToBookmark(*pBookmark)) + SelectRow( m_pSeekCursor->getRow() - 1); + else + bAllSuccessfull = sal_False; + } + } + catch(Exception&) + { + DBG_ERROR("FmGridControl::selectBookmarks: could not move to one of the bookmarks!"); + return sal_False; + } + + return bAllSuccessfull; +} + +//------------------------------------------------------------------------------ +Sequence< Any> FmGridControl::getSelectionBookmarks() +{ + // lock our update so no paint-triggered seeks interfere ... + SetUpdateMode(sal_False); + + sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0; + Sequence< Any> aBookmarks(nSelectedRows); + if ( nSelectedRows ) + { + Any* pBookmarks = (Any*)aBookmarks.getArray(); + + // (I'm not sure if the problem isn't deeper : The szenario : a large table displayed by a grid with a + // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress + // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which did a + // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition. + // Unfortunally the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the + // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning + // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails. + // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relys_ on + // the first one, should be secured against recursion, with a broad-minded interpretion of "recursion" : if any of these + // code parts is executed, no other should be accessible. But this sounds very difficult to achieve .... + // ) + + // The next problem caused by the same behaviuor (SeekCursor causes a propertyChanged) : when adjusting rows we implicitly + // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results. + // That's why we _first_ collect the indicies of the selected rows and _then_ their bookmarks. + long nIdx = FirstSelectedRow(); + while (nIdx >= 0) + { + // (we misuse the bookmarks array for this ...) + pBookmarks[i++] <<= (sal_Int32)nIdx; + nIdx = NextSelectedRow(); + } + DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indicies !"); + + for (i=0; i<nSelectedRows; ++i) + { + nIdx = ::comphelper::getINT32(pBookmarks[i]); + if (IsInsertionRow(nIdx)) + { + // leerzeile nicht loeschen + aBookmarks.realloc(--nSelectedRows); + SelectRow(nIdx,sal_False); // selection aufheben fuer leerzeile + break; + } + + // Zunaechst den DatenCursor auf den selektierten Satz pos. + if (SeekCursor(nIdx)) + { + GetSeekRow()->SetState(m_pSeekCursor, sal_True); + + pBookmarks[i] = m_pSeekCursor->getBookmark(); + } + #ifdef DBG_UTIL + else + DBG_ERROR("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !"); + #endif + } + } + SetUpdateMode(sal_True); + + // if one of the SeekCursor-calls failed .... + aBookmarks.realloc(i); + + // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems. + // but this would be incompatible as we need a locking flag, then ...) + + return aBookmarks; +} +// ----------------------------------------------------------------------------- +namespace +{ + ::rtl::OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const ::rtl::OUString& _sPropName) + { + ::rtl::OUString sRetText; + if ( _pPeer && _nPosition != -1) + { + Reference<XIndexContainer> xIndex = _pPeer->getColumns(); + if ( xIndex.is() && xIndex->getCount() > _nPosition ) + { + Reference<XPropertySet> xProp; + xIndex->getByIndex( _nPosition ) >>= xProp; + if ( xProp.is() ) + xProp->getPropertyValue( _sPropName ) >>= sRetText; + } + } + return sRetText; + } +} +// Object data and state ------------------------------------------------------ +::rtl::OUString FmGridControl::GetAccessibleObjectName( ::svt::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const +{ + ::rtl::OUString sRetText; + switch( _eObjType ) + { + case ::svt::BBTYPE_BROWSEBOX: + if ( GetPeer() ) + { + Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY); + if ( xProp.is() ) + xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText; + } + break; + case ::svt::BBTYPE_COLUMNHEADERCELL: + sRetText = getColumnPropertyFromPeer( + GetPeer(), + GetModelColumnPos( + sal::static_int_cast< sal_uInt16 >(_nPosition)), + FM_PROP_LABEL); + break; + default: + sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition); + } + return sRetText; +} +// ----------------------------------------------------------------------------- + +::rtl::OUString FmGridControl::GetAccessibleObjectDescription( ::svt::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const +{ + ::rtl::OUString sRetText; + switch( _eObjType ) + { + case ::svt::BBTYPE_BROWSEBOX: + if ( GetPeer() ) + { + Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY); + if ( xProp.is() ) + { + xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText; + if ( !sRetText.getLength() ) + xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText; + } + } + break; + case ::svt::BBTYPE_COLUMNHEADERCELL: + sRetText = getColumnPropertyFromPeer( + GetPeer(), + GetModelColumnPos( + sal::static_int_cast< sal_uInt16 >(_nPosition)), + FM_PROP_HELPTEXT); + if ( !sRetText.getLength() ) + sRetText = getColumnPropertyFromPeer( + GetPeer(), + GetModelColumnPos( + sal::static_int_cast< sal_uInt16 >(_nPosition)), + FM_PROP_DESCRIPTION); + + break; + default: + sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition); + } + return sRetText; +} +// ----------------------------------------------------------------------------- +void FmGridControl::Select() +{ + DbGridControl::Select(); + // ... betrifft das unsere Spalten ? + const MultiSelection* pColumnSelection = GetColumnSelection(); + + sal_uInt16 nSelectedColumn = + pColumnSelection && pColumnSelection->GetSelectCount() + ? sal::static_int_cast< sal_uInt16 >( + ((MultiSelection*)pColumnSelection)->FirstSelected()) + : SAL_MAX_UINT16; + // die HandleColumn wird nicht selektiert + switch (nSelectedColumn) + { + case SAL_MAX_UINT16: break; // no selection + case 0 : nSelectedColumn = SAL_MAX_UINT16; break; + // handle col can't be seledted + default : + // get the model col pos instead of the view col pos + nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1)); + break; + } + + if (nSelectedColumn != m_nCurrentSelectedColumn) + { + // VOR dem Aufruf des select am SelectionSupplier ! + m_nCurrentSelectedColumn = nSelectedColumn; + + if (!m_bSelecting) + { + m_bSelecting = sal_True; + + try + { + Reference< XIndexAccess > xColumns(GetPeer()->getColumns(), UNO_QUERY); + Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY); + if (xSelSupplier.is()) + { + if (nSelectedColumn != SAL_MAX_UINT16) + { + Reference< XPropertySet > xColumn; + ::cppu::extractInterface(xColumn,xColumns->getByIndex(nSelectedColumn)); + xSelSupplier->select(makeAny(xColumn)); + } + else + { + xSelSupplier->select(Any()); + } + } + } + catch(Exception&) + { + } + + + m_bSelecting = sal_False; + } + } +} +// ----------------------------------------------------------------------------- +sal_Int32 FmGridControl::GetSelectedColumn() const +{ + return m_nCurrentSelectedColumn; +} +// ----------------------------------------------------------------------------- +void FmGridControl::KeyInput( const KeyEvent& rKEvt ) +{ + sal_Bool bDone = sal_False; + const KeyCode& rKeyCode = rKEvt.GetKeyCode(); + if ( IsDesignMode() + && !rKeyCode.IsShift() + && !rKeyCode.IsMod1() + && !rKeyCode.IsMod2() + && GetParent() ) + { + switch ( rKeyCode.GetCode() ) + { + case KEY_ESCAPE: + GetParent()->GrabFocus(); + bDone = sal_True; + break; + case KEY_DELETE: + if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 ) + { + Reference< ::com::sun::star::container::XIndexContainer > xCols(GetPeer()->getColumns()); + if ( xCols.is() ) + { + try + { + if ( m_nCurrentSelectedColumn < xCols->getCount() ) + { + Reference< XInterface > xCol; + xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol; + xCols->removeByIndex(m_nCurrentSelectedColumn); + ::comphelper::disposeComponent(xCol); + } + } + catch(const Exception&) + { + OSL_ENSURE(0,"exception occured while deleting a column"); + } + } + } + bDone = sal_True; + break; + } + } + if ( !bDone ) + DbGridControl::KeyInput( rKEvt ); +} +// ----------------------------------------------------------------------------- + + + |