1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <svx/fmgridif.hxx>
21 #include <fmprop.hxx>
22 #include <svx/fmtools.hxx>
23 #include <fmservs.hxx>
24 #include <fmurl.hxx>
25 #include <formcontrolfactory.hxx>
26 #include <gridcell.hxx>
27 #include <gridcols.hxx>
28 #include <svx/dbaexchange.hxx>
29 #include <svx/dialmgr.hxx>
30 #include <svx/strings.hrc>
31 #include <svx/fmgridcl.hxx>
32 #include <svx/svxdlg.hxx>
33 #include <svx/svxids.hrc>
34 #include <bitmaps.hlst>
35 
36 #include <com/sun/star/form/XConfirmDeleteListener.hpp>
37 #include <com/sun/star/form/XFormComponent.hpp>
38 #include <com/sun/star/form/XGridColumnFactory.hpp>
39 #include <com/sun/star/io/XPersistObject.hpp>
40 #include <com/sun/star/sdb/CommandType.hpp>
41 #include <com/sun/star/sdb/RowChangeAction.hpp>
42 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
43 #include <com/sun/star/sdbc/DataType.hpp>
44 #include <com/sun/star/sdbc/SQLException.hpp>
45 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
46 #include <com/sun/star/sdbc/XResultSetUpdate.hpp>
47 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
48 #include <com/sun/star/sdbcx/XDeleteRows.hpp>
49 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
50 #include <com/sun/star/util/XNumberFormats.hpp>
51 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
52 #include <com/sun/star/util/URLTransformer.hpp>
53 #include <com/sun/star/util/XURLTransformer.hpp>
54 #include <com/sun/star/view/XSelectionSupplier.hpp>
55 #include <comphelper/processfactory.hxx>
56 #include <comphelper/property.hxx>
57 #include <comphelper/string.hxx>
58 #include <comphelper/types.hxx>
59 #include <connectivity/dbtools.hxx>
60 #include <sfx2/dispatch.hxx>
61 #include <sfx2/viewfrm.hxx>
62 #include <svl/eitem.hxx>
63 #include <vcl/commandevent.hxx>
64 #include <vcl/svapp.hxx>
65 #include <tools/debug.hxx>
66 #include <tools/multisel.hxx>
67 #include <tools/diagnose_ex.h>
68 #include <vcl/help.hxx>
69 #include <vcl/image.hxx>
70 #include <vcl/settings.hxx>
71 #include <sal/log.hxx>
72 #include <i18nlangtag/languagetag.hxx>
73 #include <memory>
74 #include <string_view>
75 
76 using namespace ::com::sun::star::uno;
77 using namespace ::com::sun::star::view;
78 using namespace ::com::sun::star::beans;
79 using namespace ::com::sun::star::lang;
80 using namespace ::com::sun::star::sdbcx;
81 using namespace ::com::sun::star::sdbc;
82 using namespace ::com::sun::star::sdb;
83 using namespace ::com::sun::star::form;
84 using namespace ::com::sun::star::util;
85 using namespace ::com::sun::star::container;
86 using namespace ::cppu;
87 using namespace ::svxform;
88 using namespace ::svx;
89 using namespace ::dbtools;
90 
91 struct FmGridHeaderData
92 {
93     ODataAccessDescriptor   aDropData;
94     Point                   aDropPosPixel;
95     sal_Int8                nDropAction;
96     Reference< XInterface > xDroppedStatement;
97     Reference< XInterface > xDroppedResultSet;
98 };
99 
InsertMenuItem(weld::Menu & rMenu,int nMenuPos,std::string_view id,const OUString & rText,const OUString & rImgId)100 static void InsertMenuItem(weld::Menu& rMenu, int nMenuPos, std::string_view id, const OUString& rText, const OUString& rImgId)
101 {
102     rMenu.insert(nMenuPos, OUString::fromUtf8(id), rText, &rImgId, nullptr, nullptr, TRISTATE_INDET);
103 }
104 
FmGridHeader(BrowseBox * pParent,WinBits nWinBits)105 FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits)
106         :EditBrowserHeader(pParent, nWinBits)
107         ,DropTargetHelper(this)
108         ,m_pImpl(new FmGridHeaderData)
109 {
110 }
111 
~FmGridHeader()112 FmGridHeader::~FmGridHeader()
113 {
114     disposeOnce();
115 }
116 
dispose()117 void FmGridHeader::dispose()
118 {
119     m_pImpl.reset();
120     DropTargetHelper::dispose();
121     svt::EditBrowserHeader::dispose();
122 }
123 
GetModelColumnPos(sal_uInt16 nId) const124 sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const
125 {
126     return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId);
127 }
128 
notifyColumnSelect(sal_uInt16 nColumnId)129 void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId)
130 {
131     sal_uInt16 nPos = GetModelColumnPos(nColumnId);
132     Reference< XIndexAccess >  xColumns = static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns();
133     if ( nPos < xColumns->getCount() )
134     {
135         Reference< XSelectionSupplier >  xSelSupplier(xColumns, UNO_QUERY);
136         if ( xSelSupplier.is() )
137         {
138             Reference< XPropertySet >  xColumn;
139             xColumns->getByIndex(nPos) >>= xColumn;
140             xSelSupplier->select(makeAny(xColumn));
141         }
142     }
143 }
144 
Select()145 void FmGridHeader::Select()
146 {
147     EditBrowserHeader::Select();
148     notifyColumnSelect(GetCurItemId());
149 }
150 
RequestHelp(const HelpEvent & rHEvt)151 void FmGridHeader::RequestHelp( const HelpEvent& rHEvt )
152 {
153     sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
154     if ( nItemId )
155     {
156         if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
157         {
158             tools::Rectangle aItemRect = GetItemRect( nItemId );
159             Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
160             aItemRect.SetLeft( aPt.X() );
161             aItemRect.SetTop( aPt.Y() );
162             aPt = OutputToScreenPixel( aItemRect.BottomRight() );
163             aItemRect.SetRight( aPt.X() );
164             aItemRect.SetBottom( aPt.Y() );
165 
166             sal_uInt16 nPos = GetModelColumnPos(nItemId);
167             Reference< css::container::XIndexContainer >  xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
168             try
169             {
170                 Reference< css::beans::XPropertySet >  xColumn(xColumns->getByIndex(nPos),UNO_QUERY);
171                 OUString aHelpText;
172                 xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText;
173                 if ( aHelpText.isEmpty() )
174                     xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText;
175                 if ( !aHelpText.isEmpty() )
176                 {
177                     if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
178                         Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText );
179                     else
180                         Help::ShowQuickHelp( this, aItemRect, aHelpText );
181                     return;
182                 }
183             }
184             catch(Exception&)
185             {
186                 return;
187             }
188         }
189     }
190     EditBrowserHeader::RequestHelp( rHEvt );
191 }
192 
AcceptDrop(const AcceptDropEvent & rEvt)193 sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt )
194 {
195     // drop allowed in design mode only
196     if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
197         return DND_ACTION_NONE;
198 
199     // search for recognized formats
200     const DataFlavorExVector& rFlavors = GetDataFlavorExVector();
201     if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::FIELD_DESCRIPTOR))
202         return rEvt.mnAction;
203 
204     return DND_ACTION_NONE;
205 }
206 
ExecuteDrop(const ExecuteDropEvent & _rEvt)207 sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt )
208 {
209     if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
210         return DND_ACTION_NONE;
211 
212     TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable);
213 
214     // check the formats
215     bool bColumnDescriptor  = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
216     bool bFieldDescriptor   = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
217     if (!bColumnDescriptor && !bFieldDescriptor)
218     {
219         OSL_FAIL("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!");
220         return DND_ACTION_NONE;
221     }
222 
223     // extract the descriptor
224     OUString sDatasource, sCommand, sFieldName,sDatabaseLocation;
225     sal_Int32       nCommandType = CommandType::COMMAND;
226     Reference< XPreparedStatement >     xStatement;
227     Reference< XResultSet >             xResultSet;
228     Reference< XPropertySet >           xField;
229     Reference< XConnection >            xConnection;
230 
231     ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData);
232     if (aColumn.has(DataAccessDescriptorProperty::DataSource))  aColumn[DataAccessDescriptorProperty::DataSource]   >>= sDatasource;
233     if (aColumn.has(DataAccessDescriptorProperty::DatabaseLocation))    aColumn[DataAccessDescriptorProperty::DatabaseLocation] >>= sDatabaseLocation;
234     if (aColumn.has(DataAccessDescriptorProperty::Command))     aColumn[DataAccessDescriptorProperty::Command]      >>= sCommand;
235     if (aColumn.has(DataAccessDescriptorProperty::CommandType)) aColumn[DataAccessDescriptorProperty::CommandType]  >>= nCommandType;
236     if (aColumn.has(DataAccessDescriptorProperty::ColumnName))  aColumn[DataAccessDescriptorProperty::ColumnName]   >>= sFieldName;
237     if (aColumn.has(DataAccessDescriptorProperty::ColumnObject))aColumn[DataAccessDescriptorProperty::ColumnObject] >>= xField;
238     if (aColumn.has(DataAccessDescriptorProperty::Connection))  aColumn[DataAccessDescriptorProperty::Connection]   >>= xConnection;
239 
240     if  (   sFieldName.isEmpty()
241         ||  sCommand.isEmpty()
242         ||  (   sDatasource.isEmpty()
243             &&  sDatabaseLocation.isEmpty()
244             &&  !xConnection.is()
245             )
246         )
247     {
248         OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
249         return DND_ACTION_NONE;
250     }
251 
252     try
253     {
254         // need a connection
255         if (!xConnection.is())
256         {   // the transferable did not contain the connection -> build an own one
257             try
258             {
259                 OUString sSignificantSource( sDatasource.isEmpty() ? sDatabaseLocation : sDatasource );
260                 xConnection = getConnection_withFeedback(sSignificantSource, OUString(), OUString(),
261                                   static_cast<FmGridControl*>(GetParent())->getContext(), nullptr );
262             }
263             catch(NoSuchElementException&)
264             {   // allowed, means sDatasource isn't a valid data source name...
265             }
266             catch(Exception&)
267             {
268                 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
269             }
270 
271             if (!xConnection.is())
272             {
273                 OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
274                 return DND_ACTION_NONE;
275             }
276         }
277 
278         // try to obtain the column object
279         if (!xField.is())
280         {
281 #ifdef DBG_UTIL
282             Reference< XServiceInfo >  xServiceInfo(xConnection, UNO_QUERY);
283             DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)");
284 #endif
285 
286             Reference< XNameAccess > xFields;
287             switch (nCommandType)
288             {
289                 case CommandType::TABLE:
290                 {
291                     Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
292                     Reference< XColumnsSupplier >  xSupplyColumns;
293                     xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns;
294                     xFields = xSupplyColumns->getColumns();
295                 }
296                 break;
297                 case CommandType::QUERY:
298                 {
299                     Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
300                     Reference< XColumnsSupplier > xSupplyColumns;
301                     xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns;
302                     xFields  = xSupplyColumns->getColumns();
303                 }
304                 break;
305                 default:
306                 {
307                     xStatement = xConnection->prepareStatement(sCommand);
308                     // not interested in any results
309 
310                     Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY);
311                     xStatProps->setPropertyValue("MaxRows", makeAny(sal_Int32(0)));
312 
313                     xResultSet = xStatement->executeQuery();
314                     Reference< XColumnsSupplier >  xSupplyCols(xResultSet, UNO_QUERY);
315                     if (xSupplyCols.is())
316                         xFields = xSupplyCols->getColumns();
317                 }
318             }
319 
320             if (xFields.is() && xFields->hasByName(sFieldName))
321                 xFields->getByName(sFieldName) >>= xField;
322 
323             if (!xField.is())
324             {
325                 ::comphelper::disposeComponent(xStatement);
326                 return DND_ACTION_NONE;
327             }
328         }
329 
330         // do the drop asynchronously
331         // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu)
332         m_pImpl->aDropData = aColumn;
333         m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] <<= xConnection;
334         m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] <<= xField;
335 
336         m_pImpl->nDropAction = _rEvt.mnAction;
337         m_pImpl->aDropPosPixel = _rEvt.maPosPixel;
338         m_pImpl->xDroppedStatement = xStatement;
339         m_pImpl->xDroppedResultSet = xResultSet;
340 
341         PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop), nullptr, true);
342     }
343     catch (Exception&)
344     {
345         TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
346         ::comphelper::disposeComponent(xStatement);
347         return DND_ACTION_NONE;
348     }
349 
350     return DND_ACTION_LINK;
351 }
352 
IMPL_LINK_NOARG(FmGridHeader,OnAsyncExecuteDrop,void *,void)353 IMPL_LINK_NOARG( FmGridHeader, OnAsyncExecuteDrop, void*, void )
354 {
355     OUString             sCommand, sFieldName,sURL;
356     sal_Int32                   nCommandType = CommandType::COMMAND;
357     Reference< XPropertySet >   xField;
358     Reference< XConnection >    xConnection;
359 
360     OUString sDatasource = m_pImpl->aDropData.getDataSource();
361     if ( sDatasource.isEmpty() && m_pImpl->aDropData.has(DataAccessDescriptorProperty::ConnectionResource) )
362         m_pImpl->aDropData[DataAccessDescriptorProperty::ConnectionResource]    >>= sURL;
363     m_pImpl->aDropData[DataAccessDescriptorProperty::Command]       >>= sCommand;
364     m_pImpl->aDropData[DataAccessDescriptorProperty::CommandType]   >>= nCommandType;
365     m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnName]    >>= sFieldName;
366     m_pImpl->aDropData[DataAccessDescriptorProperty::Connection]    >>= xConnection;
367     m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject]  >>= xField;
368 
369     try
370     {
371         // need number formats
372         Reference< XNumberFormatsSupplier > xSupplier = getNumberFormats(xConnection, true);
373         Reference< XNumberFormats >  xNumberFormats;
374         if (xSupplier.is())
375             xNumberFormats = xSupplier->getNumberFormats();
376         if (!xNumberFormats.is())
377         {
378             ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
379             ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
380             return;
381         }
382 
383         // The field now needs two pieces of information:
384         // a.) Name of the field for label and ControlSource
385         // b.) FormatKey, to determine which field is to be created
386         sal_Int32 nDataType = 0;
387         xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType;
388         // these datatypes can not be processed in Gridcontrol
389         switch (nDataType)
390         {
391             case DataType::BLOB:
392             case DataType::LONGVARBINARY:
393             case DataType::BINARY:
394             case DataType::VARBINARY:
395             case DataType::OTHER:
396                 ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
397                 ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
398                 return;
399         }
400 
401         // Creating the column
402         Reference< XIndexContainer >  xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
403         Reference< XGridColumnFactory >  xFactory(xCols, UNO_QUERY);
404 
405         sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel);
406         // insert position, always before the current column
407         sal_uInt16 nPos = GetModelColumnPos(nColId);
408         Reference< XPropertySet >  xCol, xSecondCol;
409 
410         // Create Column based on type, default textfield
411         std::vector<OString> aPossibleTypes;
412         std::vector<OUString> aImgResId;
413         std::vector<const char*> aStrResId;
414 
415         switch (nDataType)
416         {
417             case DataType::BIT:
418             case DataType::BOOLEAN:
419                 aPossibleTypes.emplace_back(FM_COL_CHECKBOX);
420                 aImgResId.emplace_back(RID_SVXBMP_CHECKBOX);
421                 aStrResId.emplace_back(RID_STR_PROPTITLE_CHECKBOX);
422                 break;
423             case DataType::TINYINT:
424             case DataType::SMALLINT:
425             case DataType::INTEGER:
426                 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
427                 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
428                 aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
429                 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
430                 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
431                 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
432                 break;
433             case DataType::REAL:
434             case DataType::DOUBLE:
435             case DataType::NUMERIC:
436             case DataType::DECIMAL:
437                 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
438                 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
439                 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
440                 aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
441                 aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
442                 aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
443                 break;
444             case DataType::TIMESTAMP:
445                 aPossibleTypes.emplace_back("dateandtimefield");
446                 aImgResId.emplace_back(RID_SVXBMP_DATE_N_TIME_FIELDS);
447                 aStrResId.emplace_back(RID_STR_DATE_AND_TIME);
448                 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
449                 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
450                 aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
451                 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
452                 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
453                 aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
454                 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
455                 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
456                 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
457                 break;
458             case DataType::DATE:
459                 aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
460                 aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
461                 aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
462                 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
463                 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
464                 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
465                 break;
466             case DataType::TIME:
467                 aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
468                 aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
469                 aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
470                 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
471                 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
472                 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
473                 break;
474             case DataType::CHAR:
475             case DataType::VARCHAR:
476             case DataType::LONGVARCHAR:
477             default:
478                 aPossibleTypes.emplace_back(FM_COL_TEXTFIELD);
479                 aImgResId.emplace_back(RID_SVXBMP_EDITBOX);
480                 aStrResId.emplace_back(RID_STR_PROPTITLE_EDIT);
481                 aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
482                 aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
483                 aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
484                 break;
485         }
486         // if it's a currency field, a "currency field" option
487         try
488         {
489             if  (   ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField)
490                 &&  ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY)))
491             {
492                 aPossibleTypes.insert(aPossibleTypes.begin(), FM_COL_CURRENCYFIELD);
493                 aImgResId.insert(aImgResId.begin(), RID_SVXBMP_CURRENCYFIELD);
494                 aStrResId.insert(aStrResId.begin(), RID_STR_PROPTITLE_CURRENCYFIELD);
495             }
496         }
497         catch (const Exception&)
498         {
499             TOOLS_WARN_EXCEPTION("svx", "");
500         }
501 
502         assert(aPossibleTypes.size() == aImgResId.size());
503 
504         bool bDateNTimeCol = false;
505         if (!aPossibleTypes.empty())
506         {
507             OString sPreferredType = aPossibleTypes[0];
508             if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.size() > 1))
509             {
510                 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/colsmenu.ui"));
511                 std::unique_ptr<weld::Menu> xTypeMenu(xBuilder->weld_menu("insertmenu"));
512 
513                 int nMenuPos = 0;
514                 std::vector<OString>::const_iterator iter;
515                 std::vector<const char*>::const_iterator striter;
516                 std::vector<OUString>::const_iterator imgiter;
517                 for (iter = aPossibleTypes.begin(), imgiter = aImgResId.begin(), striter = aStrResId.begin();
518                      iter != aPossibleTypes.end(); ++iter, ++striter, ++imgiter)
519                 {
520                     InsertMenuItem(*xTypeMenu, nMenuPos++, *iter, SvxResId(*striter), *imgiter);
521                 }
522 
523                 ::tools::Rectangle aRect(m_pImpl->aDropPosPixel, Size(1,1));
524                 weld::Window* pParent = weld::GetPopupParent(*this, aRect);
525                 OString sResult = xTypeMenu->popup_at_rect(pParent, aRect);
526                 if (!sResult.isEmpty())
527                     sPreferredType = sResult;
528             }
529 
530             bDateNTimeCol = sPreferredType == "dateandtimefield";
531             sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1;
532             OUString sFieldService;
533             while (nColCount--)
534             {
535                 if (bDateNTimeCol)
536                     sPreferredType = nColCount ? FM_COL_DATEFIELD : FM_COL_TIMEFIELD;
537 
538                 sFieldService = OUString::fromUtf8(sPreferredType);
539                 Reference< XPropertySet >  xThisRoundCol;
540                 if ( !sFieldService.isEmpty() )
541                     xThisRoundCol = xFactory->createColumn(sFieldService);
542                 if (nColCount)
543                     xSecondCol = xThisRoundCol;
544                 else
545                     xCol = xThisRoundCol;
546             }
547         }
548 
549         if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is()))
550         {
551             ::comphelper::disposeComponent(xCol);   // in case only the creation of the second column failed
552             ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
553             ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
554             return;
555         }
556 
557         if (bDateNTimeCol)
558         {
559             OUString sTimePostfix(SvxResId(RID_STR_POSTFIX_TIME));
560             xCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sTimePostfix ) ) );
561 
562             OUString sDatePostfix(SvxResId( RID_STR_POSTFIX_DATE));
563             xSecondCol->setPropertyValue(FM_PROP_LABEL, makeAny( OUString( sFieldName + sDatePostfix ) ) );
564         }
565         else
566             xCol->setPropertyValue(FM_PROP_LABEL, makeAny(sFieldName));
567 
568         // insert now
569         Any aElement;
570         aElement <<= xCol;
571 
572         xCols->insertByIndex(nPos, aElement);
573 
574         FormControlFactory aControlFactory;
575         aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol );
576         FormControlFactory::initializeFieldDependentProperties( xField, xCol, xNumberFormats );
577 
578         xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
579         if ( xSecondCol.is() )
580             xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, makeAny(sFieldName));
581 
582         if (bDateNTimeCol)
583         {
584             OUString aPostfix[] = {
585                 SvxResId(RID_STR_POSTFIX_DATE),
586                 SvxResId(RID_STR_POSTFIX_TIME)
587             };
588 
589             for ( size_t i=0; i<2; ++i )
590             {
591                 OUString sPurePostfix = comphelper::string::stripStart(aPostfix[i], ' ');
592                 sPurePostfix = comphelper::string::stripStart(sPurePostfix, '(');
593                 sPurePostfix = comphelper::string::stripEnd(sPurePostfix, ')');
594                 OUString sRealName = sFieldName + "_" + sPurePostfix;
595                 if (i)
596                     xSecondCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
597                 else
598                     xCol->setPropertyValue(FM_PROP_NAME, makeAny(sRealName));
599             }
600         }
601         else
602             xCol->setPropertyValue(FM_PROP_NAME, makeAny(sFieldName));
603 
604         if (bDateNTimeCol)
605         {
606             aElement <<= xSecondCol;
607             xCols->insertByIndex(nPos == sal_uInt16(-1) ? nPos : ++nPos, aElement);
608         }
609 
610         // is the component::Form tied to the database?
611         Reference< XFormComponent >  xFormCp(xCols, UNO_QUERY);
612         Reference< XPropertySet >  xForm(xFormCp->getParent(), UNO_QUERY);
613         if (xForm.is())
614         {
615             if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).isEmpty())
616             {
617                 if ( !sDatasource.isEmpty() )
618                     xForm->setPropertyValue(FM_PROP_DATASOURCE, makeAny(sDatasource));
619                 else
620                     xForm->setPropertyValue(FM_PROP_URL, makeAny(sURL));
621             }
622 
623             if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).isEmpty())
624             {
625                 xForm->setPropertyValue(FM_PROP_COMMAND, makeAny(sCommand));
626                 Any aCommandType;
627                 switch (nCommandType)
628                 {
629                     case CommandType::TABLE:
630                         aCommandType <<= sal_Int32(CommandType::TABLE);
631                         break;
632                     case CommandType::QUERY:
633                         aCommandType <<= sal_Int32(CommandType::QUERY);
634                         break;
635                     default:
636                         aCommandType <<= sal_Int32(CommandType::COMMAND);
637                         xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, css::uno::Any(2 == nCommandType));
638                         break;
639                 }
640                 xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType);
641             }
642         }
643     }
644     catch (Exception&)
645     {
646         TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
647         ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
648         ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
649         return;
650     }
651 
652     ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
653     ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
654 }
655 
PreExecuteColumnContextMenu(sal_uInt16 nColId,weld::Menu & rMenu,weld::Menu & rInsertMenu,weld::Menu & rChangeMenu,weld::Menu & rShowMenu)656 void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, weld::Menu& rMenu,
657                                                weld::Menu& rInsertMenu, weld::Menu& rChangeMenu,
658                                                weld::Menu& rShowMenu)
659 {
660     bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode();
661 
662     Reference< css::container::XIndexContainer >  xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
663     // Building of the Insert Menu
664     // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND
665     if(nColId > 0)
666     {
667         sal_uInt16 nPos2 = GetModelColumnPos(nColId);
668 
669         Reference< css::container::XIndexContainer >  xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
670         Reference< css::beans::XPropertySet>          xColumn( xColumns->getByIndex(nPos2), css::uno::UNO_QUERY);
671         Reference< css::view::XSelectionSupplier >    xSelSupplier(xColumns, UNO_QUERY);
672         if (xSelSupplier.is())
673             xSelSupplier->select(makeAny(xColumn));
674     }
675 
676     // insert position, always before the current column
677     sal_uInt16 nPos = GetModelColumnPos(nColId);
678     bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId);
679 
680     if (bDesignMode)
681     {
682         int nMenuPos = 0;
683         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_TEXTFIELD, SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
684         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_CHECKBOX, SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
685         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_COMBOBOX, SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
686         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_LISTBOX, SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
687         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_DATEFIELD, SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
688         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_TIMEFIELD, SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
689         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_NUMERICFIELD, SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
690         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_CURRENCYFIELD, SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
691         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_PATTERNFIELD, SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
692         InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_FORMATTEDFIELD, SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
693     }
694 
695     if (xCols.is() && nColId)
696     {
697         Reference< css::beans::XPropertySet > xPropSet( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
698 
699         Reference< css::io::XPersistObject >  xServiceQuestion(xPropSet, UNO_QUERY);
700         sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0;
701         if (nColType == TYPE_TEXTFIELD)
702         {   // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD
703             // in both cases. And as columns don't have a css::lang::XServiceInfo interface, we have to distinguish both
704             // types via the existence of special properties
705             if (xPropSet.is())
706             {
707                 Reference< css::beans::XPropertySetInfo >  xPropsInfo = xPropSet->getPropertySetInfo();
708                 if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
709                     nColType = TYPE_FORMATTEDFIELD;
710             }
711         }
712 
713         if (bDesignMode)
714         {
715             int nMenuPos = 0;
716             if (nColType != TYPE_TEXTFIELD)
717                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_TEXTFIELD"1", SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
718             if (nColType != TYPE_CHECKBOX)
719                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_CHECKBOX"1", SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
720             if (nColType != TYPE_COMBOBOX)
721                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_COMBOBOX"1", SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
722             if (nColType != TYPE_LISTBOX)
723                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_LISTBOX"1", SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
724             if (nColType != TYPE_DATEFIELD)
725                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_DATEFIELD"1", SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
726             if (nColType != TYPE_TIMEFIELD)
727                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_TIMEFIELD"1", SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
728             if (nColType != TYPE_NUMERICFIELD)
729                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_NUMERICFIELD"1", SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
730             if (nColType != TYPE_CURRENCYFIELD)
731                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_CURRENCYFIELD"1", SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
732             if (nColType != TYPE_PATTERNFIELD)
733                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_PATTERNFIELD"1", SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
734             if (nColType != TYPE_FORMATTEDFIELD)
735                 InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_FORMATTEDFIELD"1", SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
736         }
737 
738 
739         rMenu.set_visible("change", bDesignMode && bMarked && xCols.is());
740         rMenu.set_sensitive("change", bDesignMode && bMarked && xCols.is());
741     }
742     else
743     {
744         rMenu.set_visible("change", false);
745         rMenu.set_sensitive("change", false);
746     }
747 
748     rMenu.set_visible("insert", bDesignMode && xCols.is());
749     rMenu.set_sensitive("insert", bDesignMode && xCols.is());
750     rMenu.set_visible("delete", bDesignMode && bMarked && xCols.is());
751     rMenu.set_sensitive("delete", bDesignMode && bMarked && xCols.is());
752     rMenu.set_visible("column", bDesignMode && bMarked && xCols.is());
753     rMenu.set_sensitive("column", bDesignMode && bMarked && xCols.is());
754 
755     sal_uInt16 nHiddenCols = 0;
756     if (xCols.is())
757     {
758         // check for hidden cols
759         Reference< css::beans::XPropertySet >  xCurCol;
760         Any aHidden,aName;
761         for (sal_Int32 i=0; i<xCols->getCount(); ++i)
762         {
763             xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
764             DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !");
765             aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
766             DBG_ASSERT(aHidden.getValueType().getTypeClass() == TypeClass_BOOLEAN,
767                 "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !");
768             if (::comphelper::getBOOL(aHidden))
769             {
770                 // put the column name into the 'show col' menu
771                 if (nHiddenCols < 16)
772                 {
773                     // (only the first 16 items to keep the menu rather small)
774                     aName = xCurCol->getPropertyValue(FM_PROP_LABEL);
775                     // the ID is arbitrary, but should be unique within the whole menu
776                     rMenu.insert(nHiddenCols, OUString::number(nHiddenCols + 1), ::comphelper::getString(aName),
777                         nullptr, nullptr, nullptr, TRISTATE_INDET);
778                 }
779                 ++nHiddenCols;
780             }
781         }
782     }
783     rShowMenu.set_visible("more", xCols.is() && (nHiddenCols > 16));
784     rMenu.set_visible("show", xCols.is() && (nHiddenCols > 0));
785     rMenu.set_sensitive("show", xCols.is() && (nHiddenCols > 0));
786 
787     // allow the 'hide column' item ?
788     bool bAllowHide = bMarked;                                          // a column is marked
789     bAllowHide = bAllowHide || (!bDesignMode && (nPos != sal_uInt16(-1)));  // OR we are in alive mode and have hit a column
790     bAllowHide = bAllowHide && xCols.is();                              // AND we have a column container
791     bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1);     // AND there are at least two visible columns
792     rMenu.set_visible("hide", bAllowHide);
793     rMenu.set_sensitive("hide", bAllowHide);
794 
795     if (!bMarked)
796         return;
797 
798     SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
799     // ask the bindings of the current view frame (which should be the one we're residing in) for the state
800     if (pCurrentFrame)
801     {
802         std::unique_ptr<SfxPoolItem> pItem;
803         SfxItemState eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem);
804 
805         if (eState >= SfxItemState::DEFAULT && pItem != nullptr)
806         {
807             bool bChecked = dynamic_cast<const SfxBoolItem*>( pItem.get()) != nullptr && static_cast<SfxBoolItem*>(pItem.get())->GetValue();
808             rMenu.set_active("column", bChecked);
809         }
810     }
811 }
812 
813 namespace {
814 
815 enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone };
816 
817 }
818 
PostExecuteColumnContextMenu(sal_uInt16 nColId,const weld::Menu & rMenu,const OString & rExecutionResult)819 void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const weld::Menu& rMenu, const OString& rExecutionResult)
820 {
821     Reference< css::container::XIndexContainer >  xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
822     sal_uInt16 nPos = GetModelColumnPos(nColId);
823 
824     OUString aFieldType;
825     bool    bReplace = false;
826     InspectorAction eInspectorAction = eNone;
827 
828     if (rExecutionResult == "delete")
829     {
830         Reference< XInterface > xCol(
831             xCols->getByIndex(nPos), css::uno::UNO_QUERY);
832         xCols->removeByIndex(nPos);
833         ::comphelper::disposeComponent(xCol);
834     }
835     else if (rExecutionResult == "hide")
836     {
837         Reference< css::beans::XPropertySet > xCurCol( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
838         xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(true));
839     }
840     else if (rExecutionResult == "column")
841     {
842         eInspectorAction = rMenu.get_active("column") ? eOpenInspector : eCloseInspector;
843     }
844     else if (rExecutionResult.startsWith(FM_COL_TEXTFIELD))
845     {
846         if (rExecutionResult != FM_COL_TEXTFIELD)
847             bReplace = true;
848         aFieldType = FM_COL_TEXTFIELD;
849     }
850     else if (rExecutionResult.startsWith(FM_COL_COMBOBOX))
851     {
852         if (rExecutionResult != FM_COL_COMBOBOX)
853             bReplace = true;
854         aFieldType = FM_COL_COMBOBOX;
855     }
856     else if (rExecutionResult.startsWith(FM_COL_LISTBOX))
857     {
858         if (rExecutionResult != FM_COL_LISTBOX)
859             bReplace = true;
860         aFieldType = FM_COL_LISTBOX;
861     }
862     else if (rExecutionResult.startsWith(FM_COL_CHECKBOX))
863     {
864         if (rExecutionResult != FM_COL_CHECKBOX)
865             bReplace = true;
866         aFieldType = FM_COL_CHECKBOX;
867     }
868     else if (rExecutionResult.startsWith(FM_COL_DATEFIELD))
869     {
870         if (rExecutionResult != FM_COL_DATEFIELD)
871             bReplace = true;
872         aFieldType = FM_COL_DATEFIELD;
873     }
874     else if (rExecutionResult.startsWith(FM_COL_TIMEFIELD))
875     {
876         if (rExecutionResult != FM_COL_TIMEFIELD)
877             bReplace = true;
878         aFieldType = FM_COL_TIMEFIELD;
879     }
880     else if (rExecutionResult.startsWith(FM_COL_NUMERICFIELD))
881     {
882         if (rExecutionResult != FM_COL_NUMERICFIELD)
883             bReplace = true;
884         aFieldType = FM_COL_NUMERICFIELD;
885     }
886     else if (rExecutionResult.startsWith(FM_COL_CURRENCYFIELD))
887     {
888         if (rExecutionResult != FM_COL_CURRENCYFIELD)
889             bReplace = true;
890         aFieldType = FM_COL_CURRENCYFIELD;
891     }
892     else if (rExecutionResult.startsWith(FM_COL_PATTERNFIELD))
893     {
894         if (rExecutionResult != FM_COL_PATTERNFIELD)
895             bReplace = true;
896         aFieldType = FM_COL_PATTERNFIELD;
897     }
898     else if (rExecutionResult.startsWith(FM_COL_FORMATTEDFIELD))
899     {
900         if (rExecutionResult != FM_COL_FORMATTEDFIELD)
901             bReplace = true;
902         aFieldType = FM_COL_FORMATTEDFIELD;
903     }
904     else if (rExecutionResult == "more")
905     {
906         SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
907         ScopedVclPtr<AbstractFmShowColsDialog> pDlg(pFact->CreateFmShowColsDialog(GetFrameWeld()));
908         pDlg->SetColumns(xCols);
909         pDlg->Execute();
910     }
911     else if (rExecutionResult == "all")
912     {
913         // just iterate through all the cols ...
914         Reference< css::beans::XPropertySet >  xCurCol;
915         for (sal_Int32 i=0; i<xCols->getCount(); ++i)
916         {
917             xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
918             xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
919         }
920         // TODO : there must be a more clever way to do this...
921         // with the above the view is updated after every single model update ...
922     }
923     else if (!rExecutionResult.isEmpty())
924     {
925         sal_Int32 nExecutionResult = rExecutionResult.toInt32();
926         if (nExecutionResult>0 && nExecutionResult<=16)
927         {
928             // it was a "show column/<colname>" command (there are at most 16 such items)
929             // search the nExecutionResult'th hidden col
930             Reference< css::beans::XPropertySet >  xCurCol;
931             for (sal_Int32 i=0; i<xCols->getCount() && nExecutionResult; ++i)
932             {
933                 xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
934                 Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
935                 if (::comphelper::getBOOL(aHidden))
936                     if (!--nExecutionResult)
937                     {
938                         xCurCol->setPropertyValue(FM_PROP_HIDDEN, makeAny(false));
939                         break;
940                     }
941             }
942         }
943     }
944 
945     if ( !aFieldType.isEmpty() )
946     {
947         try
948         {
949             Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW );
950             Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW );
951 
952             if ( bReplace )
953             {
954                 // rescue over a few properties
955                 Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY );
956 
957                 TransferFormComponentProperties(
958                     xReplaced, xNewCol, Application::GetSettings().GetUILanguageTag().getLocale() );
959 
960                 xCols->replaceByIndex( nPos, makeAny( xNewCol ) );
961                 ::comphelper::disposeComponent( xReplaced );
962 
963                 eInspectorAction = eUpdateInspector;
964             }
965             else
966             {
967                 FormControlFactory factory;
968 
969                 OUString sLabel = FormControlFactory::getDefaultUniqueName_ByComponentType(
970                     Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol );
971                 xNewCol->setPropertyValue( FM_PROP_LABEL, makeAny( sLabel ) );
972                 xNewCol->setPropertyValue( FM_PROP_NAME, makeAny( sLabel ) );
973 
974                 factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol );
975 
976                 xCols->insertByIndex( nPos, makeAny( xNewCol ) );
977             }
978         }
979         catch( const Exception& )
980         {
981             DBG_UNHANDLED_EXCEPTION("svx");
982         }
983     }
984 
985     SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
986     OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" );
987     if ( !pCurrentFrame )
988         return;
989 
990     if ( eInspectorAction == eUpdateInspector )
991     {
992         if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) )
993             eInspectorAction = eNone;
994     }
995 
996     if ( eInspectorAction != eNone )
997     {
998         SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction != eCloseInspector );
999 
1000         pCurrentFrame->GetBindings().GetDispatcher()->ExecuteList(
1001                 SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON,
1002                 { &aShowItem });
1003     }
1004 }
1005 
triggerColumnContextMenu(const::Point & _rPreferredPos)1006 void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos )
1007 {
1008     // the affected col
1009     sal_uInt16 nColId = GetItemId( _rPreferredPos );
1010 
1011     // the menu
1012     std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/colsmenu.ui"));
1013     std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
1014     std::unique_ptr<weld::Menu> xInsertMenu(xBuilder->weld_menu("insertmenu"));
1015     std::unique_ptr<weld::Menu> xChangeMenu(xBuilder->weld_menu("changemenu"));
1016     std::unique_ptr<weld::Menu> xShowMenu(xBuilder->weld_menu("showmenu"));
1017 
1018     // let derivatives modify the menu
1019     PreExecuteColumnContextMenu(nColId, *xContextMenu, *xInsertMenu, *xChangeMenu, *xShowMenu);
1020 
1021     bool bEmpty = true;
1022     for (int i = 0, nCount = xContextMenu->n_children(); i < nCount; ++i)
1023     {
1024         bEmpty = !xContextMenu->get_sensitive(xContextMenu->get_id(i));
1025         if (!bEmpty)
1026             break;
1027     }
1028     if (bEmpty)
1029         return;
1030 
1031     // execute the menu
1032     ::tools::Rectangle aRect(_rPreferredPos, Size(1,1));
1033     weld::Window* pParent = weld::GetPopupParent(*this, aRect);
1034     OString sResult = xContextMenu->popup_at_rect(pParent, aRect);
1035 
1036     // let derivatives handle the result
1037     PostExecuteColumnContextMenu(nColId, *xContextMenu, sResult);
1038 }
1039 
Command(const CommandEvent & rEvt)1040 void FmGridHeader::Command(const CommandEvent& rEvt)
1041 {
1042     switch (rEvt.GetCommand())
1043     {
1044         case CommandEventId::ContextMenu:
1045         {
1046             if (!rEvt.IsMouseEvent())
1047                 return;
1048 
1049             triggerColumnContextMenu( rEvt.GetMousePosPixel() );
1050         }
1051         break;
1052         default:
1053             EditBrowserHeader::Command(rEvt);
1054     }
1055 }
1056 
FmGridControl(const Reference<css::uno::XComponentContext> & _rxContext,vcl::Window * pParent,FmXGridPeer * _pPeer,WinBits nBits)1057 FmGridControl::FmGridControl(
1058                 const Reference< css::uno::XComponentContext >& _rxContext,
1059                 vcl::Window* pParent,
1060                 FmXGridPeer* _pPeer,
1061                 WinBits nBits)
1062         :DbGridControl(_rxContext, pParent, nBits)
1063         ,m_pPeer(_pPeer)
1064         ,m_nCurrentSelectedColumn(-1)
1065         ,m_nMarkedColumnId(BROWSER_INVALIDID)
1066         ,m_bSelecting(false)
1067         ,m_bInColumnMove(false)
1068 {
1069     EnableInteractiveRowHeight( );
1070 }
1071 
Command(const CommandEvent & _rEvt)1072 void FmGridControl::Command(const CommandEvent& _rEvt)
1073 {
1074     if ( CommandEventId::ContextMenu == _rEvt.GetCommand() )
1075     {
1076         FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() );
1077         if ( pMyHeader && !_rEvt.IsMouseEvent() )
1078         {   // context menu requested by keyboard
1079             if  ( 1 == GetSelectColumnCount() || IsDesignMode() )
1080             {
1081                 sal_uInt16 nSelId = GetColumnId(
1082                     sal::static_int_cast< sal_uInt16 >( FirstSelectedColumn() ) );
1083                 ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
1084 
1085                 Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) );
1086                 pMyHeader->triggerColumnContextMenu(aRelativePos);
1087 
1088                 // handled
1089                 return;
1090             }
1091         }
1092     }
1093 
1094     DbGridControl::Command( _rEvt );
1095 }
1096 
1097 // css::beans::XPropertyChangeListener
propertyChange(const css::beans::PropertyChangeEvent & evt)1098 void FmGridControl::propertyChange(const css::beans::PropertyChangeEvent& evt)
1099 {
1100     if (evt.PropertyName == FM_PROP_ROWCOUNT)
1101     {
1102         // if we're not in the main thread call AdjustRows asynchronously
1103         implAdjustInSolarThread(true);
1104         return;
1105     }
1106 
1107     const DbGridRowRef& xRow = GetCurrentRow();
1108     // no adjustment of the properties is carried out during positioning
1109     Reference<XPropertySet> xSet(evt.Source,UNO_QUERY);
1110     if (!(xRow.is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark()))))
1111         return;
1112 
1113     if (evt.PropertyName == FM_PROP_ISMODIFIED)
1114     {
1115         // modified or clean ?
1116         GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean;
1117         if (eStatus != xRow->GetStatus())
1118         {
1119             xRow->SetStatus(eStatus);
1120             SolarMutexGuard aGuard;
1121             RowModified(GetCurrentPos());
1122         }
1123     }
1124 }
1125 
SetDesignMode(bool bMode)1126 void FmGridControl::SetDesignMode(bool bMode)
1127 {
1128     bool bOldMode = IsDesignMode();
1129     DbGridControl::SetDesignMode(bMode);
1130     if (bOldMode == bMode)
1131         return;
1132 
1133     if (!bMode)
1134     {
1135         // cancel selection
1136         markColumn(USHRT_MAX);
1137     }
1138     else
1139     {
1140         Reference< css::container::XIndexContainer >  xColumns(GetPeer()->getColumns());
1141         Reference< css::view::XSelectionSupplier >  xSelSupplier(xColumns, UNO_QUERY);
1142         if (xSelSupplier.is())
1143         {
1144             Any aSelection = xSelSupplier->getSelection();
1145             Reference< css::beans::XPropertySet >  xColumn;
1146             if (aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE)
1147                 xColumn.set(aSelection, css::uno::UNO_QUERY);
1148             Reference< XInterface >  xCurrent;
1149             for (sal_Int32 i=0; i<xColumns->getCount(); ++i)
1150             {
1151                 xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1152                 if (xCurrent == xColumn)
1153                 {
1154                     markColumn(GetColumnIdFromModelPos(i));
1155                     break;
1156                 }
1157             }
1158         }
1159     }
1160 }
1161 
DeleteSelectedRows()1162 void FmGridControl::DeleteSelectedRows()
1163 {
1164     if (!m_pSeekCursor)
1165         return;
1166 
1167     // how many rows are selected?
1168     sal_Int32 nSelectedRows = GetSelectRowCount();
1169 
1170     // the current line should be deleted but it is currently in edit mode
1171     if ( IsCurrentAppending() )
1172         return;
1173     // is the insert row selected
1174     if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
1175         nSelectedRows -= 1;
1176 
1177     // nothing to do
1178     if (nSelectedRows <= 0)
1179         return;
1180 
1181     // try to confirm the delete
1182     Reference< css::frame::XDispatchProvider >  xDispatcher = static_cast<css::frame::XDispatchProvider*>(GetPeer());
1183     if (xDispatcher.is())
1184     {
1185         css::util::URL aUrl;
1186         aUrl.Complete = FMURL_CONFIRM_DELETION;
1187         Reference< css::util::XURLTransformer > xTransformer(
1188             css::util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
1189         xTransformer->parseStrict( aUrl );
1190 
1191         Reference< css::frame::XDispatch >  xDispatch = xDispatcher->queryDispatch(aUrl, OUString(), 0);
1192         Reference< css::form::XConfirmDeleteListener >  xConfirm(xDispatch, UNO_QUERY);
1193         if (xConfirm.is())
1194         {
1195             css::sdb::RowChangeEvent aEvent;
1196             aEvent.Source = Reference< XInterface >(*getDataSource());
1197             aEvent.Rows = nSelectedRows;
1198             aEvent.Action = css::sdb::RowChangeAction::DELETE;
1199             if (!xConfirm->confirmDelete(aEvent))
1200                 return;
1201         }
1202     }
1203 
1204     const MultiSelection* pRowSelection = GetSelection();
1205     if ( pRowSelection && pRowSelection->IsAllSelected() )
1206     {
1207         BeginCursorAction();
1208         CursorWrapper* pCursor = getDataSource();
1209         Reference< XResultSetUpdate >  xUpdateCursor(Reference< XInterface >(*pCursor), UNO_QUERY);
1210         try
1211         {
1212             pCursor->beforeFirst();
1213             while( pCursor->next() )
1214                 xUpdateCursor->deleteRow();
1215 
1216             SetUpdateMode(false);
1217             SetNoSelection();
1218 
1219             xUpdateCursor->moveToInsertRow();
1220         }
1221         catch(const Exception&)
1222         {
1223             TOOLS_WARN_EXCEPTION("svx", "Exception caught while deleting rows!");
1224         }
1225         // adapt to the data cursor
1226         AdjustDataSource(true);
1227         EndCursorAction();
1228         SetUpdateMode(true);
1229     }
1230     else
1231     {
1232         Reference< css::sdbcx::XDeleteRows >  xDeleteThem(Reference< XInterface >(*getDataSource()), UNO_QUERY);
1233 
1234         // collect the bookmarks of the selected rows
1235         Sequence < Any> aBookmarks = getSelectionBookmarks();
1236 
1237         // determine the next row to position after deletion
1238         Any aBookmark;
1239         bool bNewPos = false;
1240         // if the current row isn't selected we take the row as row after deletion
1241         OSL_ENSURE( GetCurrentRow().is(), "FmGridControl::DeleteSelectedRows: no current row here?" );
1242             // crash reports suggest it can happen we don't have a current row - how?
1243             // #154303# / 2008-04-23 / frank.schoenheit@sun.com
1244         if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().is() )
1245         {
1246             aBookmark = GetCurrentRow()->GetBookmark();
1247             bNewPos   = true;
1248         }
1249         else
1250         {
1251             // we look for the first row after the selected block for selection
1252             tools::Long nIdx = LastSelectedRow() + 1;
1253             if (nIdx < GetRowCount() - 1)
1254             {
1255                 // there is a next row to position on
1256                 if (SeekCursor(nIdx))
1257                 {
1258                     GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1259 
1260                     bNewPos = true;
1261                     // if it's not the row for inserting we keep the bookmark
1262                     if (!IsInsertionRow(nIdx))
1263                         aBookmark = m_pSeekCursor->getBookmark();
1264                 }
1265             }
1266             else
1267             {
1268                 // we look for the first row before the selected block for selection after deletion
1269                 nIdx = FirstSelectedRow() - 1;
1270                 if (nIdx >= 0 && SeekCursor(nIdx))
1271                 {
1272                     GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1273 
1274                     bNewPos = true;
1275                     aBookmark = m_pSeekCursor->getBookmark();
1276                 }
1277             }
1278         }
1279 
1280         // Are all rows selected?
1281         // Second condition if no insertion line exists
1282         bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows;
1283 
1284         BeginCursorAction();
1285 
1286         // now delete the row
1287         Sequence<sal_Int32> aDeletedRows;
1288         SetUpdateMode( false );
1289         try
1290         {
1291             aDeletedRows = xDeleteThem->deleteRows(aBookmarks);
1292         }
1293         catch(SQLException&)
1294         {
1295         }
1296         SetUpdateMode( true );
1297 
1298         // how many rows are deleted?
1299         sal_Int32 nDeletedRows = static_cast<sal_Int32>(std::count_if(aDeletedRows.begin(), aDeletedRows.end(),
1300                                                                       [](const sal_Int32 nRow) { return nRow != 0; }));
1301 
1302         // have rows been deleted?
1303         if (nDeletedRows)
1304         {
1305             SetUpdateMode(false);
1306             SetNoSelection();
1307             try
1308             {
1309                 // did we delete all the rows than try to move to the next possible row
1310                 if (nDeletedRows == aDeletedRows.getLength())
1311                 {
1312                     // there exists a new position to move on
1313                     if (bNewPos)
1314                     {
1315                         if (aBookmark.hasValue())
1316                             getDataSource()->moveToBookmark(aBookmark);
1317                         // no valid bookmark so move to the insert row
1318                         else
1319                         {
1320                             Reference< XResultSetUpdate >  xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1321                             xUpdateCursor->moveToInsertRow();
1322                         }
1323                     }
1324                     else
1325                     {
1326                         Reference< css::beans::XPropertySet >  xSet(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1327 
1328                         sal_Int32 nRecordCount(0);
1329                         xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
1330                         if ( m_pDataCursor->rowDeleted() )
1331                             --nRecordCount;
1332 
1333                         // there are no rows left and we have an insert row
1334                         if (!nRecordCount && GetEmptyRow().is())
1335                         {
1336                             Reference< XResultSetUpdate >  xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
1337                             xUpdateCursor->moveToInsertRow();
1338                         }
1339                         else if (nRecordCount)
1340                             // move to the first row
1341                             getDataSource()->first();
1342                     }
1343                 }
1344                 // not all the rows where deleted, so move to the first row which remained in the resultset
1345                 else
1346                 {
1347                     auto pRow = std::find(aDeletedRows.begin(), aDeletedRows.end(), 0);
1348                     if (pRow != aDeletedRows.end())
1349                     {
1350                         auto i = static_cast<sal_Int32>(std::distance(aDeletedRows.begin(), pRow));
1351                         getDataSource()->moveToBookmark(aBookmarks[i]);
1352                     }
1353                 }
1354             }
1355             catch(const Exception&)
1356             {
1357                 try
1358                 {
1359                     // positioning went wrong so try to move to the first row
1360                     getDataSource()->first();
1361                 }
1362                 catch(const Exception&)
1363                 {
1364                 }
1365             }
1366 
1367             // adapt to the data cursor
1368             AdjustDataSource(true);
1369 
1370             // not all rows could be deleted;
1371             // never select again there the ones that could not be deleted
1372             if (nDeletedRows < nSelectedRows)
1373             {
1374                 // were all selected
1375                 if (bAllSelected)
1376                 {
1377                     SelectAll();
1378                     if (IsInsertionRow(GetRowCount() - 1))  // not the insertion row
1379                         SelectRow(GetRowCount() - 1, false);
1380                 }
1381                 else
1382                 {
1383                     // select the remaining rows
1384                     for (const sal_Int32 nSuccess : aDeletedRows)
1385                     {
1386                         try
1387                         {
1388                             if (!nSuccess)
1389                             {
1390                                 m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
1391                                 SetSeekPos(m_pSeekCursor->getRow() - 1);
1392                                 SelectRow(GetSeekPos());
1393                             }
1394                         }
1395                         catch(const Exception&)
1396                         {
1397                             // keep the seekpos in all cases
1398                             SetSeekPos(m_pSeekCursor->getRow() - 1);
1399                         }
1400                     }
1401                 }
1402             }
1403 
1404             EndCursorAction();
1405             SetUpdateMode(true);
1406         }
1407         else // row could not be deleted
1408         {
1409             EndCursorAction();
1410             try
1411             {
1412                 // currentrow is the insert row?
1413                 if (!IsCurrentAppending())
1414                     getDataSource()->refreshRow();
1415             }
1416             catch(const Exception&)
1417             {
1418             }
1419         }
1420     }
1421 
1422     // if there is no selection anymore we can start editing
1423     if (!GetSelectRowCount())
1424         ActivateCell();
1425 }
1426 
1427 // XCurrentRecordListener
positioned()1428 void FmGridControl::positioned()
1429 {
1430     SAL_INFO("svx.fmcomp", "FmGridControl::positioned");
1431     // position on the data source (force it to be done in the main thread)
1432     implAdjustInSolarThread(false);
1433 }
1434 
commit()1435 bool FmGridControl::commit()
1436 {
1437     // execute commit only if an update is not already executed by the
1438     // css::form::component::GridControl
1439     if (!IsUpdating())
1440     {
1441         if (Controller().is() && Controller()->IsValueChangedFromSaved())
1442         {
1443             if (!SaveModified())
1444                 return false;
1445         }
1446     }
1447     return true;
1448 }
1449 
inserted()1450 void FmGridControl::inserted()
1451 {
1452     const DbGridRowRef& xRow = GetCurrentRow();
1453     if (!xRow.is())
1454         return;
1455 
1456     // line has been inserted, then reset the status and mode
1457     xRow->SetState(m_pDataCursor.get(), false);
1458     xRow->SetNew(false);
1459 
1460 }
1461 
imp_CreateHeaderBar(BrowseBox * pParent)1462 VclPtr<BrowserHeader> FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
1463 {
1464     DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" );
1465     return VclPtr<FmGridHeader>::Create( pParent );
1466 }
1467 
markColumn(sal_uInt16 nId)1468 void FmGridControl::markColumn(sal_uInt16 nId)
1469 {
1470     if (!(GetHeaderBar() && m_nMarkedColumnId != nId))
1471         return;
1472 
1473     // deselect
1474     if (m_nMarkedColumnId != BROWSER_INVALIDID)
1475     {
1476         HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HeaderBarItemBits::FLAT;
1477         GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits);
1478     }
1479 
1480 
1481     if (nId != BROWSER_INVALIDID)
1482     {
1483         HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HeaderBarItemBits::FLAT;
1484         GetHeaderBar()->SetItemBits(nId, aBits);
1485     }
1486     m_nMarkedColumnId = nId;
1487 }
1488 
isColumnMarked(sal_uInt16 nId) const1489 bool FmGridControl::isColumnMarked(sal_uInt16 nId) const
1490 {
1491     return m_nMarkedColumnId == nId;
1492 }
1493 
QueryMinimumRowHeight()1494 tools::Long FmGridControl::QueryMinimumRowHeight()
1495 {
1496     tools::Long const nMinimalLogicHeight = 20; // 0.2 cm
1497     tools::Long nMinimalPixelHeight = LogicToPixel(Point(0, nMinimalLogicHeight), MapMode(MapUnit::Map10thMM)).Y();
1498     return CalcZoom( nMinimalPixelHeight );
1499 }
1500 
RowHeightChanged()1501 void FmGridControl::RowHeightChanged()
1502 {
1503     DbGridControl::RowHeightChanged();
1504 
1505     Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY );
1506     DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" );
1507     if ( !xModel.is() )
1508         return;
1509 
1510     try
1511     {
1512         sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() );
1513         Any aProperty = makeAny( static_cast<sal_Int32>(PixelToLogic( Point(0, nUnzoomedPixelHeight), MapMode(MapUnit::Map10thMM)).Y()) );
1514         xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty );
1515     }
1516     catch( const Exception& )
1517     {
1518         TOOLS_WARN_EXCEPTION( "svx", "FmGridControl::RowHeightChanged" );
1519     }
1520 }
1521 
ColumnResized(sal_uInt16 nId)1522 void FmGridControl::ColumnResized(sal_uInt16 nId)
1523 {
1524     DbGridControl::ColumnResized(nId);
1525 
1526     // transfer value to the model
1527     DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1528     const Reference< css::beans::XPropertySet >&  xColModel(pCol->getModel());
1529     if (xColModel.is())
1530     {
1531         Any aWidth;
1532         sal_Int32 nColumnWidth = GetColumnWidth(nId);
1533         nColumnWidth = CalcReverseZoom(nColumnWidth);
1534         // convert to 10THMM
1535         aWidth <<= static_cast<sal_Int32>(PixelToLogic(Point(nColumnWidth, 0), MapMode(MapUnit::Map10thMM)).X());
1536         xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth);
1537     }
1538 }
1539 
CellModified()1540 void FmGridControl::CellModified()
1541 {
1542     DbGridControl::CellModified();
1543     GetPeer()->CellModified();
1544 }
1545 
BeginCursorAction()1546 void FmGridControl::BeginCursorAction()
1547 {
1548     DbGridControl::BeginCursorAction();
1549     m_pPeer->stopCursorListening();
1550 }
1551 
EndCursorAction()1552 void FmGridControl::EndCursorAction()
1553 {
1554     m_pPeer->startCursorListening();
1555     DbGridControl::EndCursorAction();
1556 }
1557 
ColumnMoved(sal_uInt16 nId)1558 void FmGridControl::ColumnMoved(sal_uInt16 nId)
1559 {
1560     m_bInColumnMove = true;
1561 
1562     DbGridControl::ColumnMoved(nId);
1563     Reference< css::container::XIndexContainer >  xColumns(GetPeer()->getColumns());
1564 
1565     if (xColumns.is())
1566     {
1567         // locate the column and move in the model;
1568         // get ColumnPos
1569         DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
1570         Reference< css::beans::XPropertySet >  xCol;
1571 
1572         // inserting must be based on the column positions
1573         sal_Int32 i;
1574         Reference< XInterface > xCurrent;
1575         for (i = 0; !xCol.is() && i < xColumns->getCount(); i++)
1576         {
1577             xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
1578             if (xCurrent == pCol->getModel())
1579             {
1580                 xCol = pCol->getModel();
1581                 break;
1582             }
1583         }
1584 
1585         DBG_ASSERT(i < xColumns->getCount(), "Wrong css::sdbcx::Index");
1586         xColumns->removeByIndex(i);
1587         Any aElement;
1588         aElement <<= xCol;
1589         xColumns->insertByIndex(GetModelColumnPos(nId), aElement);
1590         pCol->setModel(xCol);
1591         // if the column which is shown here is selected ...
1592         if ( isColumnSelected(pCol) )
1593             markColumn(nId); // ... -> mark it
1594     }
1595 
1596     m_bInColumnMove = false;
1597 }
1598 
InitColumnsByModels(const Reference<css::container::XIndexContainer> & xColumns)1599 void FmGridControl::InitColumnsByModels(const Reference< css::container::XIndexContainer >& xColumns)
1600 {
1601     // reset columns;
1602     // if there is only one HandleColumn, then don't
1603     if (GetModelColCount())
1604     {
1605         RemoveColumns();
1606         InsertHandleColumn();
1607     }
1608 
1609     if (!xColumns.is())
1610         return;
1611 
1612     SetUpdateMode(false);
1613 
1614     // inserting must be based on the column positions
1615     sal_Int32 i;
1616     Any aWidth;
1617     for (i = 0; i < xColumns->getCount(); ++i)
1618     {
1619         Reference< css::beans::XPropertySet > xCol(
1620             xColumns->getByIndex(i), css::uno::UNO_QUERY);
1621 
1622         OUString aName(
1623             comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)));
1624 
1625         aWidth = xCol->getPropertyValue(FM_PROP_WIDTH);
1626         sal_Int32 nWidth = 0;
1627         if (aWidth >>= nWidth)
1628             nWidth = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
1629 
1630         AppendColumn(aName, static_cast<sal_uInt16>(nWidth));
1631         DbGridColumn* pCol = DbGridControl::GetColumns()[ i ].get();
1632         pCol->setModel(xCol);
1633     }
1634 
1635     // and now remove the hidden columns as well
1636     // (we did not already make it in the upper loop, since we would then have gotten
1637     // problems with the IDs of the columns: AppendColumn allocates them automatically,
1638     // but the column _after_ a hidden one needs an ID increased by one ...)
1639     Any aHidden;
1640     for (i = 0; i < xColumns->getCount(); ++i)
1641     {
1642         Reference< css::beans::XPropertySet > xCol( xColumns->getByIndex(i), css::uno::UNO_QUERY);
1643         aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN);
1644         if (::comphelper::getBOOL(aHidden))
1645             HideColumn(GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
1646     }
1647 
1648     SetUpdateMode(true);
1649 }
1650 
InitColumnByField(DbGridColumn * _pColumn,const Reference<XPropertySet> & _rxColumnModel,const Reference<XNameAccess> & _rxFieldsByNames,const Reference<XIndexAccess> & _rxFieldsByIndex)1651 void FmGridControl::InitColumnByField(
1652     DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel,
1653     const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex )
1654 {
1655     DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" );
1656 
1657     // lookup the column which belongs to the control source
1658     OUString sFieldName;
1659     _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName;
1660     Reference< XPropertySet > xField;
1661     _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
1662 
1663 
1664     if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length
1665         _rxFieldsByNames->getByName( sFieldName ) >>= xField;
1666 
1667     // determine the position of this column
1668     sal_Int32 nFieldPos = -1;
1669     if ( xField.is() )
1670     {
1671         Reference< XPropertySet > xCheck;
1672         sal_Int32 nFieldCount = _rxFieldsByIndex->getCount();
1673         for ( sal_Int32 i = 0; i < nFieldCount; ++i)
1674         {
1675             _rxFieldsByIndex->getByIndex( i ) >>= xCheck;
1676             if ( xField.get() == xCheck.get() )
1677             {
1678                 nFieldPos = i;
1679                 break;
1680             }
1681         }
1682     }
1683 
1684     if ( xField.is() && ( nFieldPos >= 0 ) )
1685     {
1686         // some data types are not allowed
1687         sal_Int32 nDataType = DataType::OTHER;
1688         xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType;
1689 
1690         bool bIllegalType = false;
1691         switch ( nDataType )
1692         {
1693             case DataType::BLOB:
1694             case DataType::LONGVARBINARY:
1695             case DataType::BINARY:
1696             case DataType::VARBINARY:
1697             case DataType::OTHER:
1698                 bIllegalType = true;
1699                 break;
1700         }
1701 
1702         if ( bIllegalType )
1703         {
1704             _pColumn->SetObject( static_cast<sal_Int16>(nFieldPos) );
1705             return;
1706         }
1707     }
1708 
1709     // the control type is determined by the ColumnServiceName
1710     static constexpr OUStringLiteral s_sPropColumnServiceName = u"ColumnServiceName";
1711     if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) )
1712         return;
1713 
1714     _pColumn->setModel( _rxColumnModel );
1715 
1716     OUString sColumnServiceName;
1717     _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName;
1718 
1719     sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName );
1720     _pColumn->CreateControl( nFieldPos, xField, nTypeId );
1721 }
1722 
InitColumnsByFields(const Reference<css::container::XIndexAccess> & _rxFields)1723 void FmGridControl::InitColumnsByFields(const Reference< css::container::XIndexAccess >& _rxFields)
1724 {
1725     if ( !_rxFields.is() )
1726         return;
1727 
1728     // initialize columns
1729     Reference< XIndexContainer > xColumns( GetPeer()->getColumns() );
1730     Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY );
1731 
1732     // inserting must be based on the column positions
1733     for (sal_Int32 i = 0; i < xColumns->getCount(); i++)
1734     {
1735         DbGridColumn* pCol = GetColumns()[ i ].get();
1736         OSL_ENSURE(pCol,"No grid column!");
1737         if ( pCol )
1738         {
1739             Reference< XPropertySet > xColumnModel(
1740                 xColumns->getByIndex( i ), css::uno::UNO_QUERY);
1741 
1742             InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields );
1743         }
1744     }
1745 }
1746 
HideColumn(sal_uInt16 nId)1747 void FmGridControl::HideColumn(sal_uInt16 nId)
1748 {
1749     DbGridControl::HideColumn(nId);
1750 
1751     sal_uInt16 nPos = GetModelColumnPos(nId);
1752     if (nPos == sal_uInt16(-1))
1753         return;
1754 
1755     DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1756     if (pColumn->IsHidden())
1757         GetPeer()->columnHidden(pColumn);
1758 
1759     if (nId == m_nMarkedColumnId)
1760         m_nMarkedColumnId = sal_uInt16(-1);
1761 }
1762 
isColumnSelected(DbGridColumn const * _pColumn)1763 bool FmGridControl::isColumnSelected(DbGridColumn const * _pColumn)
1764 {
1765     OSL_ENSURE(_pColumn,"Column can not be null!");
1766     bool bSelected = false;
1767     // if the column which is shown here is selected ...
1768     Reference< css::view::XSelectionSupplier >  xSelSupplier(GetPeer()->getColumns(), UNO_QUERY);
1769     if ( xSelSupplier.is() )
1770     {
1771         Reference< css::beans::XPropertySet >  xColumn;
1772         xSelSupplier->getSelection() >>= xColumn;
1773         bSelected = (xColumn.get() == _pColumn->getModel().get());
1774     }
1775     return bSelected;
1776 }
1777 
ShowColumn(sal_uInt16 nId)1778 void FmGridControl::ShowColumn(sal_uInt16 nId)
1779 {
1780     DbGridControl::ShowColumn(nId);
1781 
1782     sal_uInt16 nPos = GetModelColumnPos(nId);
1783     if (nPos == sal_uInt16(-1))
1784         return;
1785 
1786     DbGridColumn* pColumn = GetColumns()[ nPos ].get();
1787     if (!pColumn->IsHidden())
1788         GetPeer()->columnVisible(pColumn);
1789 
1790     // if the column which is shown here is selected ...
1791     if ( isColumnSelected(pColumn) )
1792         markColumn(nId); // ... -> mark it
1793 }
1794 
selectBookmarks(const Sequence<Any> & _rBookmarks)1795 bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks)
1796 {
1797     SolarMutexGuard aGuard;
1798         // need to lock the SolarMutex so that no paint call disturbs us ...
1799 
1800     if ( !m_pSeekCursor )
1801     {
1802         OSL_FAIL( "FmGridControl::selectBookmarks: no seek cursor!" );
1803         return false;
1804     }
1805 
1806     SetNoSelection();
1807 
1808     bool bAllSuccessfull = true;
1809     try
1810     {
1811         for (const Any& rBookmark : _rBookmarks)
1812         {
1813             // move the seek cursor to the row given
1814             if (m_pSeekCursor->moveToBookmark(rBookmark))
1815                 SelectRow( m_pSeekCursor->getRow() - 1);
1816             else
1817                 bAllSuccessfull = false;
1818         }
1819     }
1820     catch(Exception&)
1821     {
1822         OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
1823         return false;
1824     }
1825 
1826     return bAllSuccessfull;
1827 }
1828 
getSelectionBookmarks()1829 Sequence< Any> FmGridControl::getSelectionBookmarks()
1830 {
1831     // lock our update so no paint-triggered seeks interfere ...
1832     SetUpdateMode(false);
1833 
1834     sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0;
1835     Sequence< Any> aBookmarks(nSelectedRows);
1836     if ( nSelectedRows )
1837     {
1838         Any* pBookmarks = aBookmarks.getArray();
1839 
1840         // (I'm not sure if the problem isn't deeper: The scenario: a large table displayed by a grid with a
1841         // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress
1842         // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which made a
1843         // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition.
1844         // Unfortunately the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the
1845         // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning
1846         // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails.
1847         // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relies_ on
1848         // the first one, should be secured against recursion, with a broad-minded interpretation of "recursion": if any of these
1849         // code parts is executed, no other should be accessible. But this sounds very difficult to achieve...
1850         // )
1851 
1852         // The next problem caused by the same behavior (SeekCursor causes a propertyChanged): when adjusting rows we implicitly
1853         // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results.
1854         // That's why we _first_ collect the indices of the selected rows and _then_ their bookmarks.
1855         tools::Long nIdx = FirstSelectedRow();
1856         while (nIdx != BROWSER_ENDOFSELECTION)
1857         {
1858             // (we misuse the bookmarks array for this ...)
1859             pBookmarks[i++] <<= static_cast<sal_Int32>(nIdx);
1860             nIdx = NextSelectedRow();
1861         }
1862         DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indices !");
1863 
1864         for (i=0; i<nSelectedRows; ++i)
1865         {
1866             nIdx = ::comphelper::getINT32(pBookmarks[i]);
1867             if (IsInsertionRow(nIdx))
1868             {
1869                 // do not delete empty row
1870                 aBookmarks.realloc(--nSelectedRows);
1871                 SelectRow(nIdx, false);          // cancel selection for empty row
1872                 break;
1873             }
1874 
1875             // first, position the data cursor on the selected block
1876             if (SeekCursor(nIdx))
1877             {
1878                 GetSeekRow()->SetState(m_pSeekCursor.get(), true);
1879 
1880                 pBookmarks[i] = m_pSeekCursor->getBookmark();
1881             }
1882     #ifdef DBG_UTIL
1883             else
1884                 OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
1885     #endif
1886         }
1887     }
1888     SetUpdateMode(true);
1889 
1890     // if one of the SeekCursor-calls failed...
1891     aBookmarks.realloc(i);
1892 
1893     // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems.
1894     // but this would be incompatible as we need a locking flag, then...)
1895 
1896     return aBookmarks;
1897 }
1898 
1899 namespace
1900 {
getColumnPropertyFromPeer(FmXGridPeer * _pPeer,sal_Int32 _nPosition,const OUString & _sPropName)1901     OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const OUString& _sPropName)
1902     {
1903         OUString sRetText;
1904         if ( _pPeer && _nPosition != -1)
1905         {
1906             Reference<XIndexContainer> xIndex = _pPeer->getColumns();
1907             if ( xIndex.is() && xIndex->getCount() > _nPosition )
1908             {
1909                 Reference<XPropertySet> xProp;
1910                 xIndex->getByIndex( _nPosition ) >>= xProp;
1911                 if ( xProp.is() )
1912                 {
1913                     try {
1914                         xProp->getPropertyValue( _sPropName ) >>= sRetText;
1915                     } catch (UnknownPropertyException const&) {
1916                         TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
1917                     }
1918                 }
1919             }
1920         }
1921         return sRetText;
1922     }
1923 }
1924 
1925 // Object data and state
GetAccessibleObjectName(::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition) const1926 OUString FmGridControl::GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1927 {
1928     OUString sRetText;
1929     switch( _eObjType )
1930     {
1931         case ::vcl::BBTYPE_BROWSEBOX:
1932             if ( GetPeer() )
1933             {
1934                 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1935                 if ( xProp.is() )
1936                     xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText;
1937             }
1938             break;
1939         case ::vcl::BBTYPE_COLUMNHEADERCELL:
1940             sRetText = getColumnPropertyFromPeer(
1941                 GetPeer(),
1942                 GetModelColumnPos(
1943                     sal::static_int_cast< sal_uInt16 >(_nPosition)),
1944                 FM_PROP_LABEL);
1945             break;
1946         default:
1947             sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition);
1948     }
1949     return sRetText;
1950 }
1951 
GetAccessibleObjectDescription(::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition) const1952 OUString FmGridControl::GetAccessibleObjectDescription( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
1953 {
1954     OUString sRetText;
1955     switch( _eObjType )
1956     {
1957         case ::vcl::BBTYPE_BROWSEBOX:
1958             if ( GetPeer() )
1959             {
1960                 Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
1961                 if ( xProp.is() )
1962                 {
1963                     xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText;
1964                     if ( sRetText.isEmpty() )
1965                         xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText;
1966                 }
1967             }
1968             break;
1969         case ::vcl::BBTYPE_COLUMNHEADERCELL:
1970             sRetText = getColumnPropertyFromPeer(
1971                 GetPeer(),
1972                 GetModelColumnPos(
1973                     sal::static_int_cast< sal_uInt16 >(_nPosition)),
1974                 FM_PROP_HELPTEXT);
1975             if ( sRetText.isEmpty() )
1976                 sRetText = getColumnPropertyFromPeer(
1977                             GetPeer(),
1978                             GetModelColumnPos(
1979                                 sal::static_int_cast< sal_uInt16 >(_nPosition)),
1980                             FM_PROP_DESCRIPTION);
1981 
1982             break;
1983         default:
1984             sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition);
1985     }
1986     return sRetText;
1987 }
1988 
Select()1989 void FmGridControl::Select()
1990 {
1991     DbGridControl::Select();
1992     // ... does it affect our columns?
1993     const MultiSelection* pColumnSelection = GetColumnSelection();
1994 
1995     sal_uInt16 nSelectedColumn =
1996         pColumnSelection && pColumnSelection->GetSelectCount()
1997             ? sal::static_int_cast< sal_uInt16 >(
1998                 const_cast<MultiSelection*>(pColumnSelection)->FirstSelected())
1999             : SAL_MAX_UINT16;
2000     // the HandleColumn is not selected
2001     switch (nSelectedColumn)
2002     {
2003         case SAL_MAX_UINT16: break; // no selection
2004         case  0 : nSelectedColumn = SAL_MAX_UINT16; break;
2005                     // handle col can't be selected
2006         default :
2007             // get the model col pos instead of the view col pos
2008             nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1));
2009             break;
2010     }
2011 
2012     if (nSelectedColumn == m_nCurrentSelectedColumn)
2013         return;
2014 
2015     // BEFORE calling the select at the SelectionSupplier!
2016     m_nCurrentSelectedColumn = nSelectedColumn;
2017 
2018     if (m_bSelecting)
2019         return;
2020 
2021     m_bSelecting = true;
2022 
2023     try
2024     {
2025         Reference< XIndexAccess >  xColumns = GetPeer()->getColumns();
2026         Reference< XSelectionSupplier >  xSelSupplier(xColumns, UNO_QUERY);
2027         if (xSelSupplier.is())
2028         {
2029             if (nSelectedColumn != SAL_MAX_UINT16)
2030             {
2031                 Reference< XPropertySet >  xColumn(
2032                     xColumns->getByIndex(nSelectedColumn),
2033                     css::uno::UNO_QUERY);
2034                 xSelSupplier->select(makeAny(xColumn));
2035             }
2036             else
2037             {
2038                 xSelSupplier->select(Any());
2039             }
2040         }
2041     }
2042     catch(Exception&)
2043     {
2044     }
2045 
2046 
2047     m_bSelecting = false;
2048 }
2049 
2050 
KeyInput(const KeyEvent & rKEvt)2051 void FmGridControl::KeyInput( const KeyEvent& rKEvt )
2052 {
2053     bool bDone = false;
2054     const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
2055     if (    IsDesignMode()
2056         &&  !rKeyCode.IsShift()
2057         &&  !rKeyCode.IsMod1()
2058         &&  !rKeyCode.IsMod2()
2059         &&  GetParent() )
2060     {
2061         switch ( rKeyCode.GetCode() )
2062         {
2063             case KEY_ESCAPE:
2064                 GetParent()->GrabFocus();
2065                 bDone = true;
2066                 break;
2067             case KEY_DELETE:
2068                 if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 )
2069                 {
2070                     Reference< css::container::XIndexContainer >  xCols(GetPeer()->getColumns());
2071                     if ( xCols.is() )
2072                     {
2073                         try
2074                         {
2075                             if ( m_nCurrentSelectedColumn < xCols->getCount() )
2076                             {
2077                                 Reference< XInterface >  xCol;
2078                                 xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol;
2079                                 xCols->removeByIndex(m_nCurrentSelectedColumn);
2080                                 ::comphelper::disposeComponent(xCol);
2081                             }
2082                         }
2083                         catch(const Exception&)
2084                         {
2085                             TOOLS_WARN_EXCEPTION("svx", "exception occurred while deleting a column");
2086                         }
2087                     }
2088                 }
2089                 bDone = true;
2090                 break;
2091         }
2092     }
2093     if ( !bDone )
2094         DbGridControl::KeyInput( rKEvt );
2095 }
2096 
2097 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2098