Bump version to 6.4-15
[LibreOffice.git] / svx / source / fmcomp / fmgridcl.cxx
blobebbe12f2b96fa4b1c0ba593ab6a2cc1b4e1f22d0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/fmgridif.hxx>
21 #include <fmprop.hxx>
22 #include <svx/fmtools.hxx>
23 #include <fmservs.hxx>
24 #include <fmurl.hxx>
25 #include <formcontrolfactory.hxx>
26 #include <gridcell.hxx>
27 #include <gridcols.hxx>
28 #include <svx/dbaexchange.hxx>
29 #include <svx/dialmgr.hxx>
30 #include <svx/strings.hrc>
31 #include <svx/fmgridcl.hxx>
32 #include <svx/svxdlg.hxx>
33 #include <svx/svxids.hrc>
34 #include <bitmaps.hlst>
35 #include <sdbdatacolumn.hxx>
37 #include <com/sun/star/form/XConfirmDeleteListener.hpp>
38 #include <com/sun/star/form/XFormComponent.hpp>
39 #include <com/sun/star/form/XGridColumnFactory.hpp>
40 #include <com/sun/star/io/XPersistObject.hpp>
41 #include <com/sun/star/sdb/CommandType.hpp>
42 #include <com/sun/star/sdb/RowChangeAction.hpp>
43 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
44 #include <com/sun/star/sdbc/DataType.hpp>
45 #include <com/sun/star/sdbc/SQLException.hpp>
46 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
47 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
48 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
49 #include <com/sun/star/sdbcx/XDeleteRows.hpp>
50 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
51 #include <com/sun/star/uno/XNamingService.hpp>
52 #include <com/sun/star/util/XNumberFormats.hpp>
53 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
54 #include <com/sun/star/util/URLTransformer.hpp>
55 #include <com/sun/star/util/XURLTransformer.hpp>
56 #include <com/sun/star/view/XSelectionSupplier.hpp>
57 #include <comphelper/processfactory.hxx>
58 #include <comphelper/property.hxx>
59 #include <comphelper/string.hxx>
60 #include <comphelper/types.hxx>
61 #include <connectivity/dbtools.hxx>
62 #include <sfx2/dispatch.hxx>
63 #include <sfx2/viewfrm.hxx>
64 #include <svl/eitem.hxx>
65 #include <vcl/commandevent.hxx>
66 #include <vcl/fmtfield.hxx>
67 #include <vcl/svapp.hxx>
68 #include <svl/numuno.hxx>
69 #include <tools/debug.hxx>
70 #include <tools/multisel.hxx>
71 #include <tools/diagnose_ex.h>
72 #include <vcl/help.hxx>
73 #include <vcl/image.hxx>
74 #include <vcl/menu.hxx>
75 #include <vcl/settings.hxx>
76 #include <sal/log.hxx>
77 #include <i18nlangtag/languagetag.hxx>
78 #include <math.h>
79 #include <memory>
81 using namespace ::com::sun::star::uno;
82 using namespace ::com::sun::star::view;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::lang;
85 using namespace ::com::sun::star::sdbcx;
86 using namespace ::com::sun::star::sdbc;
87 using namespace ::com::sun::star::sdb;
88 using namespace ::com::sun::star::form;
89 using namespace ::com::sun::star::util;
90 using namespace ::com::sun::star::container;
91 using namespace ::cppu;
92 using namespace ::svxform;
93 using namespace ::svx;
94 using namespace ::dbtools;
96 struct FmGridHeaderData
98 ODataAccessDescriptor aDropData;
99 Point aDropPosPixel;
100 sal_Int8 nDropAction;
101 Reference< XInterface > xDroppedStatement;
102 Reference< XInterface > xDroppedResultSet;
105 static void SetMenuItem(const OUString& rImgID, const OString &rID, Menu& rMenu, bool bDesignMode)
107 Image aImage(StockImage::Yes, rImgID);
108 sal_uInt16 nID = rMenu.GetItemId(rID);
109 rMenu.SetItemImage(nID, aImage);
110 rMenu.EnableItem(nID, bDesignMode);
113 FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits)
114 :EditBrowserHeader(pParent, nWinBits)
115 ,DropTargetHelper(this)
116 ,m_pImpl(new FmGridHeaderData)
120 FmGridHeader::~FmGridHeader()
122 disposeOnce();
125 void FmGridHeader::dispose()
127 m_pImpl.reset();
128 DropTargetHelper::dispose();
129 svt::EditBrowserHeader::dispose();
132 sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const
134 return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId);
137 void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId)
139 sal_uInt16 nPos = GetModelColumnPos(nColumnId);
140 Reference< XIndexAccess > xColumns = static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns();
141 if ( nPos < xColumns->getCount() )
143 Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
144 if ( xSelSupplier.is() )
146 Reference< XPropertySet > xColumn;
147 xColumns->getByIndex(nPos) >>= xColumn;
148 xSelSupplier->select(makeAny(xColumn));
153 void FmGridHeader::Select()
155 EditBrowserHeader::Select();
156 notifyColumnSelect(GetCurItemId());
159 void FmGridHeader::RequestHelp( const HelpEvent& rHEvt )
161 sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
162 if ( nItemId )
164 if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
166 tools::Rectangle aItemRect = GetItemRect( nItemId );
167 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
168 aItemRect.SetLeft( aPt.X() );
169 aItemRect.SetTop( aPt.Y() );
170 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
171 aItemRect.SetRight( aPt.X() );
172 aItemRect.SetBottom( aPt.Y() );
174 sal_uInt16 nPos = GetModelColumnPos(nItemId);
175 Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
178 Reference< css::beans::XPropertySet > xColumn(xColumns->getByIndex(nPos),UNO_QUERY);
179 OUString aHelpText;
180 xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText;
181 if ( aHelpText.isEmpty() )
182 xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText;
183 if ( !aHelpText.isEmpty() )
185 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
186 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText );
187 else
188 Help::ShowQuickHelp( this, aItemRect, aHelpText );
189 return;
192 catch(Exception&)
194 return;
198 EditBrowserHeader::RequestHelp( rHEvt );
201 sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt )
203 // drop allowed in design mode only
204 if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
205 return DND_ACTION_NONE;
207 // search for recognized formats
208 const DataFlavorExVector& rFlavors = GetDataFlavorExVector();
209 if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::FIELD_DESCRIPTOR))
210 return rEvt.mnAction;
212 return DND_ACTION_NONE;
215 sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt )
217 if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
218 return DND_ACTION_NONE;
220 TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable);
222 // check the formats
223 bool bColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
224 bool bFieldDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
225 if (!bColumnDescriptor && !bFieldDescriptor)
227 OSL_FAIL("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!");
228 return DND_ACTION_NONE;
231 // extract the descriptor
232 OUString sDatasource, sCommand, sFieldName,sDatabaseLocation;
233 sal_Int32 nCommandType = CommandType::COMMAND;
234 Reference< XPreparedStatement > xStatement;
235 Reference< XResultSet > xResultSet;
236 Reference< XPropertySet > xField;
237 Reference< XConnection > xConnection;
239 ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData);
240 if (aColumn.has(DataAccessDescriptorProperty::DataSource)) aColumn[DataAccessDescriptorProperty::DataSource] >>= sDatasource;
241 if (aColumn.has(DataAccessDescriptorProperty::DatabaseLocation)) aColumn[DataAccessDescriptorProperty::DatabaseLocation] >>= sDatabaseLocation;
242 if (aColumn.has(DataAccessDescriptorProperty::Command)) aColumn[DataAccessDescriptorProperty::Command] >>= sCommand;
243 if (aColumn.has(DataAccessDescriptorProperty::CommandType)) aColumn[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
244 if (aColumn.has(DataAccessDescriptorProperty::ColumnName)) aColumn[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
245 if (aColumn.has(DataAccessDescriptorProperty::ColumnObject))aColumn[DataAccessDescriptorProperty::ColumnObject] >>= xField;
246 if (aColumn.has(DataAccessDescriptorProperty::Connection)) aColumn[DataAccessDescriptorProperty::Connection] >>= xConnection;
248 if ( sFieldName.isEmpty()
249 || sCommand.isEmpty()
250 || ( sDatasource.isEmpty()
251 && sDatabaseLocation.isEmpty()
252 && !xConnection.is()
256 OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
257 return DND_ACTION_NONE;
262 // need a connection
263 if (!xConnection.is())
264 { // the transferable did not contain the connection -> build an own one
267 OUString sSignificantSource( sDatasource.isEmpty() ? sDatabaseLocation : sDatasource );
268 xConnection = getConnection_withFeedback(sSignificantSource, OUString(), OUString(),
269 static_cast<FmGridControl*>(GetParent())->getContext(), nullptr );
271 catch(NoSuchElementException&)
272 { // allowed, means sDatasource isn't a valid data source name...
274 catch(Exception&)
276 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
279 if (!xConnection.is())
281 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
282 return DND_ACTION_NONE;
286 // try to obtain the column object
287 if (!xField.is())
289 #ifdef DBG_UTIL
290 Reference< XServiceInfo > xServiceInfo(xConnection, UNO_QUERY);
291 DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)");
292 #endif
294 Reference< XNameAccess > xFields;
295 switch (nCommandType)
297 case CommandType::TABLE:
299 Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
300 Reference< XColumnsSupplier > xSupplyColumns;
301 xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns;
302 xFields = xSupplyColumns->getColumns();
304 break;
305 case CommandType::QUERY:
307 Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
308 Reference< XColumnsSupplier > xSupplyColumns;
309 xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns;
310 xFields = xSupplyColumns->getColumns();
312 break;
313 default:
315 xStatement = xConnection->prepareStatement(sCommand);
316 // not interested in any results
318 Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY);
319 xStatProps->setPropertyValue("MaxRows", makeAny(sal_Int32(0)));
321 xResultSet = xStatement->executeQuery();
322 Reference< XColumnsSupplier > xSupplyCols(xResultSet, UNO_QUERY);
323 if (xSupplyCols.is())
324 xFields = xSupplyCols->getColumns();
328 if (xFields.is() && xFields->hasByName(sFieldName))
329 xFields->getByName(sFieldName) >>= xField;
331 if (!xField.is())
333 ::comphelper::disposeComponent(xStatement);
334 return DND_ACTION_NONE;
338 // do the drop asynchronously
339 // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu)
340 m_pImpl->aDropData = aColumn;
341 m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] <<= xConnection;
342 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] <<= xField;
344 m_pImpl->nDropAction = _rEvt.mnAction;
345 m_pImpl->aDropPosPixel = _rEvt.maPosPixel;
346 m_pImpl->xDroppedStatement = xStatement;
347 m_pImpl->xDroppedResultSet = xResultSet;
349 PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop), nullptr, true);
351 catch (Exception&)
353 OSL_FAIL("FmGridHeader::ExecuteDrop: caught an exception while creatin' the column !");
354 ::comphelper::disposeComponent(xStatement);
355 return DND_ACTION_NONE;
358 return DND_ACTION_LINK;
361 IMPL_LINK_NOARG( FmGridHeader, OnAsyncExecuteDrop, void*, void )
363 OUString sCommand, sFieldName,sURL;
364 sal_Int32 nCommandType = CommandType::COMMAND;
365 Reference< XPropertySet > xField;
366 Reference< XConnection > xConnection;
368 OUString sDatasource = m_pImpl->aDropData.getDataSource();
369 if ( sDatasource.isEmpty() && m_pImpl->aDropData.has(DataAccessDescriptorProperty::ConnectionResource) )
370 m_pImpl->aDropData[DataAccessDescriptorProperty::ConnectionResource] >>= sURL;
371 m_pImpl->aDropData[DataAccessDescriptorProperty::Command] >>= sCommand;
372 m_pImpl->aDropData[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
373 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
374 m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] >>= xConnection;
375 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] >>= xField;
379 // need number formats
380 Reference< XNumberFormatsSupplier > xSupplier = getNumberFormats(xConnection, true);
381 Reference< XNumberFormats > xNumberFormats;
382 if (xSupplier.is())
383 xNumberFormats = xSupplier->getNumberFormats();
384 if (!xNumberFormats.is())
386 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
387 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
388 return;
391 // The field now needs two pieces of information:
392 // a.) Name of the field for label and ControlSource
393 // b.) FormatKey, to determine which field is to be created
394 sal_Int32 nDataType = 0;
395 xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType;
396 // these datatypes can not be processed in Gridcontrol
397 switch (nDataType)
399 case DataType::BLOB:
400 case DataType::LONGVARBINARY:
401 case DataType::BINARY:
402 case DataType::VARBINARY:
403 case DataType::OTHER:
404 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
405 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
406 return;
409 // Creating the column
410 Reference< XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
411 Reference< XGridColumnFactory > xFactory(xCols, UNO_QUERY);
413 sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel);
414 // insert position, always before the current column
415 sal_uInt16 nPos = GetModelColumnPos(nColId);
416 Reference< XPropertySet > xCol, xSecondCol;
418 // Create Column based on type, default textfield
419 std::vector<OString> aPossibleTypes;
420 std::vector<OUString> aImgResId;
421 switch (nDataType)
423 case DataType::BIT:
424 case DataType::BOOLEAN:
425 aPossibleTypes.emplace_back(FM_COL_CHECKBOX);
426 aImgResId.emplace_back(RID_SVXBMP_CHECKBOX);
427 break;
428 case DataType::TINYINT:
429 case DataType::SMALLINT:
430 case DataType::INTEGER:
431 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
432 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
433 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
434 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
435 break;
436 case DataType::REAL:
437 case DataType::DOUBLE:
438 case DataType::NUMERIC:
439 case DataType::DECIMAL:
440 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
441 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
442 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
443 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
444 break;
445 case DataType::TIMESTAMP:
446 aPossibleTypes.emplace_back("dateandtimefield");
447 aImgResId.emplace_back(RID_SVXBMP_DATE_N_TIME_FIELDS);
448 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
449 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
450 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
451 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
452 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
453 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
454 break;
455 case DataType::DATE:
456 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
457 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
458 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
459 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
460 break;
461 case DataType::TIME:
462 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
463 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
464 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
465 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
466 break;
467 case DataType::CHAR:
468 case DataType::VARCHAR:
469 case DataType::LONGVARCHAR:
470 default:
471 aPossibleTypes.emplace_back(FM_COL_TEXTFIELD);
472 aImgResId.emplace_back(RID_SVXBMP_EDITBOX);
473 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
474 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
475 break;
477 // if it's a currency field, a "currency field" option
480 if ( ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField)
481 && ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY)))
483 aPossibleTypes.insert(aPossibleTypes.begin(), FM_COL_CURRENCYFIELD);
484 aImgResId.insert(aImgResId.begin(), RID_SVXBMP_CURRENCYFIELD);
487 catch (const Exception&)
489 OSL_FAIL("FmGridHeader::ExecuteDrop: Exception occurred!");
492 assert(aPossibleTypes.size() == aImgResId.size());
494 bool bDateNTimeCol = false;
495 if (!aPossibleTypes.empty())
497 OString sPreferredType = aPossibleTypes[0];
498 if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.size() > 1))
500 VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/colsmenu.ui", "");
501 VclPtr<PopupMenu> aInsertMenu(aBuilder.get_menu("menu"));
502 PopupMenu* pTypeMenu = aInsertMenu->GetPopupMenu(aInsertMenu->GetItemId("insert"));
503 pTypeMenu->ShowItem(pTypeMenu->GetItemId("dateandtimefield"));
504 std::vector<OString>::const_iterator iter;
505 std::vector<OUString>::const_iterator imgiter;
506 for (iter = aPossibleTypes.begin(), imgiter = aImgResId.begin();
507 iter != aPossibleTypes.end(); ++iter, ++imgiter)
509 SetMenuItem(*imgiter, *iter, *pTypeMenu, true);
511 if (pTypeMenu->Execute(this, m_pImpl->aDropPosPixel))
512 sPreferredType = pTypeMenu->GetCurItemIdent();
515 bDateNTimeCol = sPreferredType == "dateandtimefield";
516 sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1;
517 OUString sFieldService;
518 while (nColCount--)
520 if (bDateNTimeCol)
521 sPreferredType = nColCount ? FM_COL_DATEFIELD : FM_COL_TIMEFIELD;
523 sFieldService = OUString::fromUtf8(sPreferredType);
524 Reference< XPropertySet > xThisRoundCol;
525 if ( !sFieldService.isEmpty() )
526 xThisRoundCol = xFactory->createColumn(sFieldService);
527 if (nColCount)
528 xSecondCol = xThisRoundCol;
529 else
530 xCol = xThisRoundCol;
534 if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is()))
536 ::comphelper::disposeComponent(xCol); // in case only the creation of the second column failed
537 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
538 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
539 return;
542 if (bDateNTimeCol)
544 OUString sTimePostfix(SvxResId(RID_STR_POSTFIX_TIME));
545 xCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sTimePostfix ) ) );
547 OUString sDatePostfix(SvxResId( RID_STR_POSTFIX_DATE));
548 xSecondCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sDatePostfix ) ) );
550 else
551 xCol->setPropertyValue(FM_PROP_LABEL, makeAny(sFieldName));
553 // insert now
554 Any aElement;
555 aElement <<= xCol;
557 xCols->insertByIndex(nPos, aElement);
559 FormControlFactory aControlFactory;
560 aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol );
561 FormControlFactory::initializeFieldDependentProperties( xField, xCol, xNumberFormats );
563 xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
564 if ( xSecondCol.is() )
565 xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
567 if (bDateNTimeCol)
569 OUString aPostfix[] = {
570 SvxResId(RID_STR_POSTFIX_DATE),
571 SvxResId(RID_STR_POSTFIX_TIME)
574 for ( size_t i=0; i<2; ++i )
576 OUString sPurePostfix = comphelper::string::stripStart(aPostfix[i], ' ');
577 sPurePostfix = comphelper::string::stripStart(sPurePostfix, '(');
578 sPurePostfix = comphelper::string::stripEnd(sPurePostfix, ')');
579 OUString sRealName = sFieldName + "_" + sPurePostfix;
580 if (i)
581 xSecondCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
582 else
583 xCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
586 else
587 xCol->setPropertyValue(FM_PROP_NAME, makeAny(sFieldName));
589 if (bDateNTimeCol)
591 aElement <<= xSecondCol;
592 xCols->insertByIndex(nPos == sal_uInt16(-1) ? nPos : ++nPos, aElement);
595 // is the component::Form tied to the database?
596 Reference< XFormComponent > xFormCp(xCols, UNO_QUERY);
597 Reference< XPropertySet > xForm(xFormCp->getParent(), UNO_QUERY);
598 if (xForm.is())
600 if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).isEmpty())
602 if ( !sDatasource.isEmpty() )
603 xForm->setPropertyValue(FM_PROP_DATASOURCE, makeAny(sDatasource));
604 else
605 xForm->setPropertyValue(FM_PROP_URL, makeAny(sURL));
608 if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).isEmpty())
610 xForm->setPropertyValue(FM_PROP_COMMAND, makeAny(sCommand));
611 Any aCommandType;
612 switch (nCommandType)
614 case CommandType::TABLE:
615 aCommandType <<= sal_Int32(CommandType::TABLE);
616 break;
617 case CommandType::QUERY:
618 aCommandType <<= sal_Int32(CommandType::QUERY);
619 break;
620 default:
621 aCommandType <<= sal_Int32(CommandType::COMMAND);
622 xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, css::uno::Any(2 == nCommandType));
623 break;
625 xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType);
629 catch (Exception&)
631 OSL_FAIL("FmGridHeader::OnAsyncExecuteDrop: caught an exception while creatin' the column !");
632 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
633 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
634 return;
637 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
638 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
641 void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, PopupMenu& rMenu)
643 bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode();
645 Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
646 // Building of the Insert Menu
647 // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND
648 if(nColId > 0)
650 sal_uInt16 nPos2 = GetModelColumnPos(nColId);
652 Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
653 Reference< css::beans::XPropertySet> xColumn( xColumns->getByIndex(nPos2), css::uno::UNO_QUERY);
654 Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
655 if (xSelSupplier.is())
656 xSelSupplier->select(makeAny(xColumn));
659 // insert position, always before the current column
660 sal_uInt16 nPos = GetModelColumnPos(nColId);
661 bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId);
663 PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("insert"));
664 if (pMenu)
666 SetMenuItem(RID_SVXBMP_EDITBOX, FM_COL_TEXTFIELD, *pMenu, bDesignMode);
667 SetMenuItem(RID_SVXBMP_CHECKBOX, FM_COL_CHECKBOX, *pMenu, bDesignMode);
668 SetMenuItem(RID_SVXBMP_COMBOBOX, FM_COL_COMBOBOX, *pMenu, bDesignMode);
669 SetMenuItem(RID_SVXBMP_LISTBOX, FM_COL_LISTBOX, *pMenu, bDesignMode);
670 SetMenuItem(RID_SVXBMP_DATEFIELD, FM_COL_DATEFIELD, *pMenu, bDesignMode);
671 SetMenuItem(RID_SVXBMP_TIMEFIELD, FM_COL_TIMEFIELD, *pMenu, bDesignMode);
672 SetMenuItem(RID_SVXBMP_NUMERICFIELD, FM_COL_NUMERICFIELD, *pMenu, bDesignMode);
673 SetMenuItem(RID_SVXBMP_CURRENCYFIELD, FM_COL_CURRENCYFIELD, *pMenu, bDesignMode);
674 SetMenuItem(RID_SVXBMP_PATTERNFIELD, FM_COL_PATTERNFIELD, *pMenu, bDesignMode);
675 SetMenuItem(RID_SVXBMP_FORMATTEDFIELD, FM_COL_FORMATTEDFIELD, *pMenu, bDesignMode);
678 if (pMenu && xCols.is() && nColId)
680 Reference< css::beans::XPropertySet > xPropSet( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
682 Reference< css::io::XPersistObject > xServiceQuestion(xPropSet, UNO_QUERY);
683 sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0;
684 if (nColType == TYPE_TEXTFIELD)
685 { // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD
686 // in both cases. And as columns don't have a css::lang::XServiceInfo interface, we have to distinguish both
687 // types via the existence of special properties
688 if (xPropSet.is())
690 Reference< css::beans::XPropertySetInfo > xPropsInfo = xPropSet->getPropertySetInfo();
691 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
692 nColType = TYPE_FORMATTEDFIELD;
696 PopupMenu* pControlMenu = rMenu.GetPopupMenu(rMenu.GetItemId("change"));
697 if (pControlMenu)
699 SetMenuItem(RID_SVXBMP_EDITBOX, FM_COL_TEXTFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_TEXTFIELD));
700 SetMenuItem(RID_SVXBMP_CHECKBOX, FM_COL_CHECKBOX"1", *pControlMenu, bDesignMode && (nColType != TYPE_CHECKBOX));
701 SetMenuItem(RID_SVXBMP_COMBOBOX, FM_COL_COMBOBOX"1", *pControlMenu, bDesignMode && (nColType != TYPE_COMBOBOX));
702 SetMenuItem(RID_SVXBMP_LISTBOX, FM_COL_LISTBOX"1", *pControlMenu, bDesignMode && (nColType != TYPE_LISTBOX));
703 SetMenuItem(RID_SVXBMP_DATEFIELD, FM_COL_DATEFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_DATEFIELD));
704 SetMenuItem(RID_SVXBMP_TIMEFIELD, FM_COL_TIMEFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_TIMEFIELD));
705 SetMenuItem(RID_SVXBMP_NUMERICFIELD, FM_COL_NUMERICFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_NUMERICFIELD));
706 SetMenuItem(RID_SVXBMP_CURRENCYFIELD, FM_COL_CURRENCYFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_CURRENCYFIELD));
707 SetMenuItem(RID_SVXBMP_PATTERNFIELD, FM_COL_PATTERNFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_PATTERNFIELD));
708 SetMenuItem(RID_SVXBMP_FORMATTEDFIELD, FM_COL_FORMATTEDFIELD"1", *pControlMenu, bDesignMode && (nColType != TYPE_FORMATTEDFIELD));
710 rMenu.EnableItem(rMenu.GetItemId("change"), bDesignMode && bMarked && xCols.is());
712 else
713 rMenu.EnableItem(rMenu.GetItemId("change"), false);
715 rMenu.EnableItem(rMenu.GetItemId("insert"), bDesignMode && xCols.is());
716 rMenu.EnableItem(rMenu.GetItemId("delete"), bDesignMode && bMarked && xCols.is());
717 rMenu.EnableItem(rMenu.GetItemId("column"), bDesignMode && bMarked && xCols.is());
719 PopupMenu* pShowColsMenu = rMenu.GetPopupMenu(rMenu.GetItemId("show"));
720 sal_uInt16 nHiddenCols = 0;
721 if (pShowColsMenu)
723 if (xCols.is())
725 // check for hidden cols
726 Reference< css::beans::XPropertySet > xCurCol;
727 Any aHidden,aName;
728 for (sal_Int32 i=0; i<xCols->getCount(); ++i)
730 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
731 DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !");
732 aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
733 DBG_ASSERT(aHidden.getValueType().getTypeClass() == TypeClass_BOOLEAN,
734 "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !");
735 if (::comphelper::getBOOL(aHidden))
737 // put the column name into the 'show col' menu
738 if (nHiddenCols < 16)
739 { // (only the first 16 items to keep the menu rather small)
740 aName = xCurCol->getPropertyValue(FM_PROP_LABEL);
741 pShowColsMenu->InsertItem(nHiddenCols + 1, ::comphelper::getString(aName),
742 MenuItemBits::NONE, OString(), nHiddenCols);
743 // the ID is arbitrary, but should be unique within the whole menu
745 ++nHiddenCols;
749 pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("more"), xCols.is() && (nHiddenCols > 16));
750 pShowColsMenu->EnableItem(pShowColsMenu->GetItemId("all"), xCols.is() && (nHiddenCols > 0));
753 // allow the 'hide column' item ?
754 bool bAllowHide = bMarked; // a column is marked
755 bAllowHide = bAllowHide || (!bDesignMode && (nPos != sal_uInt16(-1))); // OR we are in alive mode and have hit a column
756 bAllowHide = bAllowHide && xCols.is(); // AND we have a column container
757 bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1); // AND there are at least two visible columns
758 rMenu.EnableItem(rMenu.GetItemId("hide"), bAllowHide);
760 if (bMarked)
763 SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
764 SfxItemState eState = SfxItemState::UNKNOWN;
765 // ask the bindings of the current view frame (which should be the one we're residing in) for the state
766 if (pCurrentFrame)
768 std::unique_ptr<SfxPoolItem> pItem;
769 eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem);
771 if (eState >= SfxItemState::DEFAULT && pItem != nullptr)
773 bool bChecked = dynamic_cast<const SfxBoolItem*>( pItem.get()) != nullptr && static_cast<SfxBoolItem*>(pItem.get())->GetValue();
774 rMenu.CheckItem("column", bChecked);
780 enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone };
782 void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const PopupMenu& rMenu, sal_uInt16 nExecutionResult)
784 Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
785 sal_uInt16 nPos = GetModelColumnPos(nColId);
787 OUString aFieldType;
788 bool bReplace = false;
789 InspectorAction eInspectorAction = eNone;
791 OString sExecutionResult = rMenu.GetCurItemIdent();
792 if (sExecutionResult.isEmpty())
794 PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("insert"));
795 if (pMenu)
796 sExecutionResult = pMenu->GetCurItemIdent();
798 if (sExecutionResult.isEmpty())
800 PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("change"));
801 if (pMenu)
802 sExecutionResult = pMenu->GetCurItemIdent();
804 if (sExecutionResult.isEmpty())
806 PopupMenu* pMenu = rMenu.GetPopupMenu(rMenu.GetItemId("show"));
807 if (pMenu)
808 sExecutionResult = pMenu->GetCurItemIdent();
811 if (sExecutionResult == "delete")
813 Reference< XInterface > xCol(
814 xCols->getByIndex(nPos), css::uno::UNO_QUERY);
815 xCols->removeByIndex(nPos);
816 ::comphelper::disposeComponent(xCol);
818 else if (sExecutionResult == "hide")
820 Reference< css::beans::XPropertySet > xCurCol( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
821 xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(true));
823 else if (sExecutionResult == "column")
825 eInspectorAction = rMenu.IsItemChecked(rMenu.GetItemId("column")) ? eOpenInspector : eCloseInspector;
827 else if (sExecutionResult.startsWith(FM_COL_TEXTFIELD))
829 if (sExecutionResult != FM_COL_TEXTFIELD)
830 bReplace = true;
831 aFieldType = FM_COL_TEXTFIELD;
833 else if (sExecutionResult.startsWith(FM_COL_COMBOBOX))
835 if (sExecutionResult != FM_COL_COMBOBOX)
836 bReplace = true;
837 aFieldType = FM_COL_COMBOBOX;
839 else if (sExecutionResult.startsWith(FM_COL_LISTBOX))
841 if (sExecutionResult != FM_COL_LISTBOX)
842 bReplace = true;
843 aFieldType = FM_COL_LISTBOX;
845 else if (sExecutionResult.startsWith(FM_COL_CHECKBOX))
847 if (sExecutionResult != FM_COL_CHECKBOX)
848 bReplace = true;
849 aFieldType = FM_COL_CHECKBOX;
851 else if (sExecutionResult.startsWith(FM_COL_DATEFIELD))
853 if (sExecutionResult != FM_COL_DATEFIELD)
854 bReplace = true;
855 aFieldType = FM_COL_DATEFIELD;
857 else if (sExecutionResult.startsWith(FM_COL_TIMEFIELD))
859 if (sExecutionResult != FM_COL_TIMEFIELD)
860 bReplace = true;
861 aFieldType = FM_COL_TIMEFIELD;
863 else if (sExecutionResult.startsWith(FM_COL_NUMERICFIELD))
865 if (sExecutionResult != FM_COL_NUMERICFIELD)
866 bReplace = true;
867 aFieldType = FM_COL_NUMERICFIELD;
869 else if (sExecutionResult.startsWith(FM_COL_CURRENCYFIELD))
871 if (sExecutionResult != FM_COL_CURRENCYFIELD)
872 bReplace = true;
873 aFieldType = FM_COL_CURRENCYFIELD;
875 else if (sExecutionResult.startsWith(FM_COL_PATTERNFIELD))
877 if (sExecutionResult != FM_COL_PATTERNFIELD)
878 bReplace = true;
879 aFieldType = FM_COL_PATTERNFIELD;
881 else if (sExecutionResult.startsWith(FM_COL_FORMATTEDFIELD))
883 if (sExecutionResult != FM_COL_FORMATTEDFIELD)
884 bReplace = true;
885 aFieldType = FM_COL_FORMATTEDFIELD;
887 else if (sExecutionResult == "more")
889 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
890 ScopedVclPtr<AbstractFmShowColsDialog> pDlg(pFact->CreateFmShowColsDialog(GetFrameWeld()));
891 pDlg->SetColumns(xCols);
892 pDlg->Execute();
894 else if (sExecutionResult == "all")
896 // just iterate through all the cols ...
897 Reference< css::beans::XPropertySet > xCurCol;
898 for (sal_Int32 i=0; i<xCols->getCount(); ++i)
900 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
901 xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
903 // TODO : there must be a more clever way to do this...
904 // with the above the view is updated after every single model update ...
906 else if (nExecutionResult>0 && nExecutionResult<=16)
907 { // it was a "show column/<colname>" command (there are at most 16 such items)
908 // search the nExecutionResult'th hidden col
909 Reference< css::beans::XPropertySet > xCurCol;
910 for (sal_Int32 i=0; i<xCols->getCount() && nExecutionResult; ++i)
912 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
913 Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
914 if (::comphelper::getBOOL(aHidden))
915 if (!--nExecutionResult)
917 xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
918 break;
923 if ( !aFieldType.isEmpty() )
927 Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW );
928 Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW );
930 if ( bReplace )
932 // rescue over a few properties
933 Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY );
935 TransferFormComponentProperties(
936 xReplaced, xNewCol, Application::GetSettings().GetUILanguageTag().getLocale() );
938 xCols->replaceByIndex( nPos, makeAny( xNewCol ) );
939 ::comphelper::disposeComponent( xReplaced );
941 eInspectorAction = eUpdateInspector;
943 else
945 FormControlFactory factory;
947 OUString sLabel = FormControlFactory::getDefaultUniqueName_ByComponentType(
948 Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol );
949 xNewCol->setPropertyValue( FM_PROP_LABEL, makeAny( sLabel ) );
950 xNewCol->setPropertyValue( FM_PROP_NAME, makeAny( sLabel ) );
952 factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol );
954 xCols->insertByIndex( nPos, makeAny( xNewCol ) );
957 catch( const Exception& )
959 DBG_UNHANDLED_EXCEPTION("svx");
963 SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
964 OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" );
965 if ( pCurrentFrame )
967 if ( eInspectorAction == eUpdateInspector )
969 if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) )
970 eInspectorAction = eNone;
973 if ( eInspectorAction != eNone )
975 SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction != eCloseInspector );
977 pCurrentFrame->GetBindings().GetDispatcher()->ExecuteList(
978 SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON,
979 { &aShowItem });
984 void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos )
986 // the affected col
987 sal_uInt16 nColId = GetItemId( _rPreferredPos );
989 // the menu
990 VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "svx/ui/colsmenu.ui", "");
991 VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu"));
993 // let derivatives modify the menu
994 PreExecuteColumnContextMenu( nColId, *aContextMenu );
995 aContextMenu->RemoveDisabledEntries( true, true );
997 // execute the menu
998 sal_uInt16 nResult = aContextMenu->Execute( this, _rPreferredPos );
1000 // let derivatives handle the result
1001 PostExecuteColumnContextMenu( nColId, *aContextMenu, nResult );
1004 void FmGridHeader::Command(const CommandEvent& rEvt)
1006 switch (rEvt.GetCommand())
1008 case CommandEventId::ContextMenu:
1010 if (!rEvt.IsMouseEvent())
1011 return;
1013 triggerColumnContextMenu( rEvt.GetMousePosPixel() );
1015 break;
1016 default:
1017 EditBrowserHeader::Command(rEvt);
1021 FmGridControl::FmGridControl(
1022 const Reference< css::uno::XComponentContext >& _rxContext,
1023 vcl::Window* pParent,
1024 FmXGridPeer* _pPeer,
1025 WinBits nBits)
1026 :DbGridControl(_rxContext, pParent, nBits)
1027 ,m_pPeer(_pPeer)
1028 ,m_nCurrentSelectedColumn(-1)
1029 ,m_nMarkedColumnId(BROWSER_INVALIDID)
1030 ,m_bSelecting(false)
1031 ,m_bInColumnMove(false)
1033 EnableInteractiveRowHeight( );
1036 void FmGridControl::Command(const CommandEvent& _rEvt)
1038 if ( CommandEventId::ContextMenu == _rEvt.GetCommand() )
1040 FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() );
1041 if ( pMyHeader && !_rEvt.IsMouseEvent() )
1042 { // context menu requested by keyboard
1043 if ( 1 == GetSelectColumnCount() || IsDesignMode() )
1045 sal_uInt16 nSelId = GetColumnId(
1046 sal::static_int_cast< sal_uInt16 >( FirstSelectedColumn() ) );
1047 ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
1049 Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) );
1050 pMyHeader->triggerColumnContextMenu(aRelativePos);
1052 // handled
1053 return;
1058 DbGridControl::Command( _rEvt );
1061 // css::beans::XPropertyChangeListener
1062 void FmGridControl::propertyChange(const css::beans::PropertyChangeEvent& evt)
1064 if (evt.PropertyName == FM_PROP_ROWCOUNT)
1066 // if we're not in the main thread call AdjustRows asynchronously
1067 implAdjustInSolarThread(true);
1068 return;
1071 const DbGridRowRef& xRow = GetCurrentRow();
1072 // no adjustment of the properties is carried out during positioning
1073 Reference<XPropertySet> xSet(evt.Source,UNO_QUERY);
1074 if (xRow.is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark())))
1076 if (evt.PropertyName == FM_PROP_ISMODIFIED)
1078 // modified or clean ?
1079 GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean;
1080 if (eStatus != xRow->GetStatus())
1082 xRow->SetStatus(eStatus);
1083 SolarMutexGuard aGuard;
1084 RowModified(GetCurrentPos());
1090 void FmGridControl::SetDesignMode(bool bMode)
1092 bool bOldMode = IsDesignMode();
1093 DbGridControl::SetDesignMode(bMode);
1094 if (bOldMode != bMode)
1096 if (!bMode)
1098 // cancel selection
1099 markColumn(USHRT_MAX);
1101 else
1103 Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
1104 Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
1105 if (xSelSupplier.is())
1107 Any aSelection = xSelSupplier->getSelection();
1108 Reference< css::beans::XPropertySet > xColumn;
1109 if (aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE)
1110 xColumn.set(aSelection, css::uno::UNO_QUERY);
1111 Reference< XInterface > xCurrent;
1112 for (sal_Int32 i=0; i<xColumns->getCount(); ++i)
1114 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1115 if (xCurrent == xColumn)
1117 markColumn(GetColumnIdFromModelPos(i));
1118 break;
1126 void FmGridControl::DeleteSelectedRows()
1128 if (!m_pSeekCursor)
1129 return;
1131 // how many rows are selected?
1132 sal_Int32 nSelectedRows = GetSelectRowCount();
1134 // the current line should be deleted but it is currently in edit mode
1135 if ( IsCurrentAppending() )
1136 return;
1137 // is the insert row selected
1138 if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
1139 nSelectedRows -= 1;
1141 // nothing to do
1142 if (nSelectedRows <= 0)
1143 return;
1145 // try to confirm the delete
1146 Reference< css::frame::XDispatchProvider > xDispatcher = static_cast<css::frame::XDispatchProvider*>(GetPeer());
1147 if (xDispatcher.is())
1149 css::util::URL aUrl;
1150 aUrl.Complete = FMURL_CONFIRM_DELETION;
1151 Reference< css::util::XURLTransformer > xTransformer(
1152 css::util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
1153 xTransformer->parseStrict( aUrl );
1155 Reference< css::frame::XDispatch > xDispatch = xDispatcher->queryDispatch(aUrl, OUString(), 0);
1156 Reference< css::form::XConfirmDeleteListener > xConfirm(xDispatch, UNO_QUERY);
1157 if (xConfirm.is())
1159 css::sdb::RowChangeEvent aEvent;
1160 aEvent.Source = Reference< XInterface >(*getDataSource());
1161 aEvent.Rows = nSelectedRows;
1162 aEvent.Action = css::sdb::RowChangeAction::DELETE;
1163 if (!xConfirm->confirmDelete(aEvent))
1164 return;
1168 const MultiSelection* pRowSelection = GetSelection();
1169 if ( pRowSelection && pRowSelection->IsAllSelected() )
1171 BeginCursorAction();
1172 CursorWrapper* pCursor = getDataSource();
1173 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*pCursor), UNO_QUERY);
1176 pCursor->beforeFirst();
1177 while( pCursor->next() )
1178 xUpdateCursor->deleteRow();
1180 SetUpdateMode(false);
1181 SetNoSelection();
1183 xUpdateCursor->moveToInsertRow();
1185 catch(const Exception&)
1187 OSL_FAIL("Exception caught while deleting rows!");
1189 // adapt to the data cursor
1190 AdjustDataSource(true);
1191 EndCursorAction();
1192 SetUpdateMode(true);
1194 else
1196 Reference< css::sdbcx::XDeleteRows > xDeleteThem(Reference< XInterface >(*getDataSource()), UNO_QUERY);
1198 // collect the bookmarks of the selected rows
1199 Sequence < Any> aBookmarks = getSelectionBookmarks();
1201 // determine the next row to position after deletion
1202 Any aBookmark;
1203 bool bNewPos = false;
1204 // if the current row isn't selected we take the row as row after deletion
1205 OSL_ENSURE( GetCurrentRow().is(), "FmGridControl::DeleteSelectedRows: no current row here?" );
1206 // crash reports suggest it can happen we don't have a current row - how?
1207 // #154303# / 2008-04-23 / frank.schoenheit@sun.com
1208 if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().is() )
1210 aBookmark = GetCurrentRow()->GetBookmark();
1211 bNewPos = true;
1213 else
1215 // we look for the first row after the selected block for selection
1216 long nIdx = LastSelectedRow() + 1;
1217 if (nIdx < GetRowCount() - 1)
1219 // there is a next row to position on
1220 if (SeekCursor(nIdx))
1222 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1224 bNewPos = true;
1225 // if it's not the row for inserting we keep the bookmark
1226 if (!IsInsertionRow(nIdx))
1227 aBookmark = m_pSeekCursor->getBookmark();
1230 else
1232 // we look for the first row before the selected block for selection after deletion
1233 nIdx = FirstSelectedRow() - 1;
1234 if (nIdx >= 0 && SeekCursor(nIdx))
1236 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1238 bNewPos = true;
1239 aBookmark = m_pSeekCursor->getBookmark();
1244 // Are all rows selected?
1245 // Second condition if no insertion line exists
1246 bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows;
1248 BeginCursorAction();
1250 // now delete the row
1251 Sequence<sal_Int32> aDeletedRows;
1252 SetUpdateMode( false );
1255 aDeletedRows = xDeleteThem->deleteRows(aBookmarks);
1257 catch(SQLException&)
1260 SetUpdateMode( true );
1262 // how many rows are deleted?
1263 sal_Int32 nDeletedRows = static_cast<sal_Int32>(std::count_if(aDeletedRows.begin(), aDeletedRows.end(),
1264 [](const sal_Int32 nRow) { return nRow != 0; }));
1266 // have rows been deleted?
1267 if (nDeletedRows)
1269 SetUpdateMode(false);
1270 SetNoSelection();
1273 // did we delete all the rows than try to move to the next possible row
1274 if (nDeletedRows == aDeletedRows.getLength())
1276 // there exists a new position to move on
1277 if (bNewPos)
1279 if (aBookmark.hasValue())
1280 getDataSource()->moveToBookmark(aBookmark);
1281 // no valid bookmark so move to the insert row
1282 else
1284 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1285 xUpdateCursor->moveToInsertRow();
1288 else
1290 Reference< css::beans::XPropertySet > xSet(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1292 sal_Int32 nRecordCount(0);
1293 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1294 if ( m_pDataCursor->rowDeleted() )
1295 --nRecordCount;
1297 // there are no rows left and we have an insert row
1298 if (!nRecordCount && GetEmptyRow().is())
1300 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1301 xUpdateCursor->moveToInsertRow();
1303 else if (nRecordCount)
1304 // move to the first row
1305 getDataSource()->first();
1308 // not all the rows where deleted, so move to the first row which remained in the resultset
1309 else
1311 auto pRow = std::find(aDeletedRows.begin(), aDeletedRows.end(), 0);
1312 if (pRow != aDeletedRows.end())
1314 auto i = static_cast<sal_Int32>(std::distance(aDeletedRows.begin(), pRow));
1315 getDataSource()->moveToBookmark(aBookmarks[i]);
1319 catch(const Exception&)
1323 // positioning went wrong so try to move to the first row
1324 getDataSource()->first();
1326 catch(const Exception&)
1331 // adapt to the data cursor
1332 AdjustDataSource(true);
1334 // not all rows could be deleted;
1335 // never select again there the ones that could not be deleted
1336 if (nDeletedRows < nSelectedRows)
1338 // were all selected
1339 if (bAllSelected)
1341 SelectAll();
1342 if (IsInsertionRow(GetRowCount() - 1)) // not the insertion row
1343 SelectRow(GetRowCount() - 1, false);
1345 else
1347 // select the remaining rows
1348 for (const sal_Int32 nSuccess : aDeletedRows)
1352 if (!nSuccess)
1354 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
1355 SetSeekPos(m_pSeekCursor->getRow() - 1);
1356 SelectRow(GetSeekPos());
1359 catch(const Exception&)
1361 // keep the seekpos in all cases
1362 SetSeekPos(m_pSeekCursor->getRow() - 1);
1368 EndCursorAction();
1369 SetUpdateMode(true);
1371 else // row could not be deleted
1373 EndCursorAction();
1376 // currentrow is the insert row?
1377 if (!IsCurrentAppending())
1378 getDataSource()->refreshRow();
1380 catch(const Exception&)
1386 // if there is no selection anymore we can start editing
1387 if (!GetSelectRowCount())
1388 ActivateCell();
1391 // XCurrentRecordListener
1392 void FmGridControl::positioned()
1394 SAL_INFO("svx.fmcomp", "FmGridControl::positioned");
1395 // position on the data source (force it to be done in the main thread)
1396 implAdjustInSolarThread(false);
1399 bool FmGridControl::commit()
1401 // execute commit only if an update is not already executed by the
1402 // css::form::component::GridControl
1403 if (!IsUpdating())
1405 if (Controller().is() && Controller()->IsModified())
1407 if (!SaveModified())
1408 return false;
1411 return true;
1414 void FmGridControl::inserted()
1416 const DbGridRowRef& xRow = GetCurrentRow();
1417 if (!xRow.is())
1418 return;
1420 // line has been inserted, then reset the status and mode
1421 xRow->SetState(m_pDataCursor.get(), false);
1422 xRow->SetNew(false);
1426 VclPtr<BrowserHeader> FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
1428 DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" );
1429 return VclPtr<FmGridHeader>::Create( pParent );
1432 void FmGridControl::markColumn(sal_uInt16 nId)
1434 if (GetHeaderBar() && m_nMarkedColumnId != nId)
1436 // deselect
1437 if (m_nMarkedColumnId != BROWSER_INVALIDID)
1439 HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HeaderBarItemBits::FLAT;
1440 GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits);
1444 if (nId != BROWSER_INVALIDID)
1446 HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HeaderBarItemBits::FLAT;
1447 GetHeaderBar()->SetItemBits(nId, aBits);
1449 m_nMarkedColumnId = nId;
1453 bool FmGridControl::isColumnMarked(sal_uInt16 nId) const
1455 return m_nMarkedColumnId == nId;
1458 long FmGridControl::QueryMinimumRowHeight()
1460 long const nMinimalLogicHeight = 20; // 0.2 cm
1461 long nMinimalPixelHeight = LogicToPixel(Point(0, nMinimalLogicHeight), MapMode(MapUnit::Map10thMM)).Y();
1462 return CalcZoom( nMinimalPixelHeight );
1465 void FmGridControl::RowHeightChanged()
1467 DbGridControl::RowHeightChanged();
1469 Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY );
1470 DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" );
1471 if ( xModel.is() )
1475 sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() );
1476 Any aProperty = makeAny( static_cast<sal_Int32>(PixelToLogic( Point(0, nUnzoomedPixelHeight), MapMode(MapUnit::Map10thMM)).Y()) );
1477 xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty );
1479 catch( const Exception& )
1481 OSL_FAIL( "FmGridControl::RowHeightChanged: caught an exception!" );
1486 void FmGridControl::ColumnResized(sal_uInt16 nId)
1488 DbGridControl::ColumnResized(nId);
1490 // transfer value to the model
1491 DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1492 const Reference< css::beans::XPropertySet >& xColModel(pCol->getModel());
1493 if (xColModel.is())
1495 Any aWidth;
1496 sal_Int32 nColumnWidth = GetColumnWidth(nId);
1497 nColumnWidth = CalcReverseZoom(nColumnWidth);
1498 // convert to 10THMM
1499 aWidth <<= static_cast<sal_Int32>(PixelToLogic(Point(nColumnWidth, 0), MapMode(MapUnit::Map10thMM)).X());
1500 xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth);
1504 void FmGridControl::CellModified()
1506 DbGridControl::CellModified();
1507 GetPeer()->CellModified();
1510 void FmGridControl::BeginCursorAction()
1512 DbGridControl::BeginCursorAction();
1513 m_pPeer->stopCursorListening();
1516 void FmGridControl::EndCursorAction()
1518 m_pPeer->startCursorListening();
1519 DbGridControl::EndCursorAction();
1522 void FmGridControl::ColumnMoved(sal_uInt16 nId)
1524 m_bInColumnMove = true;
1526 DbGridControl::ColumnMoved(nId);
1527 Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
1529 if (xColumns.is())
1531 // locate the column and move in the model;
1532 // get ColumnPos
1533 DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1534 Reference< css::beans::XPropertySet > xCol;
1536 // inserting must be based on the column positions
1537 sal_Int32 i;
1538 Reference< XInterface > xCurrent;
1539 for (i = 0; !xCol.is() && i < xColumns->getCount(); i++)
1541 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1542 if (xCurrent == pCol->getModel())
1544 xCol = pCol->getModel();
1545 break;
1549 DBG_ASSERT(i < xColumns->getCount(), "Wrong css::sdbcx::Index");
1550 xColumns->removeByIndex(i);
1551 Any aElement;
1552 aElement <<= xCol;
1553 xColumns->insertByIndex(GetModelColumnPos(nId), aElement);
1554 pCol->setModel(xCol);
1555 // if the column which is shown here is selected ...
1556 if ( isColumnSelected(pCol) )
1557 markColumn(nId); // ... -> mark it
1560 m_bInColumnMove = false;
1563 void FmGridControl::InitColumnsByModels(const Reference< css::container::XIndexContainer >& xColumns)
1565 // reset columns;
1566 // if there is only one HandleColumn, then don't
1567 if (GetModelColCount())
1569 RemoveColumns();
1570 InsertHandleColumn();
1573 if (!xColumns.is())
1574 return;
1576 SetUpdateMode(false);
1578 // inserting must be based on the column positions
1579 sal_Int32 i;
1580 Any aWidth;
1581 for (i = 0; i < xColumns->getCount(); ++i)
1583 Reference< css::beans::XPropertySet > xCol(
1584 xColumns->getByIndex(i), css::uno::UNO_QUERY);
1586 OUString aName(
1587 comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)));
1589 aWidth = xCol->getPropertyValue(FM_PROP_WIDTH);
1590 sal_Int32 nWidth = 0;
1591 if (aWidth >>= nWidth)
1592 nWidth = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
1594 AppendColumn(aName, static_cast<sal_uInt16>(nWidth));
1595 DbGridColumn* pCol = DbGridControl::GetColumns()[ i ].get();
1596 pCol->setModel(xCol);
1599 // and now remove the hidden columns as well
1600 // (we did not already make it in the upper loop, since we would then have gotten
1601 // problems with the IDs of the columns: AppendColumn allocates them automatically,
1602 // but the column _after_ a hidden one needs an ID increased by one ...)
1603 Any aHidden;
1604 for (i = 0; i < xColumns->getCount(); ++i)
1606 Reference< css::beans::XPropertySet > xCol( xColumns->getByIndex(i), css::uno::UNO_QUERY);
1607 aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN);
1608 if (::comphelper::getBOOL(aHidden))
1609 HideColumn(GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
1612 SetUpdateMode(true);
1615 void FmGridControl::InitColumnByField(
1616 DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel,
1617 const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex )
1619 DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" );
1621 // lookup the column which belongs to the control source
1622 OUString sFieldName;
1623 _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName;
1624 Reference< XPropertySet > xField;
1625 _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
1628 if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length
1629 _rxFieldsByNames->getByName( sFieldName ) >>= xField;
1631 // determine the position of this column
1632 sal_Int32 nFieldPos = -1;
1633 if ( xField.is() )
1635 Reference< XPropertySet > xCheck;
1636 sal_Int32 nFieldCount = _rxFieldsByIndex->getCount();
1637 for ( sal_Int32 i = 0; i < nFieldCount; ++i)
1639 _rxFieldsByIndex->getByIndex( i ) >>= xCheck;
1640 if ( xField.get() == xCheck.get() )
1642 nFieldPos = i;
1643 break;
1648 if ( xField.is() && ( nFieldPos >= 0 ) )
1650 // some data types are not allowed
1651 sal_Int32 nDataType = DataType::OTHER;
1652 xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType;
1654 bool bIllegalType = false;
1655 switch ( nDataType )
1657 case DataType::BLOB:
1658 case DataType::LONGVARBINARY:
1659 case DataType::BINARY:
1660 case DataType::VARBINARY:
1661 case DataType::OTHER:
1662 bIllegalType = true;
1663 break;
1666 if ( bIllegalType )
1668 _pColumn->SetObject( static_cast<sal_Int16>(nFieldPos) );
1669 return;
1673 // the control type is determined by the ColumnServiceName
1674 static const char s_sPropColumnServiceName[] = "ColumnServiceName";
1675 if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) )
1676 return;
1678 _pColumn->setModel( _rxColumnModel );
1680 OUString sColumnServiceName;
1681 _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName;
1683 sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName );
1684 _pColumn->CreateControl( nFieldPos, xField, nTypeId );
1687 void FmGridControl::InitColumnsByFields(const Reference< css::container::XIndexAccess >& _rxFields)
1689 if ( !_rxFields.is() )
1690 return;
1692 // initialize columns
1693 Reference< XIndexContainer > xColumns( GetPeer()->getColumns() );
1694 Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY );
1696 // inserting must be based on the column positions
1697 for (sal_Int32 i = 0; i < xColumns->getCount(); i++)
1699 DbGridColumn* pCol = GetColumns()[ i ].get();
1700 OSL_ENSURE(pCol,"No grid column!");
1701 if ( pCol )
1703 Reference< XPropertySet > xColumnModel(
1704 xColumns->getByIndex( i ), css::uno::UNO_QUERY);
1706 InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields );
1711 void FmGridControl::HideColumn(sal_uInt16 nId)
1713 DbGridControl::HideColumn(nId);
1715 sal_uInt16 nPos = GetModelColumnPos(nId);
1716 if (nPos == sal_uInt16(-1))
1717 return;
1719 DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1720 if (pColumn->IsHidden())
1721 GetPeer()->columnHidden(pColumn);
1723 if (nId == m_nMarkedColumnId)
1724 m_nMarkedColumnId = sal_uInt16(-1);
1727 bool FmGridControl::isColumnSelected(DbGridColumn const * _pColumn)
1729 OSL_ENSURE(_pColumn,"Column can not be null!");
1730 bool bSelected = false;
1731 // if the column which is shown here is selected ...
1732 Reference< css::view::XSelectionSupplier > xSelSupplier(GetPeer()->getColumns(), UNO_QUERY);
1733 if ( xSelSupplier.is() )
1735 Reference< css::beans::XPropertySet > xColumn;
1736 xSelSupplier->getSelection() >>= xColumn;
1737 bSelected = (xColumn.get() == _pColumn->getModel().get());
1739 return bSelected;
1742 void FmGridControl::ShowColumn(sal_uInt16 nId)
1744 DbGridControl::ShowColumn(nId);
1746 sal_uInt16 nPos = GetModelColumnPos(nId);
1747 if (nPos == sal_uInt16(-1))
1748 return;
1750 DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1751 if (!pColumn->IsHidden())
1752 GetPeer()->columnVisible(pColumn);
1754 // if the column which is shown here is selected ...
1755 if ( isColumnSelected(pColumn) )
1756 markColumn(nId); // ... -> mark it
1759 bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks)
1761 SolarMutexGuard aGuard;
1762 // need to lock the SolarMutex so that no paint call disturbs us ...
1764 if ( !m_pSeekCursor )
1766 OSL_FAIL( "FmGridControl::selectBookmarks: no seek cursor!" );
1767 return false;
1770 SetNoSelection();
1772 bool bAllSuccessfull = true;
1775 for (const Any& rBookmark : _rBookmarks)
1777 // move the seek cursor to the row given
1778 if (m_pSeekCursor->moveToBookmark(rBookmark))
1779 SelectRow( m_pSeekCursor->getRow() - 1);
1780 else
1781 bAllSuccessfull = false;
1784 catch(Exception&)
1786 OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
1787 return false;
1790 return bAllSuccessfull;
1793 Sequence< Any> FmGridControl::getSelectionBookmarks()
1795 // lock our update so no paint-triggered seeks interfere ...
1796 SetUpdateMode(false);
1798 sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0;
1799 Sequence< Any> aBookmarks(nSelectedRows);
1800 if ( nSelectedRows )
1802 Any* pBookmarks = aBookmarks.getArray();
1804 // (I'm not sure if the problem isn't deeper: The scenario: a large table displayed by a grid with a
1805 // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress
1806 // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which did a
1807 // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition.
1808 // Unfortunately the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the
1809 // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning
1810 // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails.
1811 // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relies_ on
1812 // the first one, should be secured against recursion, with a broad-minded interpretation of "recursion": if any of these
1813 // code parts is executed, no other should be accessible. But this sounds very difficult to achieve...
1814 // )
1816 // The next problem caused by the same behavior (SeekCursor causes a propertyChanged): when adjusting rows we implicitly
1817 // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results.
1818 // That's why we _first_ collect the indices of the selected rows and _then_ their bookmarks.
1819 long nIdx = FirstSelectedRow();
1820 while (nIdx != BROWSER_ENDOFSELECTION)
1822 // (we misuse the bookmarks array for this ...)
1823 pBookmarks[i++] <<= static_cast<sal_Int32>(nIdx);
1824 nIdx = NextSelectedRow();
1826 DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indices !");
1828 for (i=0; i<nSelectedRows; ++i)
1830 nIdx = ::comphelper::getINT32(pBookmarks[i]);
1831 if (IsInsertionRow(nIdx))
1833 // do not delete empty row
1834 aBookmarks.realloc(--nSelectedRows);
1835 SelectRow(nIdx, false); // cancel selection for empty row
1836 break;
1839 // first, position the data cursor on the selected block
1840 if (SeekCursor(nIdx))
1842 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1844 pBookmarks[i] = m_pSeekCursor->getBookmark();
1846 #ifdef DBG_UTIL
1847 else
1848 OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
1849 #endif
1852 SetUpdateMode(true);
1854 // if one of the SeekCursor-calls failed...
1855 aBookmarks.realloc(i);
1857 // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems.
1858 // but this would be incompatible as we need a locking flag, then...)
1860 return aBookmarks;
1863 namespace
1865 OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const OUString& _sPropName)
1867 OUString sRetText;
1868 if ( _pPeer && _nPosition != -1)
1870 Reference<XIndexContainer> xIndex = _pPeer->getColumns();
1871 if ( xIndex.is() && xIndex->getCount() > _nPosition )
1873 Reference<XPropertySet> xProp;
1874 xIndex->getByIndex( _nPosition ) >>= xProp;
1875 if ( xProp.is() )
1877 try {
1878 xProp->getPropertyValue( _sPropName ) >>= sRetText;
1879 } catch (UnknownPropertyException const&) {
1880 TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
1885 return sRetText;
1889 // Object data and state
1890 OUString FmGridControl::GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1892 OUString sRetText;
1893 switch( _eObjType )
1895 case ::vcl::BBTYPE_BROWSEBOX:
1896 if ( GetPeer() )
1898 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1899 if ( xProp.is() )
1900 xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText;
1902 break;
1903 case ::vcl::BBTYPE_COLUMNHEADERCELL:
1904 sRetText = getColumnPropertyFromPeer(
1905 GetPeer(),
1906 GetModelColumnPos(
1907 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1908 FM_PROP_LABEL);
1909 break;
1910 default:
1911 sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition);
1913 return sRetText;
1916 OUString FmGridControl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1918 OUString sRetText;
1919 switch( _eObjType )
1921 case ::vcl::BBTYPE_BROWSEBOX:
1922 if ( GetPeer() )
1924 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1925 if ( xProp.is() )
1927 xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText;
1928 if ( sRetText.isEmpty() )
1929 xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText;
1932 break;
1933 case ::vcl::BBTYPE_COLUMNHEADERCELL:
1934 sRetText = getColumnPropertyFromPeer(
1935 GetPeer(),
1936 GetModelColumnPos(
1937 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1938 FM_PROP_HELPTEXT);
1939 if ( sRetText.isEmpty() )
1940 sRetText = getColumnPropertyFromPeer(
1941 GetPeer(),
1942 GetModelColumnPos(
1943 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1944 FM_PROP_DESCRIPTION);
1946 break;
1947 default:
1948 sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition);
1950 return sRetText;
1953 void FmGridControl::Select()
1955 DbGridControl::Select();
1956 // ... does it affect our columns?
1957 const MultiSelection* pColumnSelection = GetColumnSelection();
1959 sal_uInt16 nSelectedColumn =
1960 pColumnSelection && pColumnSelection->GetSelectCount()
1961 ? sal::static_int_cast< sal_uInt16 >(
1962 const_cast<MultiSelection*>(pColumnSelection)->FirstSelected())
1963 : SAL_MAX_UINT16;
1964 // the HandleColumn is not selected
1965 switch (nSelectedColumn)
1967 case SAL_MAX_UINT16: break; // no selection
1968 case 0 : nSelectedColumn = SAL_MAX_UINT16; break;
1969 // handle col can't be selected
1970 default :
1971 // get the model col pos instead of the view col pos
1972 nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1));
1973 break;
1976 if (nSelectedColumn != m_nCurrentSelectedColumn)
1978 // BEFORE calling the select at the SelectionSupplier!
1979 m_nCurrentSelectedColumn = nSelectedColumn;
1981 if (!m_bSelecting)
1983 m_bSelecting = true;
1987 Reference< XIndexAccess > xColumns = GetPeer()->getColumns();
1988 Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
1989 if (xSelSupplier.is())
1991 if (nSelectedColumn != SAL_MAX_UINT16)
1993 Reference< XPropertySet > xColumn(
1994 xColumns->getByIndex(nSelectedColumn),
1995 css::uno::UNO_QUERY);
1996 xSelSupplier->select(makeAny(xColumn));
1998 else
2000 xSelSupplier->select(Any());
2004 catch(Exception&)
2009 m_bSelecting = false;
2015 void FmGridControl::KeyInput( const KeyEvent& rKEvt )
2017 bool bDone = false;
2018 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
2019 if ( IsDesignMode()
2020 && !rKeyCode.IsShift()
2021 && !rKeyCode.IsMod1()
2022 && !rKeyCode.IsMod2()
2023 && GetParent() )
2025 switch ( rKeyCode.GetCode() )
2027 case KEY_ESCAPE:
2028 GetParent()->GrabFocus();
2029 bDone = true;
2030 break;
2031 case KEY_DELETE:
2032 if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 )
2034 Reference< css::container::XIndexContainer > xCols(GetPeer()->getColumns());
2035 if ( xCols.is() )
2039 if ( m_nCurrentSelectedColumn < xCols->getCount() )
2041 Reference< XInterface > xCol;
2042 xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol;
2043 xCols->removeByIndex(m_nCurrentSelectedColumn);
2044 ::comphelper::disposeComponent(xCol);
2047 catch(const Exception&)
2049 OSL_FAIL("exception occurred while deleting a column");
2053 bDone = true;
2054 break;
2057 if ( !bDone )
2058 DbGridControl::KeyInput( rKEvt );
2061 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */