sd: keep a non-owning pointer to the OverridingShell
[LibreOffice.git] / svx / source / fmcomp / fmgridcl.cxx
blobaf72f2045a556d48f4df887e31e6b7094925b7a5
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>
36 #include <com/sun/star/form/XConfirmDeleteListener.hpp>
37 #include <com/sun/star/form/XFormComponent.hpp>
38 #include <com/sun/star/form/XGridColumnFactory.hpp>
39 #include <com/sun/star/io/XPersistObject.hpp>
40 #include <com/sun/star/sdb/CommandType.hpp>
41 #include <com/sun/star/sdb/RowChangeAction.hpp>
42 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
43 #include <com/sun/star/sdbc/DataType.hpp>
44 #include <com/sun/star/sdbc/SQLException.hpp>
45 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
46 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
47 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
48 #include <com/sun/star/sdbcx/XDeleteRows.hpp>
49 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
50 #include <com/sun/star/util/XNumberFormats.hpp>
51 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
52 #include <com/sun/star/util/URLTransformer.hpp>
53 #include <com/sun/star/util/XURLTransformer.hpp>
54 #include <com/sun/star/view/XSelectionSupplier.hpp>
55 #include <comphelper/processfactory.hxx>
56 #include <comphelper/property.hxx>
57 #include <comphelper/string.hxx>
58 #include <comphelper/types.hxx>
59 #include <connectivity/dbtools.hxx>
60 #include <sfx2/dispatch.hxx>
61 #include <sfx2/viewfrm.hxx>
62 #include <svl/eitem.hxx>
63 #include <vcl/commandevent.hxx>
64 #include <vcl/svapp.hxx>
65 #include <tools/debug.hxx>
66 #include <tools/multisel.hxx>
67 #include <comphelper/diagnose_ex.hxx>
68 #include <vcl/help.hxx>
69 #include <vcl/settings.hxx>
70 #include <sal/log.hxx>
71 #include <i18nlangtag/languagetag.hxx>
72 #include <memory>
74 using namespace ::com::sun::star::uno;
75 using namespace ::com::sun::star::view;
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::lang;
78 using namespace ::com::sun::star::sdbcx;
79 using namespace ::com::sun::star::sdbc;
80 using namespace ::com::sun::star::sdb;
81 using namespace ::com::sun::star::form;
82 using namespace ::com::sun::star::util;
83 using namespace ::com::sun::star::container;
84 using namespace ::cppu;
85 using namespace ::svxform;
86 using namespace ::svx;
87 using namespace ::dbtools;
89 struct FmGridHeaderData
91 ODataAccessDescriptor aDropData;
92 Point aDropPosPixel;
93 sal_Int8 nDropAction;
94 Reference< XInterface > xDroppedStatement;
95 Reference< XInterface > xDroppedResultSet;
98 static void InsertMenuItem(weld::Menu& rMenu, int nMenuPos, const OUString& id, const OUString& rText, const OUString& rImgId)
100 rMenu.insert(nMenuPos, id, rText, &rImgId, nullptr, nullptr, TRISTATE_INDET);
103 FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits)
104 :EditBrowserHeader(pParent, nWinBits)
105 ,DropTargetHelper(this)
106 ,m_pImpl(new FmGridHeaderData)
110 FmGridHeader::~FmGridHeader()
112 disposeOnce();
115 void FmGridHeader::dispose()
117 m_pImpl.reset();
118 DropTargetHelper::dispose();
119 svt::EditBrowserHeader::dispose();
122 sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const
124 return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId);
127 void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId)
129 sal_uInt16 nPos = GetModelColumnPos(nColumnId);
130 Reference< XIndexAccess > xColumns = static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns();
131 if ( nPos < xColumns->getCount() )
133 Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
134 if ( xSelSupplier.is() )
136 Reference< XPropertySet > xColumn;
137 xColumns->getByIndex(nPos) >>= xColumn;
138 xSelSupplier->select(Any(xColumn));
143 void FmGridHeader::Select()
145 EditBrowserHeader::Select();
146 notifyColumnSelect(GetCurItemId());
149 void FmGridHeader::RequestHelp( const HelpEvent& rHEvt )
151 sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
152 if ( nItemId )
154 if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
156 tools::Rectangle aItemRect = GetItemRect( nItemId );
157 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
158 aItemRect.SetLeft( aPt.X() );
159 aItemRect.SetTop( aPt.Y() );
160 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
161 aItemRect.SetRight( aPt.X() );
162 aItemRect.SetBottom( aPt.Y() );
164 sal_uInt16 nPos = GetModelColumnPos(nItemId);
165 Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
168 Reference< css::beans::XPropertySet > xColumn(xColumns->getByIndex(nPos),UNO_QUERY);
169 OUString aHelpText;
170 xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText;
171 if ( aHelpText.isEmpty() )
172 xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText;
173 if ( !aHelpText.isEmpty() )
175 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
176 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText );
177 else
178 Help::ShowQuickHelp( this, aItemRect, aHelpText );
179 return;
182 catch(Exception&)
184 return;
188 EditBrowserHeader::RequestHelp( rHEvt );
191 sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt )
193 // drop allowed in design mode only
194 if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
195 return DND_ACTION_NONE;
197 // search for recognized formats
198 const DataFlavorExVector& rFlavors = GetDataFlavorExVector();
199 if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::FIELD_DESCRIPTOR))
200 return rEvt.mnAction;
202 return DND_ACTION_NONE;
205 sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt )
207 if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
208 return DND_ACTION_NONE;
210 TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable);
212 // check the formats
213 bool bColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
214 bool bFieldDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
215 if (!bColumnDescriptor && !bFieldDescriptor)
217 OSL_FAIL("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!");
218 return DND_ACTION_NONE;
221 // extract the descriptor
222 OUString sDatasource, sCommand, sFieldName,sDatabaseLocation;
223 sal_Int32 nCommandType = CommandType::COMMAND;
224 Reference< XPreparedStatement > xStatement;
225 Reference< XResultSet > xResultSet;
226 Reference< XPropertySet > xField;
227 Reference< XConnection > xConnection;
229 ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData);
230 if (aColumn.has(DataAccessDescriptorProperty::DataSource)) aColumn[DataAccessDescriptorProperty::DataSource] >>= sDatasource;
231 if (aColumn.has(DataAccessDescriptorProperty::DatabaseLocation)) aColumn[DataAccessDescriptorProperty::DatabaseLocation] >>= sDatabaseLocation;
232 if (aColumn.has(DataAccessDescriptorProperty::Command)) aColumn[DataAccessDescriptorProperty::Command] >>= sCommand;
233 if (aColumn.has(DataAccessDescriptorProperty::CommandType)) aColumn[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
234 if (aColumn.has(DataAccessDescriptorProperty::ColumnName)) aColumn[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
235 if (aColumn.has(DataAccessDescriptorProperty::ColumnObject))aColumn[DataAccessDescriptorProperty::ColumnObject] >>= xField;
236 if (aColumn.has(DataAccessDescriptorProperty::Connection)) aColumn[DataAccessDescriptorProperty::Connection] >>= xConnection;
238 if ( sFieldName.isEmpty()
239 || sCommand.isEmpty()
240 || ( sDatasource.isEmpty()
241 && sDatabaseLocation.isEmpty()
242 && !xConnection.is()
246 OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
247 return DND_ACTION_NONE;
252 // need a connection
253 if (!xConnection.is())
254 { // the transferable did not contain the connection -> build an own one
257 OUString sSignificantSource( sDatasource.isEmpty() ? sDatabaseLocation : sDatasource );
258 xConnection = getConnection_withFeedback(sSignificantSource, OUString(), OUString(),
259 static_cast<FmGridControl*>(GetParent())->getContext(), nullptr );
261 catch(NoSuchElementException&)
262 { // allowed, means sDatasource isn't a valid data source name...
264 catch(Exception&)
266 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
269 if (!xConnection.is())
271 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
272 return DND_ACTION_NONE;
276 // try to obtain the column object
277 if (!xField.is())
279 #ifdef DBG_UTIL
280 Reference< XServiceInfo > xServiceInfo(xConnection, UNO_QUERY);
281 DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)");
282 #endif
284 Reference< XNameAccess > xFields;
285 switch (nCommandType)
287 case CommandType::TABLE:
289 Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
290 Reference< XColumnsSupplier > xSupplyColumns;
291 xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns;
292 xFields = xSupplyColumns->getColumns();
294 break;
295 case CommandType::QUERY:
297 Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
298 Reference< XColumnsSupplier > xSupplyColumns;
299 xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns;
300 xFields = xSupplyColumns->getColumns();
302 break;
303 default:
305 xStatement = xConnection->prepareStatement(sCommand);
306 // not interested in any results
308 Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY);
309 xStatProps->setPropertyValue(u"MaxRows"_ustr, Any(sal_Int32(0)));
311 xResultSet = xStatement->executeQuery();
312 Reference< XColumnsSupplier > xSupplyCols(xResultSet, UNO_QUERY);
313 if (xSupplyCols.is())
314 xFields = xSupplyCols->getColumns();
318 if (xFields.is() && xFields->hasByName(sFieldName))
319 xFields->getByName(sFieldName) >>= xField;
321 if (!xField.is())
323 ::comphelper::disposeComponent(xStatement);
324 return DND_ACTION_NONE;
328 // do the drop asynchronously
329 // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu)
330 m_pImpl->aDropData = std::move(aColumn);
331 m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] <<= xConnection;
332 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] <<= xField;
334 m_pImpl->nDropAction = _rEvt.mnAction;
335 m_pImpl->aDropPosPixel = _rEvt.maPosPixel;
336 m_pImpl->xDroppedStatement = xStatement;
337 m_pImpl->xDroppedResultSet = xResultSet;
339 PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop), nullptr, true);
341 catch (Exception&)
343 TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
344 ::comphelper::disposeComponent(xStatement);
345 return DND_ACTION_NONE;
348 return DND_ACTION_LINK;
351 IMPL_LINK_NOARG( FmGridHeader, OnAsyncExecuteDrop, void*, void )
353 OUString sCommand, sFieldName,sURL;
354 sal_Int32 nCommandType = CommandType::COMMAND;
355 Reference< XPropertySet > xField;
356 Reference< XConnection > xConnection;
358 OUString sDatasource = m_pImpl->aDropData.getDataSource();
359 if ( sDatasource.isEmpty() && m_pImpl->aDropData.has(DataAccessDescriptorProperty::ConnectionResource) )
360 m_pImpl->aDropData[DataAccessDescriptorProperty::ConnectionResource] >>= sURL;
361 m_pImpl->aDropData[DataAccessDescriptorProperty::Command] >>= sCommand;
362 m_pImpl->aDropData[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
363 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
364 m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] >>= xConnection;
365 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] >>= xField;
369 // need number formats
370 Reference< XNumberFormatsSupplier > xSupplier = getNumberFormats(xConnection, true);
371 Reference< XNumberFormats > xNumberFormats;
372 if (xSupplier.is())
373 xNumberFormats = xSupplier->getNumberFormats();
374 if (!xNumberFormats.is())
376 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
377 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
378 return;
381 // The field now needs two pieces of information:
382 // a.) Name of the field for label and ControlSource
383 // b.) FormatKey, to determine which field is to be created
384 sal_Int32 nDataType = 0;
385 xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType;
386 // these datatypes can not be processed in Gridcontrol
387 switch (nDataType)
389 case DataType::BLOB:
390 case DataType::LONGVARBINARY:
391 case DataType::BINARY:
392 case DataType::VARBINARY:
393 case DataType::OTHER:
394 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
395 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
396 return;
399 // Creating the column
400 Reference< XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
401 Reference< XGridColumnFactory > xFactory(xCols, UNO_QUERY);
403 sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel);
404 // insert position, always before the current column
405 sal_uInt16 nPos = GetModelColumnPos(nColId);
406 Reference< XPropertySet > xCol, xSecondCol;
408 // Create Column based on type, default textfield
409 std::vector<OUString> aPossibleTypes;
410 std::vector<OUString> aImgResId;
411 std::vector<TranslateId> aStrResId;
413 switch (nDataType)
415 case DataType::BIT:
416 case DataType::BOOLEAN:
417 aPossibleTypes.emplace_back(FM_COL_CHECKBOX);
418 aImgResId.emplace_back(RID_SVXBMP_CHECKBOX);
419 aStrResId.emplace_back(RID_STR_PROPTITLE_CHECKBOX);
420 break;
421 case DataType::TINYINT:
422 case DataType::SMALLINT:
423 case DataType::INTEGER:
424 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
425 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
426 aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
427 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
428 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
429 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
430 break;
431 case DataType::REAL:
432 case DataType::DOUBLE:
433 case DataType::NUMERIC:
434 case DataType::DECIMAL:
435 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
436 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
437 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
438 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
439 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
440 aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
441 break;
442 case DataType::TIMESTAMP:
443 aPossibleTypes.emplace_back("dateandtimefield");
444 aImgResId.emplace_back(RID_SVXBMP_DATE_N_TIME_FIELDS);
445 aStrResId.emplace_back(RID_STR_DATE_AND_TIME);
446 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
447 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
448 aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
449 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
450 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
451 aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
452 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
453 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
454 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
455 break;
456 case DataType::DATE:
457 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
458 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
459 aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
460 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
461 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
462 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
463 break;
464 case DataType::TIME:
465 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
466 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
467 aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
468 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
469 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
470 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
471 break;
472 case DataType::CHAR:
473 case DataType::VARCHAR:
474 case DataType::LONGVARCHAR:
475 default:
476 aPossibleTypes.emplace_back(FM_COL_TEXTFIELD);
477 aImgResId.emplace_back(RID_SVXBMP_EDITBOX);
478 aStrResId.emplace_back(RID_STR_PROPTITLE_EDIT);
479 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
480 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
481 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
482 break;
484 // if it's a currency field, a "currency field" option
487 if ( ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField)
488 && ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY)))
490 aPossibleTypes.insert(aPossibleTypes.begin(), u"" FM_COL_CURRENCYFIELD ""_ustr);
491 aImgResId.insert(aImgResId.begin(), RID_SVXBMP_CURRENCYFIELD);
492 aStrResId.insert(aStrResId.begin(), RID_STR_PROPTITLE_CURRENCYFIELD);
495 catch (const Exception&)
497 TOOLS_WARN_EXCEPTION("svx", "");
500 assert(aPossibleTypes.size() == aImgResId.size());
502 bool bDateNTimeCol = false;
503 if (!aPossibleTypes.empty())
505 OUString sPreferredType = aPossibleTypes[0];
506 if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.size() > 1))
508 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, u"svx/ui/colsmenu.ui"_ustr));
509 std::unique_ptr<weld::Menu> xTypeMenu(xBuilder->weld_menu(u"insertmenu"_ustr));
511 int nMenuPos = 0;
512 std::vector<OUString>::const_iterator iter;
513 std::vector<TranslateId>::const_iterator striter;
514 std::vector<OUString>::const_iterator imgiter;
515 for (iter = aPossibleTypes.begin(), imgiter = aImgResId.begin(), striter = aStrResId.begin();
516 iter != aPossibleTypes.end(); ++iter, ++striter, ++imgiter)
518 InsertMenuItem(*xTypeMenu, nMenuPos++, *iter, SvxResId(*striter), *imgiter);
521 ::tools::Rectangle aRect(m_pImpl->aDropPosPixel, Size(1,1));
522 weld::Window* pParent = weld::GetPopupParent(*this, aRect);
523 OUString sResult = xTypeMenu->popup_at_rect(pParent, aRect);
524 if (!sResult.isEmpty())
525 sPreferredType = sResult;
528 bDateNTimeCol = sPreferredType == "dateandtimefield";
529 sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1;
530 OUString sFieldService;
531 while (nColCount--)
533 if (bDateNTimeCol)
534 sPreferredType = nColCount ? FM_COL_DATEFIELD : FM_COL_TIMEFIELD;
536 sFieldService = sPreferredType;
537 Reference< XPropertySet > xThisRoundCol;
538 if ( !sFieldService.isEmpty() )
539 xThisRoundCol = xFactory->createColumn(sFieldService);
540 if (nColCount)
541 xSecondCol = std::move(xThisRoundCol);
542 else
543 xCol = std::move(xThisRoundCol);
547 if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is()))
549 ::comphelper::disposeComponent(xCol); // in case only the creation of the second column failed
550 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
551 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
552 return;
555 if (bDateNTimeCol)
557 OUString sTimePostfix(SvxResId(RID_STR_POSTFIX_TIME));
558 xCol->setPropertyValue(FM_PROP_LABEL, Any( OUString( sFieldName + sTimePostfix ) ) );
560 OUString sDatePostfix(SvxResId( RID_STR_POSTFIX_DATE));
561 xSecondCol->setPropertyValue(FM_PROP_LABEL, Any( OUString( sFieldName + sDatePostfix ) ) );
563 else
564 xCol->setPropertyValue(FM_PROP_LABEL, Any(sFieldName));
566 // insert now
567 Any aElement;
568 aElement <<= xCol;
570 xCols->insertByIndex(nPos, aElement);
572 FormControlFactory aControlFactory;
573 aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol );
574 FormControlFactory::initializeFieldDependentProperties( xField, xCol, xNumberFormats );
576 xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, Any(sFieldName));
577 if ( xSecondCol.is() )
578 xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, Any(sFieldName));
580 if (bDateNTimeCol)
582 OUString aPostfix[] = {
583 SvxResId(RID_STR_POSTFIX_DATE),
584 SvxResId(RID_STR_POSTFIX_TIME)
587 for ( size_t i=0; i<2; ++i )
589 OUString sPurePostfix = comphelper::string::stripStart(aPostfix[i], ' ');
590 sPurePostfix = comphelper::string::stripStart(sPurePostfix, '(');
591 sPurePostfix = comphelper::string::stripEnd(sPurePostfix, ')');
592 OUString sRealName = sFieldName + "_" + sPurePostfix;
593 if (i)
594 xSecondCol->setPropertyValue(FM_PROP_NAME, Any(sRealName));
595 else
596 xCol->setPropertyValue(FM_PROP_NAME, Any(sRealName));
599 else
600 xCol->setPropertyValue(FM_PROP_NAME, Any(sFieldName));
602 if (bDateNTimeCol)
604 aElement <<= xSecondCol;
605 xCols->insertByIndex(nPos == sal_uInt16(-1) ? nPos : ++nPos, aElement);
608 // is the component::Form tied to the database?
609 Reference< XFormComponent > xFormCp(xCols, UNO_QUERY);
610 Reference< XPropertySet > xForm(xFormCp->getParent(), UNO_QUERY);
611 if (xForm.is())
613 if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).isEmpty())
615 if ( !sDatasource.isEmpty() )
616 xForm->setPropertyValue(FM_PROP_DATASOURCE, Any(sDatasource));
617 else
618 xForm->setPropertyValue(FM_PROP_URL, Any(sURL));
621 if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).isEmpty())
623 xForm->setPropertyValue(FM_PROP_COMMAND, Any(sCommand));
624 Any aCommandType;
625 switch (nCommandType)
627 case CommandType::TABLE:
628 aCommandType <<= sal_Int32(CommandType::TABLE);
629 break;
630 case CommandType::QUERY:
631 aCommandType <<= sal_Int32(CommandType::QUERY);
632 break;
633 default:
634 aCommandType <<= sal_Int32(CommandType::COMMAND);
635 xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, css::uno::Any(2 == nCommandType));
636 break;
638 xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType);
642 catch (Exception&)
644 TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
645 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
646 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
647 return;
650 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
651 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
654 void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, weld::Menu& rMenu,
655 weld::Menu& rInsertMenu, weld::Menu& rChangeMenu,
656 weld::Menu& rShowMenu)
658 bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode();
660 Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
661 // Building of the Insert Menu
662 // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND
663 if(nColId > 0)
665 sal_uInt16 nPos2 = GetModelColumnPos(nColId);
667 Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
668 Reference< css::beans::XPropertySet> xColumn( xColumns->getByIndex(nPos2), css::uno::UNO_QUERY);
669 Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
670 if (xSelSupplier.is())
671 xSelSupplier->select(Any(xColumn));
674 // insert position, always before the current column
675 sal_uInt16 nPos = GetModelColumnPos(nColId);
676 bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId);
678 if (bDesignMode)
680 int nMenuPos = 0;
681 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_TEXTFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
682 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_CHECKBOX ""_ustr, SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
683 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_COMBOBOX ""_ustr, SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
684 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_LISTBOX ""_ustr, SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
685 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_DATEFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
686 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_TIMEFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
687 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_NUMERICFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
688 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_CURRENCYFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
689 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_PATTERNFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
690 InsertMenuItem(rInsertMenu, nMenuPos++, u"" FM_COL_FORMATTEDFIELD ""_ustr, SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
693 if (xCols.is() && nColId)
695 Reference< css::beans::XPropertySet > xPropSet( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
697 Reference< css::io::XPersistObject > xServiceQuestion(xPropSet, UNO_QUERY);
698 sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0;
699 if (nColType == TYPE_TEXTFIELD)
700 { // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD
701 // in both cases. And as columns don't have a css::lang::XServiceInfo interface, we have to distinguish both
702 // types via the existence of special properties
703 if (xPropSet.is())
705 Reference< css::beans::XPropertySetInfo > xPropsInfo = xPropSet->getPropertySetInfo();
706 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
707 nColType = TYPE_FORMATTEDFIELD;
711 if (bDesignMode)
713 int nMenuPos = 0;
714 if (nColType != TYPE_TEXTFIELD)
715 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_TEXTFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
716 if (nColType != TYPE_CHECKBOX)
717 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_CHECKBOX"1"_ustr, SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
718 if (nColType != TYPE_COMBOBOX)
719 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_COMBOBOX"1"_ustr, SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
720 if (nColType != TYPE_LISTBOX)
721 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_LISTBOX"1"_ustr, SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
722 if (nColType != TYPE_DATEFIELD)
723 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_DATEFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
724 if (nColType != TYPE_TIMEFIELD)
725 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_TIMEFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
726 if (nColType != TYPE_NUMERICFIELD)
727 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_NUMERICFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
728 if (nColType != TYPE_CURRENCYFIELD)
729 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_CURRENCYFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
730 if (nColType != TYPE_PATTERNFIELD)
731 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_PATTERNFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
732 if (nColType != TYPE_FORMATTEDFIELD)
733 InsertMenuItem(rChangeMenu, nMenuPos++, u"" FM_COL_FORMATTEDFIELD"1"_ustr, SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
737 rMenu.set_visible(u"change"_ustr, bDesignMode && bMarked && xCols.is());
738 rMenu.set_sensitive(u"change"_ustr, bDesignMode && bMarked && xCols.is());
740 else
742 rMenu.set_visible(u"change"_ustr, false);
743 rMenu.set_sensitive(u"change"_ustr, false);
746 rMenu.set_visible(u"insert"_ustr, bDesignMode && xCols.is());
747 rMenu.set_sensitive(u"insert"_ustr, bDesignMode && xCols.is());
748 rMenu.set_visible(u"delete"_ustr, bDesignMode && bMarked && xCols.is());
749 rMenu.set_sensitive(u"delete"_ustr, bDesignMode && bMarked && xCols.is());
750 rMenu.set_visible(u"column"_ustr, bDesignMode && bMarked && xCols.is());
751 rMenu.set_sensitive(u"column"_ustr, bDesignMode && bMarked && xCols.is());
753 sal_uInt16 nHiddenCols = 0;
754 if (xCols.is())
756 // check for hidden cols
757 Reference< css::beans::XPropertySet > xCurCol;
758 Any aHidden,aName;
759 for (sal_Int32 i=0; i<xCols->getCount(); ++i)
761 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
762 DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !");
763 aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
764 DBG_ASSERT(aHidden.getValueTypeClass() == TypeClass_BOOLEAN,
765 "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !");
766 if (::comphelper::getBOOL(aHidden))
768 // put the column name into the 'show col' menu
769 if (nHiddenCols < 16)
771 // (only the first 16 items to keep the menu rather small)
772 aName = xCurCol->getPropertyValue(FM_PROP_LABEL);
773 // the ID is arbitrary, but should be unique within the whole menu
774 rMenu.insert(nHiddenCols, OUString::number(nHiddenCols + 1), ::comphelper::getString(aName),
775 nullptr, nullptr, nullptr, TRISTATE_INDET);
777 ++nHiddenCols;
781 rShowMenu.set_visible(u"more"_ustr, xCols.is() && (nHiddenCols > 16));
782 rMenu.set_visible(u"show"_ustr, xCols.is() && (nHiddenCols > 0));
783 rMenu.set_sensitive(u"show"_ustr, xCols.is() && (nHiddenCols > 0));
785 // allow the 'hide column' item ?
786 bool bAllowHide = bMarked; // a column is marked
787 bAllowHide = bAllowHide || (!bDesignMode && (nPos != sal_uInt16(-1))); // OR we are in alive mode and have hit a column
788 bAllowHide = bAllowHide && xCols.is(); // AND we have a column container
789 bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1); // AND there are at least two visible columns
790 rMenu.set_visible(u"hide"_ustr, bAllowHide);
791 rMenu.set_sensitive(u"hide"_ustr, bAllowHide);
793 if (!bMarked)
794 return;
796 SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
797 // ask the bindings of the current view frame (which should be the one we're residing in) for the state
798 if (pCurrentFrame)
800 std::unique_ptr<SfxBoolItem> pItem;
801 SfxItemState eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem);
803 if (eState >= SfxItemState::DEFAULT && pItem)
805 rMenu.set_active(u"column"_ustr, pItem->GetValue());
810 namespace {
812 enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone };
816 void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const weld::Menu& rMenu, const OUString& rExecutionResult)
818 Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
819 sal_uInt16 nPos = GetModelColumnPos(nColId);
821 OUString aFieldType;
822 bool bReplace = false;
823 InspectorAction eInspectorAction = eNone;
825 if (rExecutionResult == "delete")
827 Reference< XInterface > xCol(
828 xCols->getByIndex(nPos), css::uno::UNO_QUERY);
829 xCols->removeByIndex(nPos);
830 ::comphelper::disposeComponent(xCol);
832 else if (rExecutionResult == "hide")
834 Reference< css::beans::XPropertySet > xCurCol( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
835 xCurCol->setPropertyValue(FM_PROP_HIDDEN, Any(true));
837 else if (rExecutionResult == "column")
839 eInspectorAction = rMenu.get_active(u"column"_ustr) ? eOpenInspector : eCloseInspector;
841 else if (rExecutionResult.startsWith(FM_COL_TEXTFIELD))
843 if (rExecutionResult != FM_COL_TEXTFIELD)
844 bReplace = true;
845 aFieldType = FM_COL_TEXTFIELD;
847 else if (rExecutionResult.startsWith(FM_COL_COMBOBOX))
849 if (rExecutionResult != FM_COL_COMBOBOX)
850 bReplace = true;
851 aFieldType = FM_COL_COMBOBOX;
853 else if (rExecutionResult.startsWith(FM_COL_LISTBOX))
855 if (rExecutionResult != FM_COL_LISTBOX)
856 bReplace = true;
857 aFieldType = FM_COL_LISTBOX;
859 else if (rExecutionResult.startsWith(FM_COL_CHECKBOX))
861 if (rExecutionResult != FM_COL_CHECKBOX)
862 bReplace = true;
863 aFieldType = FM_COL_CHECKBOX;
865 else if (rExecutionResult.startsWith(FM_COL_DATEFIELD))
867 if (rExecutionResult != FM_COL_DATEFIELD)
868 bReplace = true;
869 aFieldType = FM_COL_DATEFIELD;
871 else if (rExecutionResult.startsWith(FM_COL_TIMEFIELD))
873 if (rExecutionResult != FM_COL_TIMEFIELD)
874 bReplace = true;
875 aFieldType = FM_COL_TIMEFIELD;
877 else if (rExecutionResult.startsWith(FM_COL_NUMERICFIELD))
879 if (rExecutionResult != FM_COL_NUMERICFIELD)
880 bReplace = true;
881 aFieldType = FM_COL_NUMERICFIELD;
883 else if (rExecutionResult.startsWith(FM_COL_CURRENCYFIELD))
885 if (rExecutionResult != FM_COL_CURRENCYFIELD)
886 bReplace = true;
887 aFieldType = FM_COL_CURRENCYFIELD;
889 else if (rExecutionResult.startsWith(FM_COL_PATTERNFIELD))
891 if (rExecutionResult != FM_COL_PATTERNFIELD)
892 bReplace = true;
893 aFieldType = FM_COL_PATTERNFIELD;
895 else if (rExecutionResult.startsWith(FM_COL_FORMATTEDFIELD))
897 if (rExecutionResult != FM_COL_FORMATTEDFIELD)
898 bReplace = true;
899 aFieldType = FM_COL_FORMATTEDFIELD;
901 else if (rExecutionResult == "more")
903 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
904 ScopedVclPtr<AbstractFmShowColsDialog> pDlg(pFact->CreateFmShowColsDialog(GetFrameWeld()));
905 pDlg->SetColumns(xCols);
906 pDlg->Execute();
908 else if (rExecutionResult == "all")
910 // just iterate through all the cols ...
911 Reference< css::beans::XPropertySet > xCurCol;
912 for (sal_Int32 i=0; i<xCols->getCount(); ++i)
914 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
915 xCurCol->setPropertyValue(FM_PROP_HIDDEN, Any(false));
917 // TODO : there must be a more clever way to do this...
918 // with the above the view is updated after every single model update ...
920 else if (!rExecutionResult.isEmpty())
922 sal_Int32 nExecutionResult = rExecutionResult.toInt32();
923 if (nExecutionResult>0 && nExecutionResult<=16)
925 // it was a "show column/<colname>" command (there are at most 16 such items)
926 // search the nExecutionResult'th hidden col
927 Reference< css::beans::XPropertySet > xCurCol;
928 for (sal_Int32 i=0; i<xCols->getCount() && nExecutionResult; ++i)
930 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
931 Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
932 if (::comphelper::getBOOL(aHidden))
933 if (!--nExecutionResult)
935 xCurCol->setPropertyValue(FM_PROP_HIDDEN, Any(false));
936 break;
942 if ( !aFieldType.isEmpty() )
946 Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW );
947 Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW );
949 if ( bReplace )
951 // rescue over a few properties
952 Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY );
954 TransferFormComponentProperties(
955 xReplaced, xNewCol, Application::GetSettings().GetUILanguageTag().getLocale() );
957 xCols->replaceByIndex( nPos, Any( xNewCol ) );
958 ::comphelper::disposeComponent( xReplaced );
960 eInspectorAction = eUpdateInspector;
962 else
964 FormControlFactory factory;
966 OUString sLabel = FormControlFactory::getDefaultUniqueName_ByComponentType(
967 Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol );
968 xNewCol->setPropertyValue( FM_PROP_LABEL, Any( sLabel ) );
969 xNewCol->setPropertyValue( FM_PROP_NAME, Any( sLabel ) );
971 factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol );
973 xCols->insertByIndex( nPos, Any( xNewCol ) );
976 catch( const Exception& )
978 DBG_UNHANDLED_EXCEPTION("svx");
982 SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
983 OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" );
984 if ( !pCurrentFrame )
985 return;
987 if ( eInspectorAction == eUpdateInspector )
989 if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) )
990 eInspectorAction = eNone;
993 if ( eInspectorAction != eNone )
995 SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction != eCloseInspector );
997 pCurrentFrame->GetBindings().GetDispatcher()->ExecuteList(
998 SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON,
999 { &aShowItem });
1003 void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos )
1005 // the affected col
1006 sal_uInt16 nColId = GetItemId( _rPreferredPos );
1008 // the menu
1009 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, u"svx/ui/colsmenu.ui"_ustr));
1010 std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
1011 std::unique_ptr<weld::Menu> xInsertMenu(xBuilder->weld_menu(u"insertmenu"_ustr));
1012 std::unique_ptr<weld::Menu> xChangeMenu(xBuilder->weld_menu(u"changemenu"_ustr));
1013 std::unique_ptr<weld::Menu> xShowMenu(xBuilder->weld_menu(u"showmenu"_ustr));
1015 // let derivatives modify the menu
1016 PreExecuteColumnContextMenu(nColId, *xContextMenu, *xInsertMenu, *xChangeMenu, *xShowMenu);
1018 bool bEmpty = true;
1019 for (int i = 0, nCount = xContextMenu->n_children(); i < nCount; ++i)
1021 bEmpty = !xContextMenu->get_sensitive(xContextMenu->get_id(i));
1022 if (!bEmpty)
1023 break;
1025 if (bEmpty)
1026 return;
1028 // execute the menu
1029 ::tools::Rectangle aRect(_rPreferredPos, Size(1,1));
1030 weld::Window* pParent = weld::GetPopupParent(*this, aRect);
1031 OUString sResult = xContextMenu->popup_at_rect(pParent, aRect);
1033 // let derivatives handle the result
1034 PostExecuteColumnContextMenu(nColId, *xContextMenu, sResult);
1037 void FmGridHeader::Command(const CommandEvent& rEvt)
1039 switch (rEvt.GetCommand())
1041 case CommandEventId::ContextMenu:
1043 if (!rEvt.IsMouseEvent())
1044 return;
1046 triggerColumnContextMenu( rEvt.GetMousePosPixel() );
1048 break;
1049 default:
1050 EditBrowserHeader::Command(rEvt);
1054 FmGridControl::FmGridControl(
1055 const Reference< css::uno::XComponentContext >& _rxContext,
1056 vcl::Window* pParent,
1057 FmXGridPeer* _pPeer,
1058 WinBits nBits)
1059 :DbGridControl(_rxContext, pParent, nBits)
1060 ,m_pPeer(_pPeer)
1061 ,m_nCurrentSelectedColumn(-1)
1062 ,m_nMarkedColumnId(BROWSER_INVALIDID)
1063 ,m_bSelecting(false)
1064 ,m_bInColumnMove(false)
1066 EnableInteractiveRowHeight( );
1069 void FmGridControl::Command(const CommandEvent& _rEvt)
1071 if ( CommandEventId::ContextMenu == _rEvt.GetCommand() )
1073 FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() );
1074 if ( pMyHeader && !_rEvt.IsMouseEvent() )
1075 { // context menu requested by keyboard
1076 if ( 1 == GetSelectColumnCount() || IsDesignMode() )
1078 sal_uInt16 nSelId = GetColumnId(
1079 sal::static_int_cast< sal_uInt16 >( FirstSelectedColumn() ) );
1080 ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
1082 Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) );
1083 pMyHeader->triggerColumnContextMenu(aRelativePos);
1085 // handled
1086 return;
1091 DbGridControl::Command( _rEvt );
1094 // css::beans::XPropertyChangeListener
1095 void FmGridControl::propertyChange(const css::beans::PropertyChangeEvent& evt)
1097 if (evt.PropertyName == FM_PROP_ROWCOUNT)
1099 // if we're not in the main thread call AdjustRows asynchronously
1100 implAdjustInSolarThread(true);
1101 return;
1104 const DbGridRowRef& xRow = GetCurrentRow();
1105 // no adjustment of the properties is carried out during positioning
1106 Reference<XPropertySet> xSet(evt.Source,UNO_QUERY);
1107 if (!(xRow.is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark()))))
1108 return;
1110 if (evt.PropertyName == FM_PROP_ISMODIFIED)
1112 // modified or clean ?
1113 GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean;
1114 if (eStatus != xRow->GetStatus())
1116 xRow->SetStatus(eStatus);
1117 SolarMutexGuard aGuard;
1118 RowModified(GetCurrentPos());
1123 void FmGridControl::SetDesignMode(bool bMode)
1125 bool bOldMode = IsDesignMode();
1126 DbGridControl::SetDesignMode(bMode);
1127 if (bOldMode == bMode)
1128 return;
1130 if (!bMode)
1132 // cancel selection
1133 markColumn(USHRT_MAX);
1135 else
1137 Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
1138 Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
1139 if (xSelSupplier.is())
1141 Any aSelection = xSelSupplier->getSelection();
1142 Reference< css::beans::XPropertySet > xColumn;
1143 if (aSelection.getValueTypeClass() == TypeClass_INTERFACE)
1144 xColumn.set(aSelection, css::uno::UNO_QUERY);
1145 Reference< XInterface > xCurrent;
1146 for (sal_Int32 i=0; i<xColumns->getCount(); ++i)
1148 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1149 if (xCurrent == xColumn)
1151 markColumn(GetColumnIdFromModelPos(i));
1152 break;
1159 void FmGridControl::DeleteSelectedRows()
1161 if (!m_pSeekCursor)
1162 return;
1164 // how many rows are selected?
1165 sal_Int32 nSelectedRows = GetSelectRowCount();
1167 // the current line should be deleted but it is currently in edit mode
1168 if ( IsCurrentAppending() )
1169 return;
1170 // is the insert row selected
1171 if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
1172 nSelectedRows -= 1;
1174 // nothing to do
1175 if (nSelectedRows <= 0)
1176 return;
1178 // try to confirm the delete
1179 Reference< css::frame::XDispatchProvider > xDispatcher = static_cast<css::frame::XDispatchProvider*>(GetPeer());
1180 if (xDispatcher.is())
1182 css::util::URL aUrl;
1183 aUrl.Complete = FMURL_CONFIRM_DELETION;
1184 Reference< css::util::XURLTransformer > xTransformer(
1185 css::util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
1186 xTransformer->parseStrict( aUrl );
1188 Reference< css::frame::XDispatch > xDispatch = xDispatcher->queryDispatch(aUrl, OUString(), 0);
1189 Reference< css::form::XConfirmDeleteListener > xConfirm(xDispatch, UNO_QUERY);
1190 if (xConfirm.is())
1192 css::sdb::RowChangeEvent aEvent;
1193 aEvent.Source = Reference< XInterface >(*getDataSource());
1194 aEvent.Rows = nSelectedRows;
1195 aEvent.Action = css::sdb::RowChangeAction::DELETE;
1196 if (!xConfirm->confirmDelete(aEvent))
1197 return;
1201 const MultiSelection* pRowSelection = GetSelection();
1202 if ( pRowSelection && pRowSelection->IsAllSelected() )
1204 BeginCursorAction();
1205 CursorWrapper* pCursor = getDataSource();
1206 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*pCursor), UNO_QUERY);
1209 pCursor->beforeFirst();
1210 while( pCursor->next() )
1211 xUpdateCursor->deleteRow();
1213 SetUpdateMode(false);
1214 SetNoSelection();
1216 xUpdateCursor->moveToInsertRow();
1218 catch(const Exception&)
1220 TOOLS_WARN_EXCEPTION("svx", "Exception caught while deleting rows!");
1222 // adapt to the data cursor
1223 AdjustDataSource(true);
1224 EndCursorAction();
1225 SetUpdateMode(true);
1227 else
1229 Reference< css::sdbcx::XDeleteRows > xDeleteThem(Reference< XInterface >(*getDataSource()), UNO_QUERY);
1231 // collect the bookmarks of the selected rows
1232 Sequence < Any> aBookmarks = getSelectionBookmarks();
1234 // determine the next row to position after deletion
1235 Any aBookmark;
1236 bool bNewPos = false;
1237 // if the current row isn't selected we take the row as row after deletion
1238 OSL_ENSURE( GetCurrentRow().is(), "FmGridControl::DeleteSelectedRows: no current row here?" );
1239 // crash reports suggest it can happen we don't have a current row - how?
1240 // #154303# / 2008-04-23 / frank.schoenheit@sun.com
1241 if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().is() )
1243 aBookmark = GetCurrentRow()->GetBookmark();
1244 bNewPos = true;
1246 else
1248 // we look for the first row after the selected block for selection
1249 tools::Long nIdx = LastSelectedRow() + 1;
1250 if (nIdx < GetRowCount() - 1)
1252 // there is a next row to position on
1253 if (SeekCursor(nIdx))
1255 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1257 bNewPos = true;
1258 // if it's not the row for inserting we keep the bookmark
1259 if (!IsInsertionRow(nIdx))
1260 aBookmark = m_pSeekCursor->getBookmark();
1263 else
1265 // we look for the first row before the selected block for selection after deletion
1266 nIdx = FirstSelectedRow() - 1;
1267 if (nIdx >= 0 && SeekCursor(nIdx))
1269 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1271 bNewPos = true;
1272 aBookmark = m_pSeekCursor->getBookmark();
1277 // Are all rows selected?
1278 // Second condition if no insertion line exists
1279 bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows;
1281 BeginCursorAction();
1283 // now delete the row
1284 Sequence<sal_Int32> aDeletedRows;
1285 SetUpdateMode( false );
1288 aDeletedRows = xDeleteThem->deleteRows(aBookmarks);
1290 catch(SQLException&)
1293 SetUpdateMode( true );
1295 // how many rows are deleted?
1296 sal_Int32 nDeletedRows = static_cast<sal_Int32>(std::count_if(std::cbegin(aDeletedRows), std::cend(aDeletedRows),
1297 [](const sal_Int32 nRow) { return nRow != 0; }));
1299 // have rows been deleted?
1300 if (nDeletedRows)
1302 SetUpdateMode(false);
1303 SetNoSelection();
1306 // did we delete all the rows than try to move to the next possible row
1307 if (nDeletedRows == aDeletedRows.getLength())
1309 // there exists a new position to move on
1310 if (bNewPos)
1312 if (aBookmark.hasValue())
1313 getDataSource()->moveToBookmark(aBookmark);
1314 // no valid bookmark so move to the insert row
1315 else
1317 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1318 xUpdateCursor->moveToInsertRow();
1321 else
1323 Reference< css::beans::XPropertySet > xSet(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1325 sal_Int32 nRecordCount(0);
1326 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1327 if ( m_pDataCursor->rowDeleted() )
1328 --nRecordCount;
1330 // there are no rows left and we have an insert row
1331 if (!nRecordCount && GetEmptyRow().is())
1333 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1334 xUpdateCursor->moveToInsertRow();
1336 else if (nRecordCount)
1337 // move to the first row
1338 getDataSource()->first();
1341 // not all the rows where deleted, so move to the first row which remained in the resultset
1342 else
1344 auto pRow = std::find(std::cbegin(aDeletedRows), std::cend(aDeletedRows), 0);
1345 if (pRow != std::cend(aDeletedRows))
1347 auto i = static_cast<sal_Int32>(std::distance(std::cbegin(aDeletedRows), pRow));
1348 getDataSource()->moveToBookmark(aBookmarks[i]);
1352 catch(const Exception&)
1356 // positioning went wrong so try to move to the first row
1357 getDataSource()->first();
1359 catch(const Exception&)
1364 // adapt to the data cursor
1365 AdjustDataSource(true);
1367 // not all rows could be deleted;
1368 // never select again there the ones that could not be deleted
1369 if (nDeletedRows < nSelectedRows)
1371 // were all selected
1372 if (bAllSelected)
1374 SelectAll();
1375 if (IsInsertionRow(GetRowCount() - 1)) // not the insertion row
1376 SelectRow(GetRowCount() - 1, false);
1378 else
1380 // select the remaining rows
1381 for (const sal_Int32 nSuccess : aDeletedRows)
1385 if (!nSuccess)
1387 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
1388 SetSeekPos(m_pSeekCursor->getRow() - 1);
1389 SelectRow(GetSeekPos());
1392 catch(const Exception&)
1394 // keep the seekpos in all cases
1395 SetSeekPos(m_pSeekCursor->getRow() - 1);
1401 EndCursorAction();
1402 SetUpdateMode(true);
1404 else // row could not be deleted
1406 EndCursorAction();
1409 // currentrow is the insert row?
1410 if (!IsCurrentAppending())
1411 getDataSource()->refreshRow();
1413 catch(const Exception&)
1419 // if there is no selection anymore we can start editing
1420 if (!GetSelectRowCount())
1421 ActivateCell();
1424 // XCurrentRecordListener
1425 void FmGridControl::positioned()
1427 SAL_INFO("svx.fmcomp", "FmGridControl::positioned");
1428 // position on the data source (force it to be done in the main thread)
1429 implAdjustInSolarThread(false);
1432 bool FmGridControl::commit()
1434 // execute commit only if an update is not already executed by the
1435 // css::form::component::GridControl
1436 if (!IsUpdating())
1438 if (Controller().is() && Controller()->IsValueChangedFromSaved())
1440 if (!SaveModified())
1441 return false;
1444 return true;
1447 void FmGridControl::inserted()
1449 const DbGridRowRef& xRow = GetCurrentRow();
1450 if (!xRow.is())
1451 return;
1453 // line has been inserted, then reset the status and mode
1454 xRow->SetState(m_pDataCursor.get(), false);
1455 xRow->SetNew(false);
1459 VclPtr<BrowserHeader> FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
1461 DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" );
1462 return VclPtr<FmGridHeader>::Create( pParent );
1465 void FmGridControl::markColumn(sal_uInt16 nId)
1467 if (!(GetHeaderBar() && m_nMarkedColumnId != nId))
1468 return;
1470 // deselect
1471 if (m_nMarkedColumnId != BROWSER_INVALIDID)
1473 HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HeaderBarItemBits::FLAT;
1474 GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits);
1478 if (nId != BROWSER_INVALIDID)
1480 HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HeaderBarItemBits::FLAT;
1481 GetHeaderBar()->SetItemBits(nId, aBits);
1483 m_nMarkedColumnId = nId;
1486 bool FmGridControl::isColumnMarked(sal_uInt16 nId) const
1488 return m_nMarkedColumnId == nId;
1491 tools::Long FmGridControl::QueryMinimumRowHeight()
1493 tools::Long const nMinimalLogicHeight = 20; // 0.2 cm
1494 tools::Long nMinimalPixelHeight = LogicToPixel(Point(0, nMinimalLogicHeight), MapMode(MapUnit::Map10thMM)).Y();
1495 return CalcZoom( nMinimalPixelHeight );
1498 void FmGridControl::RowHeightChanged()
1500 DbGridControl::RowHeightChanged();
1502 Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY );
1503 DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" );
1504 if ( !xModel.is() )
1505 return;
1509 sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() );
1510 Any aProperty( static_cast<sal_Int32>(PixelToLogic( Point(0, nUnzoomedPixelHeight), MapMode(MapUnit::Map10thMM)).Y()) );
1511 xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty );
1513 catch( const Exception& )
1515 TOOLS_WARN_EXCEPTION( "svx", "FmGridControl::RowHeightChanged" );
1519 void FmGridControl::ColumnResized(sal_uInt16 nId)
1521 DbGridControl::ColumnResized(nId);
1523 // transfer value to the model
1524 DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1525 const Reference< css::beans::XPropertySet >& xColModel(pCol->getModel());
1526 if (xColModel.is())
1528 Any aWidth;
1529 sal_Int32 nColumnWidth = GetColumnWidth(nId);
1530 nColumnWidth = CalcReverseZoom(nColumnWidth);
1531 // convert to 10THMM
1532 aWidth <<= static_cast<sal_Int32>(PixelToLogic(Point(nColumnWidth, 0), MapMode(MapUnit::Map10thMM)).X());
1533 xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth);
1537 void FmGridControl::CellModified()
1539 DbGridControl::CellModified();
1540 GetPeer()->CellModified();
1543 void FmGridControl::BeginCursorAction()
1545 DbGridControl::BeginCursorAction();
1546 m_pPeer->stopCursorListening();
1549 void FmGridControl::EndCursorAction()
1551 m_pPeer->startCursorListening();
1552 DbGridControl::EndCursorAction();
1555 void FmGridControl::ColumnMoved(sal_uInt16 nId)
1557 m_bInColumnMove = true;
1559 DbGridControl::ColumnMoved(nId);
1560 Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
1562 if (xColumns.is())
1564 // locate the column and move in the model;
1565 // get ColumnPos
1566 DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1567 Reference< css::beans::XPropertySet > xCol;
1569 // inserting must be based on the column positions
1570 sal_Int32 i;
1571 Reference< XInterface > xCurrent;
1572 for (i = 0; !xCol.is() && i < xColumns->getCount(); i++)
1574 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1575 if (xCurrent == pCol->getModel())
1577 xCol = pCol->getModel();
1578 break;
1582 DBG_ASSERT(i < xColumns->getCount(), "Wrong css::sdbcx::Index");
1583 xColumns->removeByIndex(i);
1584 Any aElement;
1585 aElement <<= xCol;
1586 xColumns->insertByIndex(GetModelColumnPos(nId), aElement);
1587 pCol->setModel(xCol);
1588 // if the column which is shown here is selected ...
1589 if ( isColumnSelected(pCol) )
1590 markColumn(nId); // ... -> mark it
1593 m_bInColumnMove = false;
1596 void FmGridControl::InitColumnsByModels(const Reference< css::container::XIndexContainer >& xColumns)
1598 // reset columns;
1599 // if there is only one HandleColumn, then don't
1600 if (GetModelColCount())
1602 RemoveColumns();
1603 InsertHandleColumn();
1606 if (!xColumns.is())
1607 return;
1609 SetUpdateMode(false);
1611 // inserting must be based on the column positions
1612 sal_Int32 i;
1613 Any aWidth;
1614 for (i = 0; i < xColumns->getCount(); ++i)
1616 Reference< css::beans::XPropertySet > xCol(
1617 xColumns->getByIndex(i), css::uno::UNO_QUERY);
1619 OUString aName(
1620 comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)));
1622 aWidth = xCol->getPropertyValue(FM_PROP_WIDTH);
1623 sal_Int32 nWidth = 0;
1624 if (aWidth >>= nWidth)
1625 nWidth = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
1627 AppendColumn(aName, static_cast<sal_uInt16>(nWidth));
1628 DbGridColumn* pCol = DbGridControl::GetColumns()[ i ].get();
1629 pCol->setModel(xCol);
1632 // and now remove the hidden columns as well
1633 // (we did not already make it in the upper loop, since we would then have gotten
1634 // problems with the IDs of the columns: AppendColumn allocates them automatically,
1635 // but the column _after_ a hidden one needs an ID increased by one ...)
1636 Any aHidden;
1637 for (i = 0; i < xColumns->getCount(); ++i)
1639 Reference< css::beans::XPropertySet > xCol( xColumns->getByIndex(i), css::uno::UNO_QUERY);
1640 aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN);
1641 if (::comphelper::getBOOL(aHidden))
1642 HideColumn(GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
1645 SetUpdateMode(true);
1648 void FmGridControl::InitColumnByField(
1649 DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel,
1650 const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex )
1652 DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" );
1654 // lookup the column which belongs to the control source
1655 OUString sFieldName;
1656 _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName;
1657 Reference< XPropertySet > xField;
1658 _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
1661 if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length
1662 _rxFieldsByNames->getByName( sFieldName ) >>= xField;
1664 // determine the position of this column
1665 sal_Int32 nFieldPos = -1;
1666 if ( xField.is() )
1668 Reference< XPropertySet > xCheck;
1669 sal_Int32 nFieldCount = _rxFieldsByIndex->getCount();
1670 for ( sal_Int32 i = 0; i < nFieldCount; ++i)
1672 _rxFieldsByIndex->getByIndex( i ) >>= xCheck;
1673 if ( xField.get() == xCheck.get() )
1675 nFieldPos = i;
1676 break;
1681 if ( xField.is() && ( nFieldPos >= 0 ) )
1683 // some data types are not allowed
1684 sal_Int32 nDataType = DataType::OTHER;
1685 xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType;
1687 bool bIllegalType = false;
1688 switch ( nDataType )
1690 case DataType::BLOB:
1691 case DataType::LONGVARBINARY:
1692 case DataType::BINARY:
1693 case DataType::VARBINARY:
1694 case DataType::OTHER:
1695 bIllegalType = true;
1696 break;
1699 if ( bIllegalType )
1701 _pColumn->SetObject( static_cast<sal_Int16>(nFieldPos) );
1702 return;
1706 // the control type is determined by the ColumnServiceName
1707 static constexpr OUString s_sPropColumnServiceName = u"ColumnServiceName"_ustr;
1708 if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) )
1709 return;
1711 _pColumn->setModel( _rxColumnModel );
1713 OUString sColumnServiceName;
1714 _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName;
1716 sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName );
1717 _pColumn->CreateControl( nFieldPos, xField, nTypeId );
1720 void FmGridControl::InitColumnsByFields(const Reference< css::container::XIndexAccess >& _rxFields)
1722 if ( !_rxFields.is() )
1723 return;
1725 // initialize columns
1726 Reference< XIndexContainer > xColumns( GetPeer()->getColumns() );
1727 Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY );
1729 // inserting must be based on the column positions
1730 for (sal_Int32 i = 0; i < xColumns->getCount(); i++)
1732 DbGridColumn* pCol = GetColumns()[ i ].get();
1733 OSL_ENSURE(pCol,"No grid column!");
1734 if ( pCol )
1736 Reference< XPropertySet > xColumnModel(
1737 xColumns->getByIndex( i ), css::uno::UNO_QUERY);
1739 InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields );
1744 void FmGridControl::HideColumn(sal_uInt16 nId)
1746 DbGridControl::HideColumn(nId);
1748 sal_uInt16 nPos = GetModelColumnPos(nId);
1749 if (nPos == sal_uInt16(-1))
1750 return;
1752 DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1753 if (pColumn->IsHidden())
1754 GetPeer()->columnHidden(pColumn);
1756 if (nId == m_nMarkedColumnId)
1757 m_nMarkedColumnId = sal_uInt16(-1);
1760 bool FmGridControl::isColumnSelected(DbGridColumn const * _pColumn) const
1762 assert(_pColumn && "Column can not be null!");
1763 bool bSelected = false;
1764 // if the column which is shown here is selected ...
1765 Reference< css::view::XSelectionSupplier > xSelSupplier(GetPeer()->getColumns(), UNO_QUERY);
1766 if ( xSelSupplier.is() )
1768 Reference< css::beans::XPropertySet > xColumn;
1769 xSelSupplier->getSelection() >>= xColumn;
1770 bSelected = (xColumn.get() == _pColumn->getModel().get());
1772 return bSelected;
1775 void FmGridControl::ShowColumn(sal_uInt16 nId)
1777 DbGridControl::ShowColumn(nId);
1779 sal_uInt16 nPos = GetModelColumnPos(nId);
1780 if (nPos == sal_uInt16(-1))
1781 return;
1783 DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1784 if (!pColumn->IsHidden())
1785 GetPeer()->columnVisible(pColumn);
1787 // if the column which is shown here is selected ...
1788 if ( isColumnSelected(pColumn) )
1789 markColumn(nId); // ... -> mark it
1792 bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks)
1794 SolarMutexGuard aGuard;
1795 // need to lock the SolarMutex so that no paint call disturbs us ...
1797 if ( !m_pSeekCursor )
1799 OSL_FAIL( "FmGridControl::selectBookmarks: no seek cursor!" );
1800 return false;
1803 SetNoSelection();
1805 bool bAllSuccessful = true;
1808 for (const Any& rBookmark : _rBookmarks)
1810 // move the seek cursor to the row given
1811 if (m_pSeekCursor->moveToBookmark(rBookmark))
1812 SelectRow( m_pSeekCursor->getRow() - 1);
1813 else
1814 bAllSuccessful = false;
1817 catch(Exception&)
1819 OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
1820 return false;
1823 return bAllSuccessful;
1826 Sequence< Any> FmGridControl::getSelectionBookmarks()
1828 // lock our update so no paint-triggered seeks interfere ...
1829 SetUpdateMode(false);
1831 sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0;
1832 Sequence< Any> aBookmarks(nSelectedRows);
1833 if ( nSelectedRows )
1835 Any* pBookmarks = aBookmarks.getArray();
1837 // (I'm not sure if the problem isn't deeper: The scenario: a large table displayed by a grid with a
1838 // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress
1839 // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which made a
1840 // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition.
1841 // Unfortunately the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the
1842 // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning
1843 // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails.
1844 // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relies_ on
1845 // the first one, should be secured against recursion, with a broad-minded interpretation of "recursion": if any of these
1846 // code parts is executed, no other should be accessible. But this sounds very difficult to achieve...
1847 // )
1849 // The next problem caused by the same behavior (SeekCursor causes a propertyChanged): when adjusting rows we implicitly
1850 // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results.
1851 // That's why we _first_ collect the indices of the selected rows and _then_ their bookmarks.
1852 tools::Long nIdx = FirstSelectedRow();
1853 while (nIdx != BROWSER_ENDOFSELECTION)
1855 // (we misuse the bookmarks array for this ...)
1856 pBookmarks[i++] <<= static_cast<sal_Int32>(nIdx);
1857 nIdx = NextSelectedRow();
1859 DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indices !");
1861 for (i=0; i<nSelectedRows; ++i)
1863 nIdx = ::comphelper::getINT32(pBookmarks[i]);
1864 if (IsInsertionRow(nIdx))
1866 // do not delete empty row
1867 aBookmarks.realloc(--nSelectedRows);
1868 SelectRow(nIdx, false); // cancel selection for empty row
1869 break;
1872 // first, position the data cursor on the selected block
1873 if (SeekCursor(nIdx))
1875 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1877 pBookmarks[i] = m_pSeekCursor->getBookmark();
1879 #ifdef DBG_UTIL
1880 else
1881 OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
1882 #endif
1885 SetUpdateMode(true);
1887 // if one of the SeekCursor-calls failed...
1888 aBookmarks.realloc(i);
1890 // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems.
1891 // but this would be incompatible as we need a locking flag, then...)
1893 return aBookmarks;
1896 namespace
1898 OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const OUString& _sPropName)
1900 OUString sRetText;
1901 if ( _pPeer && _nPosition != -1)
1903 Reference<XIndexContainer> xIndex = _pPeer->getColumns();
1904 if ( xIndex.is() && xIndex->getCount() > _nPosition )
1906 Reference<XPropertySet> xProp;
1907 xIndex->getByIndex( _nPosition ) >>= xProp;
1908 if ( xProp.is() )
1910 try {
1911 xProp->getPropertyValue( _sPropName ) >>= sRetText;
1912 } catch (UnknownPropertyException const&) {
1913 TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
1918 return sRetText;
1922 // Object data and state
1923 OUString FmGridControl::GetAccessibleObjectName( AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1925 OUString sRetText;
1926 switch( _eObjType )
1928 case AccessibleBrowseBoxObjType::BrowseBox:
1929 if ( GetPeer() )
1931 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1932 if ( xProp.is() )
1933 xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText;
1935 break;
1936 case AccessibleBrowseBoxObjType::ColumnHeaderCell:
1937 sRetText = getColumnPropertyFromPeer(
1938 GetPeer(),
1939 GetModelColumnPos(
1940 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1941 FM_PROP_LABEL);
1942 break;
1943 default:
1944 sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition);
1946 return sRetText;
1949 OUString FmGridControl::GetAccessibleObjectDescription( AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1951 OUString sRetText;
1952 switch( _eObjType )
1954 case AccessibleBrowseBoxObjType::BrowseBox:
1955 if ( GetPeer() )
1957 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1958 if ( xProp.is() )
1960 xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText;
1961 if ( sRetText.isEmpty() )
1962 xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText;
1965 break;
1966 case AccessibleBrowseBoxObjType::ColumnHeaderCell:
1967 sRetText = getColumnPropertyFromPeer(
1968 GetPeer(),
1969 GetModelColumnPos(
1970 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1971 FM_PROP_HELPTEXT);
1972 if ( sRetText.isEmpty() )
1973 sRetText = getColumnPropertyFromPeer(
1974 GetPeer(),
1975 GetModelColumnPos(
1976 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1977 FM_PROP_DESCRIPTION);
1979 break;
1980 default:
1981 sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition);
1983 return sRetText;
1986 void FmGridControl::Select()
1988 DbGridControl::Select();
1989 // ... does it affect our columns?
1990 const MultiSelection* pColumnSelection = GetColumnSelection();
1992 sal_uInt16 nSelectedColumn =
1993 pColumnSelection && pColumnSelection->GetSelectCount()
1994 ? sal::static_int_cast< sal_uInt16 >(
1995 const_cast<MultiSelection*>(pColumnSelection)->FirstSelected())
1996 : SAL_MAX_UINT16;
1997 // the HandleColumn is not selected
1998 switch (nSelectedColumn)
2000 case SAL_MAX_UINT16: break; // no selection
2001 case 0 : nSelectedColumn = SAL_MAX_UINT16; break;
2002 // handle col can't be selected
2003 default :
2004 // get the model col pos instead of the view col pos
2005 nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1));
2006 break;
2009 if (nSelectedColumn == m_nCurrentSelectedColumn)
2010 return;
2012 // BEFORE calling the select at the SelectionSupplier!
2013 m_nCurrentSelectedColumn = nSelectedColumn;
2015 if (m_bSelecting)
2016 return;
2018 m_bSelecting = true;
2022 Reference< XIndexAccess > xColumns = GetPeer()->getColumns();
2023 Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
2024 if (xSelSupplier.is())
2026 if (nSelectedColumn != SAL_MAX_UINT16)
2028 Reference< XPropertySet > xColumn(
2029 xColumns->getByIndex(nSelectedColumn),
2030 css::uno::UNO_QUERY);
2031 xSelSupplier->select(Any(xColumn));
2033 else
2035 xSelSupplier->select(Any());
2039 catch(Exception&)
2044 m_bSelecting = false;
2048 void FmGridControl::KeyInput( const KeyEvent& rKEvt )
2050 bool bDone = false;
2051 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
2052 if ( IsDesignMode()
2053 && !rKeyCode.IsShift()
2054 && !rKeyCode.IsMod1()
2055 && !rKeyCode.IsMod2()
2056 && GetParent() )
2058 switch ( rKeyCode.GetCode() )
2060 case KEY_ESCAPE:
2061 GetParent()->GrabFocus();
2062 bDone = true;
2063 break;
2064 case KEY_DELETE:
2065 if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 )
2067 Reference< css::container::XIndexContainer > xCols(GetPeer()->getColumns());
2068 if ( xCols.is() )
2072 if ( m_nCurrentSelectedColumn < xCols->getCount() )
2074 Reference< XInterface > xCol;
2075 xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol;
2076 xCols->removeByIndex(m_nCurrentSelectedColumn);
2077 ::comphelper::disposeComponent(xCol);
2080 catch(const Exception&)
2082 TOOLS_WARN_EXCEPTION("svx", "exception occurred while deleting a column");
2086 bDone = true;
2087 break;
2090 if ( !bDone )
2091 DbGridControl::KeyInput( rKEvt );
2094 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */