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