1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
22 #include <svx/fmtools.hxx>
23 #include <fmservs.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>
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
;
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()
115 void FmGridHeader::dispose()
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() ) );
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
);
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
);
178 Help::ShowQuickHelp( this, aItemRect
, aHelpText
);
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
);
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()
246 OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
247 return DND_ACTION_NONE
;
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...
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
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 !)");
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();
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();
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
;
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);
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
;
373 xNumberFormats
= xSupplier
->getNumberFormats();
374 if (!xNumberFormats
.is())
376 ::comphelper::disposeComponent(m_pImpl
->xDroppedResultSet
);
377 ::comphelper::disposeComponent(m_pImpl
->xDroppedStatement
);
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
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
);
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
;
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
);
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
);
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
);
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
);
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
);
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
);
473 case DataType::VARCHAR
:
474 case DataType::LONGVARCHAR
:
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
);
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
));
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
;
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
);
541 xSecondCol
= std::move(xThisRoundCol
);
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
);
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
) ) );
564 xCol
->setPropertyValue(FM_PROP_LABEL
, Any(sFieldName
));
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
));
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
;
594 xSecondCol
->setPropertyValue(FM_PROP_NAME
, Any(sRealName
));
596 xCol
->setPropertyValue(FM_PROP_NAME
, Any(sRealName
));
600 xCol
->setPropertyValue(FM_PROP_NAME
, Any(sFieldName
));
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
);
613 if (::comphelper::getString(xForm
->getPropertyValue(FM_PROP_DATASOURCE
)).isEmpty())
615 if ( !sDatasource
.isEmpty() )
616 xForm
->setPropertyValue(FM_PROP_DATASOURCE
, Any(sDatasource
));
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
));
625 switch (nCommandType
)
627 case CommandType::TABLE
:
628 aCommandType
<<= sal_Int32(CommandType::TABLE
);
630 case CommandType::QUERY
:
631 aCommandType
<<= sal_Int32(CommandType::QUERY
);
634 aCommandType
<<= sal_Int32(CommandType::COMMAND
);
635 xForm
->setPropertyValue(FM_PROP_ESCAPE_PROCESSING
, css::uno::Any(2 == nCommandType
));
638 xForm
->setPropertyValue(FM_PROP_COMMANDTYPE
, aCommandType
);
644 TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
645 ::comphelper::disposeComponent(m_pImpl
->xDroppedResultSet
);
646 ::comphelper::disposeComponent(m_pImpl
->xDroppedStatement
);
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
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
);
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
705 Reference
< css::beans::XPropertySetInfo
> xPropsInfo
= xPropSet
->getPropertySetInfo();
706 if (xPropsInfo
.is() && xPropsInfo
->hasPropertyByName(FM_PROP_FORMATSSUPPLIER
))
707 nColType
= TYPE_FORMATTEDFIELD
;
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());
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;
756 // check for hidden cols
757 Reference
< css::beans::XPropertySet
> xCurCol
;
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
);
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
);
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
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());
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
);
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
)
845 aFieldType
= FM_COL_TEXTFIELD
;
847 else if (rExecutionResult
.startsWith(FM_COL_COMBOBOX
))
849 if (rExecutionResult
!= FM_COL_COMBOBOX
)
851 aFieldType
= FM_COL_COMBOBOX
;
853 else if (rExecutionResult
.startsWith(FM_COL_LISTBOX
))
855 if (rExecutionResult
!= FM_COL_LISTBOX
)
857 aFieldType
= FM_COL_LISTBOX
;
859 else if (rExecutionResult
.startsWith(FM_COL_CHECKBOX
))
861 if (rExecutionResult
!= FM_COL_CHECKBOX
)
863 aFieldType
= FM_COL_CHECKBOX
;
865 else if (rExecutionResult
.startsWith(FM_COL_DATEFIELD
))
867 if (rExecutionResult
!= FM_COL_DATEFIELD
)
869 aFieldType
= FM_COL_DATEFIELD
;
871 else if (rExecutionResult
.startsWith(FM_COL_TIMEFIELD
))
873 if (rExecutionResult
!= FM_COL_TIMEFIELD
)
875 aFieldType
= FM_COL_TIMEFIELD
;
877 else if (rExecutionResult
.startsWith(FM_COL_NUMERICFIELD
))
879 if (rExecutionResult
!= FM_COL_NUMERICFIELD
)
881 aFieldType
= FM_COL_NUMERICFIELD
;
883 else if (rExecutionResult
.startsWith(FM_COL_CURRENCYFIELD
))
885 if (rExecutionResult
!= FM_COL_CURRENCYFIELD
)
887 aFieldType
= FM_COL_CURRENCYFIELD
;
889 else if (rExecutionResult
.startsWith(FM_COL_PATTERNFIELD
))
891 if (rExecutionResult
!= FM_COL_PATTERNFIELD
)
893 aFieldType
= FM_COL_PATTERNFIELD
;
895 else if (rExecutionResult
.startsWith(FM_COL_FORMATTEDFIELD
))
897 if (rExecutionResult
!= FM_COL_FORMATTEDFIELD
)
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
);
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));
942 if ( !aFieldType
.isEmpty() )
946 Reference
< XGridColumnFactory
> xFactory( xCols
, UNO_QUERY_THROW
);
947 Reference
< XPropertySet
> xNewCol( xFactory
->createColumn( aFieldType
), UNO_SET_THROW
);
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
;
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
)
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
,
1003 void FmGridHeader::triggerColumnContextMenu( const ::Point
& _rPreferredPos
)
1006 sal_uInt16 nColId
= GetItemId( _rPreferredPos
);
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
);
1019 for (int i
= 0, nCount
= xContextMenu
->n_children(); i
< nCount
; ++i
)
1021 bEmpty
= !xContextMenu
->get_sensitive(xContextMenu
->get_id(i
));
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())
1046 triggerColumnContextMenu( rEvt
.GetMousePosPixel() );
1050 EditBrowserHeader::Command(rEvt
);
1054 FmGridControl::FmGridControl(
1055 const Reference
< css::uno::XComponentContext
>& _rxContext
,
1056 vcl::Window
* pParent
,
1057 FmXGridPeer
* _pPeer
,
1059 :DbGridControl(_rxContext
, pParent
, nBits
)
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
);
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);
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()))))
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
)
1133 markColumn(USHRT_MAX
);
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
));
1159 void FmGridControl::DeleteSelectedRows()
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() )
1170 // is the insert row selected
1171 if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
1175 if (nSelectedRows
<= 0)
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
);
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
))
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);
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);
1225 SetUpdateMode(true);
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
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();
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);
1258 // if it's not the row for inserting we keep the bookmark
1259 if (!IsInsertionRow(nIdx
))
1260 aBookmark
= m_pSeekCursor
->getBookmark();
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);
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?
1302 SetUpdateMode(false);
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
1312 if (aBookmark
.hasValue())
1313 getDataSource()->moveToBookmark(aBookmark
);
1314 // no valid bookmark so move to the insert row
1317 Reference
< XResultSetUpdate
> xUpdateCursor(Reference
< XInterface
>(*m_pDataCursor
), UNO_QUERY
);
1318 xUpdateCursor
->moveToInsertRow();
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() )
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
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
1375 if (IsInsertionRow(GetRowCount() - 1)) // not the insertion row
1376 SelectRow(GetRowCount() - 1, false);
1380 // select the remaining rows
1381 for (const sal_Int32 nSuccess
: aDeletedRows
)
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);
1402 SetUpdateMode(true);
1404 else // row could not be deleted
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())
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
1438 if (Controller().is() && Controller()->IsValueChangedFromSaved())
1440 if (!SaveModified())
1447 void FmGridControl::inserted()
1449 const DbGridRowRef
& xRow
= GetCurrentRow();
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
))
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!" );
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());
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());
1564 // locate the column and move in the model;
1566 DbGridColumn
* pCol
= DbGridControl::GetColumns()[ GetModelColumnPos(nId
) ].get();
1567 Reference
< css::beans::XPropertySet
> xCol
;
1569 // inserting must be based on the column positions
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();
1582 DBG_ASSERT(i
< xColumns
->getCount(), "Wrong css::sdbcx::Index");
1583 xColumns
->removeByIndex(i
);
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
)
1599 // if there is only one HandleColumn, then don't
1600 if (GetModelColCount())
1603 InsertHandleColumn();
1609 SetUpdateMode(false);
1611 // inserting must be based on the column positions
1614 for (i
= 0; i
< xColumns
->getCount(); ++i
)
1616 Reference
< css::beans::XPropertySet
> xCol(
1617 xColumns
->getByIndex(i
), css::uno::UNO_QUERY
);
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 ...)
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;
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() )
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;
1701 _pColumn
->SetObject( static_cast<sal_Int16
>(nFieldPos
) );
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
) )
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() )
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!");
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))
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());
1775 void FmGridControl::ShowColumn(sal_uInt16 nId
)
1777 DbGridControl::ShowColumn(nId
);
1779 sal_uInt16 nPos
= GetModelColumnPos(nId
);
1780 if (nPos
== sal_uInt16(-1))
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!" );
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);
1814 bAllSuccessful
= false;
1819 OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
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...
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
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();
1881 OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
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...)
1898 OUString
getColumnPropertyFromPeer(FmXGridPeer
* _pPeer
,sal_Int32 _nPosition
,const OUString
& _sPropName
)
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
;
1911 xProp
->getPropertyValue( _sPropName
) >>= sRetText
;
1912 } catch (UnknownPropertyException
const&) {
1913 TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
1922 // Object data and state
1923 OUString
FmGridControl::GetAccessibleObjectName( AccessibleBrowseBoxObjType _eObjType
,sal_Int32 _nPosition
) const
1928 case AccessibleBrowseBoxObjType::BrowseBox
:
1931 Reference
<XPropertySet
> xProp(GetPeer()->getColumns(),UNO_QUERY
);
1933 xProp
->getPropertyValue(FM_PROP_NAME
) >>= sRetText
;
1936 case AccessibleBrowseBoxObjType::ColumnHeaderCell
:
1937 sRetText
= getColumnPropertyFromPeer(
1940 sal::static_int_cast
< sal_uInt16
>(_nPosition
)),
1944 sRetText
= DbGridControl::GetAccessibleObjectName(_eObjType
,_nPosition
);
1949 OUString
FmGridControl::GetAccessibleObjectDescription( AccessibleBrowseBoxObjType _eObjType
,sal_Int32 _nPosition
) const
1954 case AccessibleBrowseBoxObjType::BrowseBox
:
1957 Reference
<XPropertySet
> xProp(GetPeer()->getColumns(),UNO_QUERY
);
1960 xProp
->getPropertyValue(FM_PROP_HELPTEXT
) >>= sRetText
;
1961 if ( sRetText
.isEmpty() )
1962 xProp
->getPropertyValue(FM_PROP_DESCRIPTION
) >>= sRetText
;
1966 case AccessibleBrowseBoxObjType::ColumnHeaderCell
:
1967 sRetText
= getColumnPropertyFromPeer(
1970 sal::static_int_cast
< sal_uInt16
>(_nPosition
)),
1972 if ( sRetText
.isEmpty() )
1973 sRetText
= getColumnPropertyFromPeer(
1976 sal::static_int_cast
< sal_uInt16
>(_nPosition
)),
1977 FM_PROP_DESCRIPTION
);
1981 sRetText
= DbGridControl::GetAccessibleObjectDescription(_eObjType
,_nPosition
);
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())
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
2004 // get the model col pos instead of the view col pos
2005 nSelectedColumn
= GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn
- 1));
2009 if (nSelectedColumn
== m_nCurrentSelectedColumn
)
2012 // BEFORE calling the select at the SelectionSupplier!
2013 m_nCurrentSelectedColumn
= nSelectedColumn
;
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
));
2035 xSelSupplier
->select(Any());
2044 m_bSelecting
= false;
2048 void FmGridControl::KeyInput( const KeyEvent
& rKEvt
)
2051 const vcl::KeyCode
& rKeyCode
= rKEvt
.GetKeyCode();
2053 && !rKeyCode
.IsShift()
2054 && !rKeyCode
.IsMod1()
2055 && !rKeyCode
.IsMod2()
2058 switch ( rKeyCode
.GetCode() )
2061 GetParent()->GrabFocus();
2065 if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn
>= 0 )
2067 Reference
< css::container::XIndexContainer
> xCols(GetPeer()->getColumns());
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");
2091 DbGridControl::KeyInput( rKEvt
);
2094 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */