bump product version to 7.2.5.1
[LibreOffice.git] / svx / source / fmcomp / fmgridcl.cxx
blob10e5e980a74f1efeeaef9b369808b491a778051d
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 <tools/diagnose_ex.h>
68 #include <vcl/help.hxx>
69 #include <vcl/image.hxx>
70 #include <vcl/settings.hxx>
71 #include <sal/log.hxx>
72 #include <i18nlangtag/languagetag.hxx>
73 #include <memory>
74 #include <string_view>
76 using namespace ::com::sun::star::uno;
77 using namespace ::com::sun::star::view;
78 using namespace ::com::sun::star::beans;
79 using namespace ::com::sun::star::lang;
80 using namespace ::com::sun::star::sdbcx;
81 using namespace ::com::sun::star::sdbc;
82 using namespace ::com::sun::star::sdb;
83 using namespace ::com::sun::star::form;
84 using namespace ::com::sun::star::util;
85 using namespace ::com::sun::star::container;
86 using namespace ::cppu;
87 using namespace ::svxform;
88 using namespace ::svx;
89 using namespace ::dbtools;
91 struct FmGridHeaderData
93 ODataAccessDescriptor aDropData;
94 Point aDropPosPixel;
95 sal_Int8 nDropAction;
96 Reference< XInterface > xDroppedStatement;
97 Reference< XInterface > xDroppedResultSet;
100 static void InsertMenuItem(weld::Menu& rMenu, int nMenuPos, std::string_view id, const OUString& rText, const OUString& rImgId)
102 rMenu.insert(nMenuPos, OUString::fromUtf8(id), rText, &rImgId, nullptr, nullptr, TRISTATE_INDET);
105 FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits)
106 :EditBrowserHeader(pParent, nWinBits)
107 ,DropTargetHelper(this)
108 ,m_pImpl(new FmGridHeaderData)
112 FmGridHeader::~FmGridHeader()
114 disposeOnce();
117 void FmGridHeader::dispose()
119 m_pImpl.reset();
120 DropTargetHelper::dispose();
121 svt::EditBrowserHeader::dispose();
124 sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const
126 return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId);
129 void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId)
131 sal_uInt16 nPos = GetModelColumnPos(nColumnId);
132 Reference< XIndexAccess > xColumns = static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns();
133 if ( nPos < xColumns->getCount() )
135 Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
136 if ( xSelSupplier.is() )
138 Reference< XPropertySet > xColumn;
139 xColumns->getByIndex(nPos) >>= xColumn;
140 xSelSupplier->select(makeAny(xColumn));
145 void FmGridHeader::Select()
147 EditBrowserHeader::Select();
148 notifyColumnSelect(GetCurItemId());
151 void FmGridHeader::RequestHelp( const HelpEvent& rHEvt )
153 sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
154 if ( nItemId )
156 if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
158 tools::Rectangle aItemRect = GetItemRect( nItemId );
159 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
160 aItemRect.SetLeft( aPt.X() );
161 aItemRect.SetTop( aPt.Y() );
162 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
163 aItemRect.SetRight( aPt.X() );
164 aItemRect.SetBottom( aPt.Y() );
166 sal_uInt16 nPos = GetModelColumnPos(nItemId);
167 Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
170 Reference< css::beans::XPropertySet > xColumn(xColumns->getByIndex(nPos),UNO_QUERY);
171 OUString aHelpText;
172 xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText;
173 if ( aHelpText.isEmpty() )
174 xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText;
175 if ( !aHelpText.isEmpty() )
177 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
178 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText );
179 else
180 Help::ShowQuickHelp( this, aItemRect, aHelpText );
181 return;
184 catch(Exception&)
186 return;
190 EditBrowserHeader::RequestHelp( rHEvt );
193 sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt )
195 // drop allowed in design mode only
196 if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
197 return DND_ACTION_NONE;
199 // search for recognized formats
200 const DataFlavorExVector& rFlavors = GetDataFlavorExVector();
201 if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::FIELD_DESCRIPTOR))
202 return rEvt.mnAction;
204 return DND_ACTION_NONE;
207 sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt )
209 if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
210 return DND_ACTION_NONE;
212 TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable);
214 // check the formats
215 bool bColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
216 bool bFieldDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
217 if (!bColumnDescriptor && !bFieldDescriptor)
219 OSL_FAIL("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!");
220 return DND_ACTION_NONE;
223 // extract the descriptor
224 OUString sDatasource, sCommand, sFieldName,sDatabaseLocation;
225 sal_Int32 nCommandType = CommandType::COMMAND;
226 Reference< XPreparedStatement > xStatement;
227 Reference< XResultSet > xResultSet;
228 Reference< XPropertySet > xField;
229 Reference< XConnection > xConnection;
231 ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData);
232 if (aColumn.has(DataAccessDescriptorProperty::DataSource)) aColumn[DataAccessDescriptorProperty::DataSource] >>= sDatasource;
233 if (aColumn.has(DataAccessDescriptorProperty::DatabaseLocation)) aColumn[DataAccessDescriptorProperty::DatabaseLocation] >>= sDatabaseLocation;
234 if (aColumn.has(DataAccessDescriptorProperty::Command)) aColumn[DataAccessDescriptorProperty::Command] >>= sCommand;
235 if (aColumn.has(DataAccessDescriptorProperty::CommandType)) aColumn[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
236 if (aColumn.has(DataAccessDescriptorProperty::ColumnName)) aColumn[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
237 if (aColumn.has(DataAccessDescriptorProperty::ColumnObject))aColumn[DataAccessDescriptorProperty::ColumnObject] >>= xField;
238 if (aColumn.has(DataAccessDescriptorProperty::Connection)) aColumn[DataAccessDescriptorProperty::Connection] >>= xConnection;
240 if ( sFieldName.isEmpty()
241 || sCommand.isEmpty()
242 || ( sDatasource.isEmpty()
243 && sDatabaseLocation.isEmpty()
244 && !xConnection.is()
248 OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
249 return DND_ACTION_NONE;
254 // need a connection
255 if (!xConnection.is())
256 { // the transferable did not contain the connection -> build an own one
259 OUString sSignificantSource( sDatasource.isEmpty() ? sDatabaseLocation : sDatasource );
260 xConnection = getConnection_withFeedback(sSignificantSource, OUString(), OUString(),
261 static_cast<FmGridControl*>(GetParent())->getContext(), nullptr );
263 catch(NoSuchElementException&)
264 { // allowed, means sDatasource isn't a valid data source name...
266 catch(Exception&)
268 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
271 if (!xConnection.is())
273 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
274 return DND_ACTION_NONE;
278 // try to obtain the column object
279 if (!xField.is())
281 #ifdef DBG_UTIL
282 Reference< XServiceInfo > xServiceInfo(xConnection, UNO_QUERY);
283 DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)");
284 #endif
286 Reference< XNameAccess > xFields;
287 switch (nCommandType)
289 case CommandType::TABLE:
291 Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
292 Reference< XColumnsSupplier > xSupplyColumns;
293 xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns;
294 xFields = xSupplyColumns->getColumns();
296 break;
297 case CommandType::QUERY:
299 Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
300 Reference< XColumnsSupplier > xSupplyColumns;
301 xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns;
302 xFields = xSupplyColumns->getColumns();
304 break;
305 default:
307 xStatement = xConnection->prepareStatement(sCommand);
308 // not interested in any results
310 Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY);
311 xStatProps->setPropertyValue("MaxRows", makeAny(sal_Int32(0)));
313 xResultSet = xStatement->executeQuery();
314 Reference< XColumnsSupplier > xSupplyCols(xResultSet, UNO_QUERY);
315 if (xSupplyCols.is())
316 xFields = xSupplyCols->getColumns();
320 if (xFields.is() && xFields->hasByName(sFieldName))
321 xFields->getByName(sFieldName) >>= xField;
323 if (!xField.is())
325 ::comphelper::disposeComponent(xStatement);
326 return DND_ACTION_NONE;
330 // do the drop asynchronously
331 // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu)
332 m_pImpl->aDropData = aColumn;
333 m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] <<= xConnection;
334 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] <<= xField;
336 m_pImpl->nDropAction = _rEvt.mnAction;
337 m_pImpl->aDropPosPixel = _rEvt.maPosPixel;
338 m_pImpl->xDroppedStatement = xStatement;
339 m_pImpl->xDroppedResultSet = xResultSet;
341 PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop), nullptr, true);
343 catch (Exception&)
345 TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
346 ::comphelper::disposeComponent(xStatement);
347 return DND_ACTION_NONE;
350 return DND_ACTION_LINK;
353 IMPL_LINK_NOARG( FmGridHeader, OnAsyncExecuteDrop, void*, void )
355 OUString sCommand, sFieldName,sURL;
356 sal_Int32 nCommandType = CommandType::COMMAND;
357 Reference< XPropertySet > xField;
358 Reference< XConnection > xConnection;
360 OUString sDatasource = m_pImpl->aDropData.getDataSource();
361 if ( sDatasource.isEmpty() && m_pImpl->aDropData.has(DataAccessDescriptorProperty::ConnectionResource) )
362 m_pImpl->aDropData[DataAccessDescriptorProperty::ConnectionResource] >>= sURL;
363 m_pImpl->aDropData[DataAccessDescriptorProperty::Command] >>= sCommand;
364 m_pImpl->aDropData[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
365 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
366 m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] >>= xConnection;
367 m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] >>= xField;
371 // need number formats
372 Reference< XNumberFormatsSupplier > xSupplier = getNumberFormats(xConnection, true);
373 Reference< XNumberFormats > xNumberFormats;
374 if (xSupplier.is())
375 xNumberFormats = xSupplier->getNumberFormats();
376 if (!xNumberFormats.is())
378 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
379 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
380 return;
383 // The field now needs two pieces of information:
384 // a.) Name of the field for label and ControlSource
385 // b.) FormatKey, to determine which field is to be created
386 sal_Int32 nDataType = 0;
387 xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType;
388 // these datatypes can not be processed in Gridcontrol
389 switch (nDataType)
391 case DataType::BLOB:
392 case DataType::LONGVARBINARY:
393 case DataType::BINARY:
394 case DataType::VARBINARY:
395 case DataType::OTHER:
396 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
397 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
398 return;
401 // Creating the column
402 Reference< XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
403 Reference< XGridColumnFactory > xFactory(xCols, UNO_QUERY);
405 sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel);
406 // insert position, always before the current column
407 sal_uInt16 nPos = GetModelColumnPos(nColId);
408 Reference< XPropertySet > xCol, xSecondCol;
410 // Create Column based on type, default textfield
411 std::vector<OString> aPossibleTypes;
412 std::vector<OUString> aImgResId;
413 std::vector<const char*> aStrResId;
415 switch (nDataType)
417 case DataType::BIT:
418 case DataType::BOOLEAN:
419 aPossibleTypes.emplace_back(FM_COL_CHECKBOX);
420 aImgResId.emplace_back(RID_SVXBMP_CHECKBOX);
421 aStrResId.emplace_back(RID_STR_PROPTITLE_CHECKBOX);
422 break;
423 case DataType::TINYINT:
424 case DataType::SMALLINT:
425 case DataType::INTEGER:
426 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
427 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
428 aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
429 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
430 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
431 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
432 break;
433 case DataType::REAL:
434 case DataType::DOUBLE:
435 case DataType::NUMERIC:
436 case DataType::DECIMAL:
437 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
438 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
439 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
440 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
441 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
442 aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
443 break;
444 case DataType::TIMESTAMP:
445 aPossibleTypes.emplace_back("dateandtimefield");
446 aImgResId.emplace_back(RID_SVXBMP_DATE_N_TIME_FIELDS);
447 aStrResId.emplace_back(RID_STR_DATE_AND_TIME);
448 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
449 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
450 aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
451 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
452 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
453 aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
454 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
455 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
456 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
457 break;
458 case DataType::DATE:
459 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
460 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
461 aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
462 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
463 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
464 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
465 break;
466 case DataType::TIME:
467 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
468 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
469 aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
470 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
471 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
472 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
473 break;
474 case DataType::CHAR:
475 case DataType::VARCHAR:
476 case DataType::LONGVARCHAR:
477 default:
478 aPossibleTypes.emplace_back(FM_COL_TEXTFIELD);
479 aImgResId.emplace_back(RID_SVXBMP_EDITBOX);
480 aStrResId.emplace_back(RID_STR_PROPTITLE_EDIT);
481 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
482 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
483 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
484 break;
486 // if it's a currency field, a "currency field" option
489 if ( ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField)
490 && ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY)))
492 aPossibleTypes.insert(aPossibleTypes.begin(), FM_COL_CURRENCYFIELD);
493 aImgResId.insert(aImgResId.begin(), RID_SVXBMP_CURRENCYFIELD);
494 aStrResId.insert(aStrResId.begin(), RID_STR_PROPTITLE_CURRENCYFIELD);
497 catch (const Exception&)
499 TOOLS_WARN_EXCEPTION("svx", "");
502 assert(aPossibleTypes.size() == aImgResId.size());
504 bool bDateNTimeCol = false;
505 if (!aPossibleTypes.empty())
507 OString sPreferredType = aPossibleTypes[0];
508 if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.size() > 1))
510 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/colsmenu.ui"));
511 std::unique_ptr<weld::Menu> xTypeMenu(xBuilder->weld_menu("insertmenu"));
513 int nMenuPos = 0;
514 std::vector<OString>::const_iterator iter;
515 std::vector<const char*>::const_iterator striter;
516 std::vector<OUString>::const_iterator imgiter;
517 for (iter = aPossibleTypes.begin(), imgiter = aImgResId.begin(), striter = aStrResId.begin();
518 iter != aPossibleTypes.end(); ++iter, ++striter, ++imgiter)
520 InsertMenuItem(*xTypeMenu, nMenuPos++, *iter, SvxResId(*striter), *imgiter);
523 ::tools::Rectangle aRect(m_pImpl->aDropPosPixel, Size(1,1));
524 weld::Window* pParent = weld::GetPopupParent(*this, aRect);
525 OString sResult = xTypeMenu->popup_at_rect(pParent, aRect);
526 if (!sResult.isEmpty())
527 sPreferredType = sResult;
530 bDateNTimeCol = sPreferredType == "dateandtimefield";
531 sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1;
532 OUString sFieldService;
533 while (nColCount--)
535 if (bDateNTimeCol)
536 sPreferredType = nColCount ? FM_COL_DATEFIELD : FM_COL_TIMEFIELD;
538 sFieldService = OUString::fromUtf8(sPreferredType);
539 Reference< XPropertySet > xThisRoundCol;
540 if ( !sFieldService.isEmpty() )
541 xThisRoundCol = xFactory->createColumn(sFieldService);
542 if (nColCount)
543 xSecondCol = xThisRoundCol;
544 else
545 xCol = xThisRoundCol;
549 if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is()))
551 ::comphelper::disposeComponent(xCol); // in case only the creation of the second column failed
552 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
553 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
554 return;
557 if (bDateNTimeCol)
559 OUString sTimePostfix(SvxResId(RID_STR_POSTFIX_TIME));
560 xCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sTimePostfix ) ) );
562 OUString sDatePostfix(SvxResId( RID_STR_POSTFIX_DATE));
563 xSecondCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sDatePostfix ) ) );
565 else
566 xCol->setPropertyValue(FM_PROP_LABEL, makeAny(sFieldName));
568 // insert now
569 Any aElement;
570 aElement <<= xCol;
572 xCols->insertByIndex(nPos, aElement);
574 FormControlFactory aControlFactory;
575 aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol );
576 FormControlFactory::initializeFieldDependentProperties( xField, xCol, xNumberFormats );
578 xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
579 if ( xSecondCol.is() )
580 xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
582 if (bDateNTimeCol)
584 OUString aPostfix[] = {
585 SvxResId(RID_STR_POSTFIX_DATE),
586 SvxResId(RID_STR_POSTFIX_TIME)
589 for ( size_t i=0; i<2; ++i )
591 OUString sPurePostfix = comphelper::string::stripStart(aPostfix[i], ' ');
592 sPurePostfix = comphelper::string::stripStart(sPurePostfix, '(');
593 sPurePostfix = comphelper::string::stripEnd(sPurePostfix, ')');
594 OUString sRealName = sFieldName + "_" + sPurePostfix;
595 if (i)
596 xSecondCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
597 else
598 xCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
601 else
602 xCol->setPropertyValue(FM_PROP_NAME, makeAny(sFieldName));
604 if (bDateNTimeCol)
606 aElement <<= xSecondCol;
607 xCols->insertByIndex(nPos == sal_uInt16(-1) ? nPos : ++nPos, aElement);
610 // is the component::Form tied to the database?
611 Reference< XFormComponent > xFormCp(xCols, UNO_QUERY);
612 Reference< XPropertySet > xForm(xFormCp->getParent(), UNO_QUERY);
613 if (xForm.is())
615 if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).isEmpty())
617 if ( !sDatasource.isEmpty() )
618 xForm->setPropertyValue(FM_PROP_DATASOURCE, makeAny(sDatasource));
619 else
620 xForm->setPropertyValue(FM_PROP_URL, makeAny(sURL));
623 if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).isEmpty())
625 xForm->setPropertyValue(FM_PROP_COMMAND, makeAny(sCommand));
626 Any aCommandType;
627 switch (nCommandType)
629 case CommandType::TABLE:
630 aCommandType <<= sal_Int32(CommandType::TABLE);
631 break;
632 case CommandType::QUERY:
633 aCommandType <<= sal_Int32(CommandType::QUERY);
634 break;
635 default:
636 aCommandType <<= sal_Int32(CommandType::COMMAND);
637 xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, css::uno::Any(2 == nCommandType));
638 break;
640 xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType);
644 catch (Exception&)
646 TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
647 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
648 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
649 return;
652 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
653 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
656 void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, weld::Menu& rMenu,
657 weld::Menu& rInsertMenu, weld::Menu& rChangeMenu,
658 weld::Menu& rShowMenu)
660 bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode();
662 Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
663 // Building of the Insert Menu
664 // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND
665 if(nColId > 0)
667 sal_uInt16 nPos2 = GetModelColumnPos(nColId);
669 Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
670 Reference< css::beans::XPropertySet> xColumn( xColumns->getByIndex(nPos2), css::uno::UNO_QUERY);
671 Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
672 if (xSelSupplier.is())
673 xSelSupplier->select(makeAny(xColumn));
676 // insert position, always before the current column
677 sal_uInt16 nPos = GetModelColumnPos(nColId);
678 bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId);
680 if (bDesignMode)
682 int nMenuPos = 0;
683 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_TEXTFIELD, SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
684 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_CHECKBOX, SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
685 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_COMBOBOX, SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
686 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_LISTBOX, SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
687 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_DATEFIELD, SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
688 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_TIMEFIELD, SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
689 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_NUMERICFIELD, SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
690 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_CURRENCYFIELD, SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
691 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_PATTERNFIELD, SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
692 InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_FORMATTEDFIELD, SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
695 if (xCols.is() && nColId)
697 Reference< css::beans::XPropertySet > xPropSet( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
699 Reference< css::io::XPersistObject > xServiceQuestion(xPropSet, UNO_QUERY);
700 sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0;
701 if (nColType == TYPE_TEXTFIELD)
702 { // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD
703 // in both cases. And as columns don't have a css::lang::XServiceInfo interface, we have to distinguish both
704 // types via the existence of special properties
705 if (xPropSet.is())
707 Reference< css::beans::XPropertySetInfo > xPropsInfo = xPropSet->getPropertySetInfo();
708 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
709 nColType = TYPE_FORMATTEDFIELD;
713 if (bDesignMode)
715 int nMenuPos = 0;
716 if (nColType != TYPE_TEXTFIELD)
717 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_TEXTFIELD"1", SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
718 if (nColType != TYPE_CHECKBOX)
719 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_CHECKBOX"1", SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
720 if (nColType != TYPE_COMBOBOX)
721 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_COMBOBOX"1", SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
722 if (nColType != TYPE_LISTBOX)
723 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_LISTBOX"1", SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
724 if (nColType != TYPE_DATEFIELD)
725 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_DATEFIELD"1", SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
726 if (nColType != TYPE_TIMEFIELD)
727 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_TIMEFIELD"1", SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
728 if (nColType != TYPE_NUMERICFIELD)
729 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_NUMERICFIELD"1", SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
730 if (nColType != TYPE_CURRENCYFIELD)
731 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_CURRENCYFIELD"1", SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
732 if (nColType != TYPE_PATTERNFIELD)
733 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_PATTERNFIELD"1", SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
734 if (nColType != TYPE_FORMATTEDFIELD)
735 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_FORMATTEDFIELD"1", SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
739 rMenu.set_visible("change", bDesignMode && bMarked && xCols.is());
740 rMenu.set_sensitive("change", bDesignMode && bMarked && xCols.is());
742 else
744 rMenu.set_visible("change", false);
745 rMenu.set_sensitive("change", false);
748 rMenu.set_visible("insert", bDesignMode && xCols.is());
749 rMenu.set_sensitive("insert", bDesignMode && xCols.is());
750 rMenu.set_visible("delete", bDesignMode && bMarked && xCols.is());
751 rMenu.set_sensitive("delete", bDesignMode && bMarked && xCols.is());
752 rMenu.set_visible("column", bDesignMode && bMarked && xCols.is());
753 rMenu.set_sensitive("column", bDesignMode && bMarked && xCols.is());
755 sal_uInt16 nHiddenCols = 0;
756 if (xCols.is())
758 // check for hidden cols
759 Reference< css::beans::XPropertySet > xCurCol;
760 Any aHidden,aName;
761 for (sal_Int32 i=0; i<xCols->getCount(); ++i)
763 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
764 DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !");
765 aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
766 DBG_ASSERT(aHidden.getValueType().getTypeClass() == TypeClass_BOOLEAN,
767 "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !");
768 if (::comphelper::getBOOL(aHidden))
770 // put the column name into the 'show col' menu
771 if (nHiddenCols < 16)
773 // (only the first 16 items to keep the menu rather small)
774 aName = xCurCol->getPropertyValue(FM_PROP_LABEL);
775 // the ID is arbitrary, but should be unique within the whole menu
776 rMenu.insert(nHiddenCols, OUString::number(nHiddenCols + 1), ::comphelper::getString(aName),
777 nullptr, nullptr, nullptr, TRISTATE_INDET);
779 ++nHiddenCols;
783 rShowMenu.set_visible("more", xCols.is() && (nHiddenCols > 16));
784 rMenu.set_visible("show", xCols.is() && (nHiddenCols > 0));
785 rMenu.set_sensitive("show", xCols.is() && (nHiddenCols > 0));
787 // allow the 'hide column' item ?
788 bool bAllowHide = bMarked; // a column is marked
789 bAllowHide = bAllowHide || (!bDesignMode && (nPos != sal_uInt16(-1))); // OR we are in alive mode and have hit a column
790 bAllowHide = bAllowHide && xCols.is(); // AND we have a column container
791 bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1); // AND there are at least two visible columns
792 rMenu.set_visible("hide", bAllowHide);
793 rMenu.set_sensitive("hide", bAllowHide);
795 if (!bMarked)
796 return;
798 SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
799 // ask the bindings of the current view frame (which should be the one we're residing in) for the state
800 if (pCurrentFrame)
802 std::unique_ptr<SfxPoolItem> pItem;
803 SfxItemState eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem);
805 if (eState >= SfxItemState::DEFAULT && pItem != nullptr)
807 bool bChecked = dynamic_cast<const SfxBoolItem*>( pItem.get()) != nullptr && static_cast<SfxBoolItem*>(pItem.get())->GetValue();
808 rMenu.set_active("column", bChecked);
813 namespace {
815 enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone };
819 void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const weld::Menu& rMenu, const OString& rExecutionResult)
821 Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
822 sal_uInt16 nPos = GetModelColumnPos(nColId);
824 OUString aFieldType;
825 bool bReplace = false;
826 InspectorAction eInspectorAction = eNone;
828 if (rExecutionResult == "delete")
830 Reference< XInterface > xCol(
831 xCols->getByIndex(nPos), css::uno::UNO_QUERY);
832 xCols->removeByIndex(nPos);
833 ::comphelper::disposeComponent(xCol);
835 else if (rExecutionResult == "hide")
837 Reference< css::beans::XPropertySet > xCurCol( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
838 xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(true));
840 else if (rExecutionResult == "column")
842 eInspectorAction = rMenu.get_active("column") ? eOpenInspector : eCloseInspector;
844 else if (rExecutionResult.startsWith(FM_COL_TEXTFIELD))
846 if (rExecutionResult != FM_COL_TEXTFIELD)
847 bReplace = true;
848 aFieldType = FM_COL_TEXTFIELD;
850 else if (rExecutionResult.startsWith(FM_COL_COMBOBOX))
852 if (rExecutionResult != FM_COL_COMBOBOX)
853 bReplace = true;
854 aFieldType = FM_COL_COMBOBOX;
856 else if (rExecutionResult.startsWith(FM_COL_LISTBOX))
858 if (rExecutionResult != FM_COL_LISTBOX)
859 bReplace = true;
860 aFieldType = FM_COL_LISTBOX;
862 else if (rExecutionResult.startsWith(FM_COL_CHECKBOX))
864 if (rExecutionResult != FM_COL_CHECKBOX)
865 bReplace = true;
866 aFieldType = FM_COL_CHECKBOX;
868 else if (rExecutionResult.startsWith(FM_COL_DATEFIELD))
870 if (rExecutionResult != FM_COL_DATEFIELD)
871 bReplace = true;
872 aFieldType = FM_COL_DATEFIELD;
874 else if (rExecutionResult.startsWith(FM_COL_TIMEFIELD))
876 if (rExecutionResult != FM_COL_TIMEFIELD)
877 bReplace = true;
878 aFieldType = FM_COL_TIMEFIELD;
880 else if (rExecutionResult.startsWith(FM_COL_NUMERICFIELD))
882 if (rExecutionResult != FM_COL_NUMERICFIELD)
883 bReplace = true;
884 aFieldType = FM_COL_NUMERICFIELD;
886 else if (rExecutionResult.startsWith(FM_COL_CURRENCYFIELD))
888 if (rExecutionResult != FM_COL_CURRENCYFIELD)
889 bReplace = true;
890 aFieldType = FM_COL_CURRENCYFIELD;
892 else if (rExecutionResult.startsWith(FM_COL_PATTERNFIELD))
894 if (rExecutionResult != FM_COL_PATTERNFIELD)
895 bReplace = true;
896 aFieldType = FM_COL_PATTERNFIELD;
898 else if (rExecutionResult.startsWith(FM_COL_FORMATTEDFIELD))
900 if (rExecutionResult != FM_COL_FORMATTEDFIELD)
901 bReplace = true;
902 aFieldType = FM_COL_FORMATTEDFIELD;
904 else if (rExecutionResult == "more")
906 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
907 ScopedVclPtr<AbstractFmShowColsDialog> pDlg(pFact->CreateFmShowColsDialog(GetFrameWeld()));
908 pDlg->SetColumns(xCols);
909 pDlg->Execute();
911 else if (rExecutionResult == "all")
913 // just iterate through all the cols ...
914 Reference< css::beans::XPropertySet > xCurCol;
915 for (sal_Int32 i=0; i<xCols->getCount(); ++i)
917 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
918 xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
920 // TODO : there must be a more clever way to do this...
921 // with the above the view is updated after every single model update ...
923 else if (!rExecutionResult.isEmpty())
925 sal_Int32 nExecutionResult = rExecutionResult.toInt32();
926 if (nExecutionResult>0 && nExecutionResult<=16)
928 // it was a "show column/<colname>" command (there are at most 16 such items)
929 // search the nExecutionResult'th hidden col
930 Reference< css::beans::XPropertySet > xCurCol;
931 for (sal_Int32 i=0; i<xCols->getCount() && nExecutionResult; ++i)
933 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
934 Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
935 if (::comphelper::getBOOL(aHidden))
936 if (!--nExecutionResult)
938 xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
939 break;
945 if ( !aFieldType.isEmpty() )
949 Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW );
950 Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW );
952 if ( bReplace )
954 // rescue over a few properties
955 Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY );
957 TransferFormComponentProperties(
958 xReplaced, xNewCol, Application::GetSettings().GetUILanguageTag().getLocale() );
960 xCols->replaceByIndex( nPos, makeAny( xNewCol ) );
961 ::comphelper::disposeComponent( xReplaced );
963 eInspectorAction = eUpdateInspector;
965 else
967 FormControlFactory factory;
969 OUString sLabel = FormControlFactory::getDefaultUniqueName_ByComponentType(
970 Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol );
971 xNewCol->setPropertyValue( FM_PROP_LABEL, makeAny( sLabel ) );
972 xNewCol->setPropertyValue( FM_PROP_NAME, makeAny( sLabel ) );
974 factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol );
976 xCols->insertByIndex( nPos, makeAny( xNewCol ) );
979 catch( const Exception& )
981 DBG_UNHANDLED_EXCEPTION("svx");
985 SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
986 OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" );
987 if ( !pCurrentFrame )
988 return;
990 if ( eInspectorAction == eUpdateInspector )
992 if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) )
993 eInspectorAction = eNone;
996 if ( eInspectorAction != eNone )
998 SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction != eCloseInspector );
1000 pCurrentFrame->GetBindings().GetDispatcher()->ExecuteList(
1001 SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON,
1002 { &aShowItem });
1006 void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos )
1008 // the affected col
1009 sal_uInt16 nColId = GetItemId( _rPreferredPos );
1011 // the menu
1012 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/colsmenu.ui"));
1013 std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
1014 std::unique_ptr<weld::Menu> xInsertMenu(xBuilder->weld_menu("insertmenu"));
1015 std::unique_ptr<weld::Menu> xChangeMenu(xBuilder->weld_menu("changemenu"));
1016 std::unique_ptr<weld::Menu> xShowMenu(xBuilder->weld_menu("showmenu"));
1018 // let derivatives modify the menu
1019 PreExecuteColumnContextMenu(nColId, *xContextMenu, *xInsertMenu, *xChangeMenu, *xShowMenu);
1021 bool bEmpty = true;
1022 for (int i = 0, nCount = xContextMenu->n_children(); i < nCount; ++i)
1024 bEmpty = !xContextMenu->get_sensitive(xContextMenu->get_id(i));
1025 if (!bEmpty)
1026 break;
1028 if (bEmpty)
1029 return;
1031 // execute the menu
1032 ::tools::Rectangle aRect(_rPreferredPos, Size(1,1));
1033 weld::Window* pParent = weld::GetPopupParent(*this, aRect);
1034 OString sResult = xContextMenu->popup_at_rect(pParent, aRect);
1036 // let derivatives handle the result
1037 PostExecuteColumnContextMenu(nColId, *xContextMenu, sResult);
1040 void FmGridHeader::Command(const CommandEvent& rEvt)
1042 switch (rEvt.GetCommand())
1044 case CommandEventId::ContextMenu:
1046 if (!rEvt.IsMouseEvent())
1047 return;
1049 triggerColumnContextMenu( rEvt.GetMousePosPixel() );
1051 break;
1052 default:
1053 EditBrowserHeader::Command(rEvt);
1057 FmGridControl::FmGridControl(
1058 const Reference< css::uno::XComponentContext >& _rxContext,
1059 vcl::Window* pParent,
1060 FmXGridPeer* _pPeer,
1061 WinBits nBits)
1062 :DbGridControl(_rxContext, pParent, nBits)
1063 ,m_pPeer(_pPeer)
1064 ,m_nCurrentSelectedColumn(-1)
1065 ,m_nMarkedColumnId(BROWSER_INVALIDID)
1066 ,m_bSelecting(false)
1067 ,m_bInColumnMove(false)
1069 EnableInteractiveRowHeight( );
1072 void FmGridControl::Command(const CommandEvent& _rEvt)
1074 if ( CommandEventId::ContextMenu == _rEvt.GetCommand() )
1076 FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() );
1077 if ( pMyHeader && !_rEvt.IsMouseEvent() )
1078 { // context menu requested by keyboard
1079 if ( 1 == GetSelectColumnCount() || IsDesignMode() )
1081 sal_uInt16 nSelId = GetColumnId(
1082 sal::static_int_cast< sal_uInt16 >( FirstSelectedColumn() ) );
1083 ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
1085 Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) );
1086 pMyHeader->triggerColumnContextMenu(aRelativePos);
1088 // handled
1089 return;
1094 DbGridControl::Command( _rEvt );
1097 // css::beans::XPropertyChangeListener
1098 void FmGridControl::propertyChange(const css::beans::PropertyChangeEvent& evt)
1100 if (evt.PropertyName == FM_PROP_ROWCOUNT)
1102 // if we're not in the main thread call AdjustRows asynchronously
1103 implAdjustInSolarThread(true);
1104 return;
1107 const DbGridRowRef& xRow = GetCurrentRow();
1108 // no adjustment of the properties is carried out during positioning
1109 Reference<XPropertySet> xSet(evt.Source,UNO_QUERY);
1110 if (!(xRow.is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark()))))
1111 return;
1113 if (evt.PropertyName == FM_PROP_ISMODIFIED)
1115 // modified or clean ?
1116 GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean;
1117 if (eStatus != xRow->GetStatus())
1119 xRow->SetStatus(eStatus);
1120 SolarMutexGuard aGuard;
1121 RowModified(GetCurrentPos());
1126 void FmGridControl::SetDesignMode(bool bMode)
1128 bool bOldMode = IsDesignMode();
1129 DbGridControl::SetDesignMode(bMode);
1130 if (bOldMode == bMode)
1131 return;
1133 if (!bMode)
1135 // cancel selection
1136 markColumn(USHRT_MAX);
1138 else
1140 Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
1141 Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
1142 if (xSelSupplier.is())
1144 Any aSelection = xSelSupplier->getSelection();
1145 Reference< css::beans::XPropertySet > xColumn;
1146 if (aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE)
1147 xColumn.set(aSelection, css::uno::UNO_QUERY);
1148 Reference< XInterface > xCurrent;
1149 for (sal_Int32 i=0; i<xColumns->getCount(); ++i)
1151 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1152 if (xCurrent == xColumn)
1154 markColumn(GetColumnIdFromModelPos(i));
1155 break;
1162 void FmGridControl::DeleteSelectedRows()
1164 if (!m_pSeekCursor)
1165 return;
1167 // how many rows are selected?
1168 sal_Int32 nSelectedRows = GetSelectRowCount();
1170 // the current line should be deleted but it is currently in edit mode
1171 if ( IsCurrentAppending() )
1172 return;
1173 // is the insert row selected
1174 if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
1175 nSelectedRows -= 1;
1177 // nothing to do
1178 if (nSelectedRows <= 0)
1179 return;
1181 // try to confirm the delete
1182 Reference< css::frame::XDispatchProvider > xDispatcher = static_cast<css::frame::XDispatchProvider*>(GetPeer());
1183 if (xDispatcher.is())
1185 css::util::URL aUrl;
1186 aUrl.Complete = FMURL_CONFIRM_DELETION;
1187 Reference< css::util::XURLTransformer > xTransformer(
1188 css::util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
1189 xTransformer->parseStrict( aUrl );
1191 Reference< css::frame::XDispatch > xDispatch = xDispatcher->queryDispatch(aUrl, OUString(), 0);
1192 Reference< css::form::XConfirmDeleteListener > xConfirm(xDispatch, UNO_QUERY);
1193 if (xConfirm.is())
1195 css::sdb::RowChangeEvent aEvent;
1196 aEvent.Source = Reference< XInterface >(*getDataSource());
1197 aEvent.Rows = nSelectedRows;
1198 aEvent.Action = css::sdb::RowChangeAction::DELETE;
1199 if (!xConfirm->confirmDelete(aEvent))
1200 return;
1204 const MultiSelection* pRowSelection = GetSelection();
1205 if ( pRowSelection && pRowSelection->IsAllSelected() )
1207 BeginCursorAction();
1208 CursorWrapper* pCursor = getDataSource();
1209 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*pCursor), UNO_QUERY);
1212 pCursor->beforeFirst();
1213 while( pCursor->next() )
1214 xUpdateCursor->deleteRow();
1216 SetUpdateMode(false);
1217 SetNoSelection();
1219 xUpdateCursor->moveToInsertRow();
1221 catch(const Exception&)
1223 TOOLS_WARN_EXCEPTION("svx", "Exception caught while deleting rows!");
1225 // adapt to the data cursor
1226 AdjustDataSource(true);
1227 EndCursorAction();
1228 SetUpdateMode(true);
1230 else
1232 Reference< css::sdbcx::XDeleteRows > xDeleteThem(Reference< XInterface >(*getDataSource()), UNO_QUERY);
1234 // collect the bookmarks of the selected rows
1235 Sequence < Any> aBookmarks = getSelectionBookmarks();
1237 // determine the next row to position after deletion
1238 Any aBookmark;
1239 bool bNewPos = false;
1240 // if the current row isn't selected we take the row as row after deletion
1241 OSL_ENSURE( GetCurrentRow().is(), "FmGridControl::DeleteSelectedRows: no current row here?" );
1242 // crash reports suggest it can happen we don't have a current row - how?
1243 // #154303# / 2008-04-23 / frank.schoenheit@sun.com
1244 if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().is() )
1246 aBookmark = GetCurrentRow()->GetBookmark();
1247 bNewPos = true;
1249 else
1251 // we look for the first row after the selected block for selection
1252 tools::Long nIdx = LastSelectedRow() + 1;
1253 if (nIdx < GetRowCount() - 1)
1255 // there is a next row to position on
1256 if (SeekCursor(nIdx))
1258 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1260 bNewPos = true;
1261 // if it's not the row for inserting we keep the bookmark
1262 if (!IsInsertionRow(nIdx))
1263 aBookmark = m_pSeekCursor->getBookmark();
1266 else
1268 // we look for the first row before the selected block for selection after deletion
1269 nIdx = FirstSelectedRow() - 1;
1270 if (nIdx >= 0 && SeekCursor(nIdx))
1272 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1274 bNewPos = true;
1275 aBookmark = m_pSeekCursor->getBookmark();
1280 // Are all rows selected?
1281 // Second condition if no insertion line exists
1282 bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows;
1284 BeginCursorAction();
1286 // now delete the row
1287 Sequence<sal_Int32> aDeletedRows;
1288 SetUpdateMode( false );
1291 aDeletedRows = xDeleteThem->deleteRows(aBookmarks);
1293 catch(SQLException&)
1296 SetUpdateMode( true );
1298 // how many rows are deleted?
1299 sal_Int32 nDeletedRows = static_cast<sal_Int32>(std::count_if(aDeletedRows.begin(), aDeletedRows.end(),
1300 [](const sal_Int32 nRow) { return nRow != 0; }));
1302 // have rows been deleted?
1303 if (nDeletedRows)
1305 SetUpdateMode(false);
1306 SetNoSelection();
1309 // did we delete all the rows than try to move to the next possible row
1310 if (nDeletedRows == aDeletedRows.getLength())
1312 // there exists a new position to move on
1313 if (bNewPos)
1315 if (aBookmark.hasValue())
1316 getDataSource()->moveToBookmark(aBookmark);
1317 // no valid bookmark so move to the insert row
1318 else
1320 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1321 xUpdateCursor->moveToInsertRow();
1324 else
1326 Reference< css::beans::XPropertySet > xSet(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1328 sal_Int32 nRecordCount(0);
1329 xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1330 if ( m_pDataCursor->rowDeleted() )
1331 --nRecordCount;
1333 // there are no rows left and we have an insert row
1334 if (!nRecordCount && GetEmptyRow().is())
1336 Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1337 xUpdateCursor->moveToInsertRow();
1339 else if (nRecordCount)
1340 // move to the first row
1341 getDataSource()->first();
1344 // not all the rows where deleted, so move to the first row which remained in the resultset
1345 else
1347 auto pRow = std::find(aDeletedRows.begin(), aDeletedRows.end(), 0);
1348 if (pRow != aDeletedRows.end())
1350 auto i = static_cast<sal_Int32>(std::distance(aDeletedRows.begin(), pRow));
1351 getDataSource()->moveToBookmark(aBookmarks[i]);
1355 catch(const Exception&)
1359 // positioning went wrong so try to move to the first row
1360 getDataSource()->first();
1362 catch(const Exception&)
1367 // adapt to the data cursor
1368 AdjustDataSource(true);
1370 // not all rows could be deleted;
1371 // never select again there the ones that could not be deleted
1372 if (nDeletedRows < nSelectedRows)
1374 // were all selected
1375 if (bAllSelected)
1377 SelectAll();
1378 if (IsInsertionRow(GetRowCount() - 1)) // not the insertion row
1379 SelectRow(GetRowCount() - 1, false);
1381 else
1383 // select the remaining rows
1384 for (const sal_Int32 nSuccess : aDeletedRows)
1388 if (!nSuccess)
1390 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
1391 SetSeekPos(m_pSeekCursor->getRow() - 1);
1392 SelectRow(GetSeekPos());
1395 catch(const Exception&)
1397 // keep the seekpos in all cases
1398 SetSeekPos(m_pSeekCursor->getRow() - 1);
1404 EndCursorAction();
1405 SetUpdateMode(true);
1407 else // row could not be deleted
1409 EndCursorAction();
1412 // currentrow is the insert row?
1413 if (!IsCurrentAppending())
1414 getDataSource()->refreshRow();
1416 catch(const Exception&)
1422 // if there is no selection anymore we can start editing
1423 if (!GetSelectRowCount())
1424 ActivateCell();
1427 // XCurrentRecordListener
1428 void FmGridControl::positioned()
1430 SAL_INFO("svx.fmcomp", "FmGridControl::positioned");
1431 // position on the data source (force it to be done in the main thread)
1432 implAdjustInSolarThread(false);
1435 bool FmGridControl::commit()
1437 // execute commit only if an update is not already executed by the
1438 // css::form::component::GridControl
1439 if (!IsUpdating())
1441 if (Controller().is() && Controller()->IsValueChangedFromSaved())
1443 if (!SaveModified())
1444 return false;
1447 return true;
1450 void FmGridControl::inserted()
1452 const DbGridRowRef& xRow = GetCurrentRow();
1453 if (!xRow.is())
1454 return;
1456 // line has been inserted, then reset the status and mode
1457 xRow->SetState(m_pDataCursor.get(), false);
1458 xRow->SetNew(false);
1462 VclPtr<BrowserHeader> FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
1464 DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" );
1465 return VclPtr<FmGridHeader>::Create( pParent );
1468 void FmGridControl::markColumn(sal_uInt16 nId)
1470 if (!(GetHeaderBar() && m_nMarkedColumnId != nId))
1471 return;
1473 // deselect
1474 if (m_nMarkedColumnId != BROWSER_INVALIDID)
1476 HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HeaderBarItemBits::FLAT;
1477 GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits);
1481 if (nId != BROWSER_INVALIDID)
1483 HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HeaderBarItemBits::FLAT;
1484 GetHeaderBar()->SetItemBits(nId, aBits);
1486 m_nMarkedColumnId = nId;
1489 bool FmGridControl::isColumnMarked(sal_uInt16 nId) const
1491 return m_nMarkedColumnId == nId;
1494 tools::Long FmGridControl::QueryMinimumRowHeight()
1496 tools::Long const nMinimalLogicHeight = 20; // 0.2 cm
1497 tools::Long nMinimalPixelHeight = LogicToPixel(Point(0, nMinimalLogicHeight), MapMode(MapUnit::Map10thMM)).Y();
1498 return CalcZoom( nMinimalPixelHeight );
1501 void FmGridControl::RowHeightChanged()
1503 DbGridControl::RowHeightChanged();
1505 Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY );
1506 DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" );
1507 if ( !xModel.is() )
1508 return;
1512 sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() );
1513 Any aProperty = makeAny( static_cast<sal_Int32>(PixelToLogic( Point(0, nUnzoomedPixelHeight), MapMode(MapUnit::Map10thMM)).Y()) );
1514 xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty );
1516 catch( const Exception& )
1518 TOOLS_WARN_EXCEPTION( "svx", "FmGridControl::RowHeightChanged" );
1522 void FmGridControl::ColumnResized(sal_uInt16 nId)
1524 DbGridControl::ColumnResized(nId);
1526 // transfer value to the model
1527 DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1528 const Reference< css::beans::XPropertySet >& xColModel(pCol->getModel());
1529 if (xColModel.is())
1531 Any aWidth;
1532 sal_Int32 nColumnWidth = GetColumnWidth(nId);
1533 nColumnWidth = CalcReverseZoom(nColumnWidth);
1534 // convert to 10THMM
1535 aWidth <<= static_cast<sal_Int32>(PixelToLogic(Point(nColumnWidth, 0), MapMode(MapUnit::Map10thMM)).X());
1536 xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth);
1540 void FmGridControl::CellModified()
1542 DbGridControl::CellModified();
1543 GetPeer()->CellModified();
1546 void FmGridControl::BeginCursorAction()
1548 DbGridControl::BeginCursorAction();
1549 m_pPeer->stopCursorListening();
1552 void FmGridControl::EndCursorAction()
1554 m_pPeer->startCursorListening();
1555 DbGridControl::EndCursorAction();
1558 void FmGridControl::ColumnMoved(sal_uInt16 nId)
1560 m_bInColumnMove = true;
1562 DbGridControl::ColumnMoved(nId);
1563 Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
1565 if (xColumns.is())
1567 // locate the column and move in the model;
1568 // get ColumnPos
1569 DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1570 Reference< css::beans::XPropertySet > xCol;
1572 // inserting must be based on the column positions
1573 sal_Int32 i;
1574 Reference< XInterface > xCurrent;
1575 for (i = 0; !xCol.is() && i < xColumns->getCount(); i++)
1577 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1578 if (xCurrent == pCol->getModel())
1580 xCol = pCol->getModel();
1581 break;
1585 DBG_ASSERT(i < xColumns->getCount(), "Wrong css::sdbcx::Index");
1586 xColumns->removeByIndex(i);
1587 Any aElement;
1588 aElement <<= xCol;
1589 xColumns->insertByIndex(GetModelColumnPos(nId), aElement);
1590 pCol->setModel(xCol);
1591 // if the column which is shown here is selected ...
1592 if ( isColumnSelected(pCol) )
1593 markColumn(nId); // ... -> mark it
1596 m_bInColumnMove = false;
1599 void FmGridControl::InitColumnsByModels(const Reference< css::container::XIndexContainer >& xColumns)
1601 // reset columns;
1602 // if there is only one HandleColumn, then don't
1603 if (GetModelColCount())
1605 RemoveColumns();
1606 InsertHandleColumn();
1609 if (!xColumns.is())
1610 return;
1612 SetUpdateMode(false);
1614 // inserting must be based on the column positions
1615 sal_Int32 i;
1616 Any aWidth;
1617 for (i = 0; i < xColumns->getCount(); ++i)
1619 Reference< css::beans::XPropertySet > xCol(
1620 xColumns->getByIndex(i), css::uno::UNO_QUERY);
1622 OUString aName(
1623 comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)));
1625 aWidth = xCol->getPropertyValue(FM_PROP_WIDTH);
1626 sal_Int32 nWidth = 0;
1627 if (aWidth >>= nWidth)
1628 nWidth = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
1630 AppendColumn(aName, static_cast<sal_uInt16>(nWidth));
1631 DbGridColumn* pCol = DbGridControl::GetColumns()[ i ].get();
1632 pCol->setModel(xCol);
1635 // and now remove the hidden columns as well
1636 // (we did not already make it in the upper loop, since we would then have gotten
1637 // problems with the IDs of the columns: AppendColumn allocates them automatically,
1638 // but the column _after_ a hidden one needs an ID increased by one ...)
1639 Any aHidden;
1640 for (i = 0; i < xColumns->getCount(); ++i)
1642 Reference< css::beans::XPropertySet > xCol( xColumns->getByIndex(i), css::uno::UNO_QUERY);
1643 aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN);
1644 if (::comphelper::getBOOL(aHidden))
1645 HideColumn(GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
1648 SetUpdateMode(true);
1651 void FmGridControl::InitColumnByField(
1652 DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel,
1653 const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex )
1655 DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" );
1657 // lookup the column which belongs to the control source
1658 OUString sFieldName;
1659 _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName;
1660 Reference< XPropertySet > xField;
1661 _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
1664 if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length
1665 _rxFieldsByNames->getByName( sFieldName ) >>= xField;
1667 // determine the position of this column
1668 sal_Int32 nFieldPos = -1;
1669 if ( xField.is() )
1671 Reference< XPropertySet > xCheck;
1672 sal_Int32 nFieldCount = _rxFieldsByIndex->getCount();
1673 for ( sal_Int32 i = 0; i < nFieldCount; ++i)
1675 _rxFieldsByIndex->getByIndex( i ) >>= xCheck;
1676 if ( xField.get() == xCheck.get() )
1678 nFieldPos = i;
1679 break;
1684 if ( xField.is() && ( nFieldPos >= 0 ) )
1686 // some data types are not allowed
1687 sal_Int32 nDataType = DataType::OTHER;
1688 xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType;
1690 bool bIllegalType = false;
1691 switch ( nDataType )
1693 case DataType::BLOB:
1694 case DataType::LONGVARBINARY:
1695 case DataType::BINARY:
1696 case DataType::VARBINARY:
1697 case DataType::OTHER:
1698 bIllegalType = true;
1699 break;
1702 if ( bIllegalType )
1704 _pColumn->SetObject( static_cast<sal_Int16>(nFieldPos) );
1705 return;
1709 // the control type is determined by the ColumnServiceName
1710 static constexpr OUStringLiteral s_sPropColumnServiceName = u"ColumnServiceName";
1711 if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) )
1712 return;
1714 _pColumn->setModel( _rxColumnModel );
1716 OUString sColumnServiceName;
1717 _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName;
1719 sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName );
1720 _pColumn->CreateControl( nFieldPos, xField, nTypeId );
1723 void FmGridControl::InitColumnsByFields(const Reference< css::container::XIndexAccess >& _rxFields)
1725 if ( !_rxFields.is() )
1726 return;
1728 // initialize columns
1729 Reference< XIndexContainer > xColumns( GetPeer()->getColumns() );
1730 Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY );
1732 // inserting must be based on the column positions
1733 for (sal_Int32 i = 0; i < xColumns->getCount(); i++)
1735 DbGridColumn* pCol = GetColumns()[ i ].get();
1736 OSL_ENSURE(pCol,"No grid column!");
1737 if ( pCol )
1739 Reference< XPropertySet > xColumnModel(
1740 xColumns->getByIndex( i ), css::uno::UNO_QUERY);
1742 InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields );
1747 void FmGridControl::HideColumn(sal_uInt16 nId)
1749 DbGridControl::HideColumn(nId);
1751 sal_uInt16 nPos = GetModelColumnPos(nId);
1752 if (nPos == sal_uInt16(-1))
1753 return;
1755 DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1756 if (pColumn->IsHidden())
1757 GetPeer()->columnHidden(pColumn);
1759 if (nId == m_nMarkedColumnId)
1760 m_nMarkedColumnId = sal_uInt16(-1);
1763 bool FmGridControl::isColumnSelected(DbGridColumn const * _pColumn)
1765 OSL_ENSURE(_pColumn,"Column can not be null!");
1766 bool bSelected = false;
1767 // if the column which is shown here is selected ...
1768 Reference< css::view::XSelectionSupplier > xSelSupplier(GetPeer()->getColumns(), UNO_QUERY);
1769 if ( xSelSupplier.is() )
1771 Reference< css::beans::XPropertySet > xColumn;
1772 xSelSupplier->getSelection() >>= xColumn;
1773 bSelected = (xColumn.get() == _pColumn->getModel().get());
1775 return bSelected;
1778 void FmGridControl::ShowColumn(sal_uInt16 nId)
1780 DbGridControl::ShowColumn(nId);
1782 sal_uInt16 nPos = GetModelColumnPos(nId);
1783 if (nPos == sal_uInt16(-1))
1784 return;
1786 DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1787 if (!pColumn->IsHidden())
1788 GetPeer()->columnVisible(pColumn);
1790 // if the column which is shown here is selected ...
1791 if ( isColumnSelected(pColumn) )
1792 markColumn(nId); // ... -> mark it
1795 bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks)
1797 SolarMutexGuard aGuard;
1798 // need to lock the SolarMutex so that no paint call disturbs us ...
1800 if ( !m_pSeekCursor )
1802 OSL_FAIL( "FmGridControl::selectBookmarks: no seek cursor!" );
1803 return false;
1806 SetNoSelection();
1808 bool bAllSuccessfull = true;
1811 for (const Any& rBookmark : _rBookmarks)
1813 // move the seek cursor to the row given
1814 if (m_pSeekCursor->moveToBookmark(rBookmark))
1815 SelectRow( m_pSeekCursor->getRow() - 1);
1816 else
1817 bAllSuccessfull = false;
1820 catch(Exception&)
1822 OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
1823 return false;
1826 return bAllSuccessfull;
1829 Sequence< Any> FmGridControl::getSelectionBookmarks()
1831 // lock our update so no paint-triggered seeks interfere ...
1832 SetUpdateMode(false);
1834 sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0;
1835 Sequence< Any> aBookmarks(nSelectedRows);
1836 if ( nSelectedRows )
1838 Any* pBookmarks = aBookmarks.getArray();
1840 // (I'm not sure if the problem isn't deeper: The scenario: a large table displayed by a grid with a
1841 // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress
1842 // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which made a
1843 // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition.
1844 // Unfortunately the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the
1845 // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning
1846 // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails.
1847 // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relies_ on
1848 // the first one, should be secured against recursion, with a broad-minded interpretation of "recursion": if any of these
1849 // code parts is executed, no other should be accessible. But this sounds very difficult to achieve...
1850 // )
1852 // The next problem caused by the same behavior (SeekCursor causes a propertyChanged): when adjusting rows we implicitly
1853 // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results.
1854 // That's why we _first_ collect the indices of the selected rows and _then_ their bookmarks.
1855 tools::Long nIdx = FirstSelectedRow();
1856 while (nIdx != BROWSER_ENDOFSELECTION)
1858 // (we misuse the bookmarks array for this ...)
1859 pBookmarks[i++] <<= static_cast<sal_Int32>(nIdx);
1860 nIdx = NextSelectedRow();
1862 DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indices !");
1864 for (i=0; i<nSelectedRows; ++i)
1866 nIdx = ::comphelper::getINT32(pBookmarks[i]);
1867 if (IsInsertionRow(nIdx))
1869 // do not delete empty row
1870 aBookmarks.realloc(--nSelectedRows);
1871 SelectRow(nIdx, false); // cancel selection for empty row
1872 break;
1875 // first, position the data cursor on the selected block
1876 if (SeekCursor(nIdx))
1878 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1880 pBookmarks[i] = m_pSeekCursor->getBookmark();
1882 #ifdef DBG_UTIL
1883 else
1884 OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
1885 #endif
1888 SetUpdateMode(true);
1890 // if one of the SeekCursor-calls failed...
1891 aBookmarks.realloc(i);
1893 // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems.
1894 // but this would be incompatible as we need a locking flag, then...)
1896 return aBookmarks;
1899 namespace
1901 OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const OUString& _sPropName)
1903 OUString sRetText;
1904 if ( _pPeer && _nPosition != -1)
1906 Reference<XIndexContainer> xIndex = _pPeer->getColumns();
1907 if ( xIndex.is() && xIndex->getCount() > _nPosition )
1909 Reference<XPropertySet> xProp;
1910 xIndex->getByIndex( _nPosition ) >>= xProp;
1911 if ( xProp.is() )
1913 try {
1914 xProp->getPropertyValue( _sPropName ) >>= sRetText;
1915 } catch (UnknownPropertyException const&) {
1916 TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
1921 return sRetText;
1925 // Object data and state
1926 OUString FmGridControl::GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1928 OUString sRetText;
1929 switch( _eObjType )
1931 case ::vcl::BBTYPE_BROWSEBOX:
1932 if ( GetPeer() )
1934 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1935 if ( xProp.is() )
1936 xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText;
1938 break;
1939 case ::vcl::BBTYPE_COLUMNHEADERCELL:
1940 sRetText = getColumnPropertyFromPeer(
1941 GetPeer(),
1942 GetModelColumnPos(
1943 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1944 FM_PROP_LABEL);
1945 break;
1946 default:
1947 sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition);
1949 return sRetText;
1952 OUString FmGridControl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1954 OUString sRetText;
1955 switch( _eObjType )
1957 case ::vcl::BBTYPE_BROWSEBOX:
1958 if ( GetPeer() )
1960 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1961 if ( xProp.is() )
1963 xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText;
1964 if ( sRetText.isEmpty() )
1965 xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText;
1968 break;
1969 case ::vcl::BBTYPE_COLUMNHEADERCELL:
1970 sRetText = getColumnPropertyFromPeer(
1971 GetPeer(),
1972 GetModelColumnPos(
1973 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1974 FM_PROP_HELPTEXT);
1975 if ( sRetText.isEmpty() )
1976 sRetText = getColumnPropertyFromPeer(
1977 GetPeer(),
1978 GetModelColumnPos(
1979 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1980 FM_PROP_DESCRIPTION);
1982 break;
1983 default:
1984 sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition);
1986 return sRetText;
1989 void FmGridControl::Select()
1991 DbGridControl::Select();
1992 // ... does it affect our columns?
1993 const MultiSelection* pColumnSelection = GetColumnSelection();
1995 sal_uInt16 nSelectedColumn =
1996 pColumnSelection && pColumnSelection->GetSelectCount()
1997 ? sal::static_int_cast< sal_uInt16 >(
1998 const_cast<MultiSelection*>(pColumnSelection)->FirstSelected())
1999 : SAL_MAX_UINT16;
2000 // the HandleColumn is not selected
2001 switch (nSelectedColumn)
2003 case SAL_MAX_UINT16: break; // no selection
2004 case 0 : nSelectedColumn = SAL_MAX_UINT16; break;
2005 // handle col can't be selected
2006 default :
2007 // get the model col pos instead of the view col pos
2008 nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1));
2009 break;
2012 if (nSelectedColumn == m_nCurrentSelectedColumn)
2013 return;
2015 // BEFORE calling the select at the SelectionSupplier!
2016 m_nCurrentSelectedColumn = nSelectedColumn;
2018 if (m_bSelecting)
2019 return;
2021 m_bSelecting = true;
2025 Reference< XIndexAccess > xColumns = GetPeer()->getColumns();
2026 Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
2027 if (xSelSupplier.is())
2029 if (nSelectedColumn != SAL_MAX_UINT16)
2031 Reference< XPropertySet > xColumn(
2032 xColumns->getByIndex(nSelectedColumn),
2033 css::uno::UNO_QUERY);
2034 xSelSupplier->select(makeAny(xColumn));
2036 else
2038 xSelSupplier->select(Any());
2042 catch(Exception&)
2047 m_bSelecting = false;
2051 void FmGridControl::KeyInput( const KeyEvent& rKEvt )
2053 bool bDone = false;
2054 const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
2055 if ( IsDesignMode()
2056 && !rKeyCode.IsShift()
2057 && !rKeyCode.IsMod1()
2058 && !rKeyCode.IsMod2()
2059 && GetParent() )
2061 switch ( rKeyCode.GetCode() )
2063 case KEY_ESCAPE:
2064 GetParent()->GrabFocus();
2065 bDone = true;
2066 break;
2067 case KEY_DELETE:
2068 if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 )
2070 Reference< css::container::XIndexContainer > xCols(GetPeer()->getColumns());
2071 if ( xCols.is() )
2075 if ( m_nCurrentSelectedColumn < xCols->getCount() )
2077 Reference< XInterface > xCol;
2078 xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol;
2079 xCols->removeByIndex(m_nCurrentSelectedColumn);
2080 ::comphelper::disposeComponent(xCol);
2083 catch(const Exception&)
2085 TOOLS_WARN_EXCEPTION("svx", "exception occurred while deleting a column");
2089 bDone = true;
2090 break;
2093 if ( !bDone )
2094 DbGridControl::KeyInput( rKEvt );
2097 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */