Avoid potential negative array index access to cached text.
[LibreOffice.git] / extensions / source / dbpilots / controlwizard.cxx
blob17ec948ed0176b6795225e7421515ea12a657215
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "controlwizard.hxx"
21 #include <tools/debug.hxx>
22 #include <comphelper/diagnose_ex.hxx>
23 #include <com/sun/star/container/XNameAccess.hpp>
24 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
25 #include <com/sun/star/sdb/DatabaseContext.hpp>
26 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
27 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
28 #include <com/sun/star/container/XChild.hpp>
29 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
30 #include <com/sun/star/frame/XModel.hpp>
31 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
32 #include <com/sun/star/drawing/XDrawView.hpp>
33 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
34 #include <com/sun/star/sdb/CommandType.hpp>
35 #include <com/sun/star/sdbc/SQLWarning.hpp>
36 #include <com/sun/star/sdb/SQLContext.hpp>
37 #include <com/sun/star/task/InteractionHandler.hpp>
38 #include <comphelper/types.hxx>
39 #include <connectivity/dbtools.hxx>
40 #include <comphelper/interaction.hxx>
41 #include <vcl/stdtext.hxx>
42 #include <connectivity/conncleanup.hxx>
43 #include <com/sun/star/sdbc/DataType.hpp>
44 #include <tools/urlobj.hxx>
46 #define WIZARD_SIZE_X 60
47 #define WIZARD_SIZE_Y 23
49 namespace dbp
51 using namespace ::com::sun::star::uno;
52 using namespace ::com::sun::star::awt;
53 using namespace ::com::sun::star::lang;
54 using namespace ::com::sun::star::sdb;
55 using namespace ::com::sun::star::sdbc;
56 using namespace ::com::sun::star::sdbcx;
57 using namespace ::com::sun::star::beans;
58 using namespace ::com::sun::star::container;
59 using namespace ::com::sun::star::drawing;
60 using namespace ::com::sun::star::frame;
61 using namespace ::com::sun::star::sheet;
62 using namespace ::com::sun::star::form;
63 using namespace ::com::sun::star::task;
64 using namespace ::comphelper;
65 using namespace ::dbtools;
67 struct OAccessRegulator
69 friend class OControlWizardPage;
71 protected:
72 OAccessRegulator() { }
75 OControlWizardPage::OControlWizardPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OUString& rID)
76 : OControlWizardPage_Base(pPage, pWizard, rUIXMLDescription, rID)
77 , m_pDialog(pWizard)
79 m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * WIZARD_SIZE_X,
80 m_xContainer->get_text_height() * WIZARD_SIZE_Y);
83 OControlWizardPage::~OControlWizardPage()
87 OControlWizard* OControlWizardPage::getDialog()
89 return m_pDialog;
92 const OControlWizard* OControlWizardPage::getDialog() const
94 return m_pDialog;
97 bool OControlWizardPage::updateContext()
99 return m_pDialog->updateContext(OAccessRegulator());
102 Reference< XConnection > OControlWizardPage::getFormConnection() const
104 return m_pDialog->getFormConnection(OAccessRegulator());
107 void OControlWizardPage::setFormConnection( const Reference< XConnection >& _rxConn, bool _bAutoDispose )
109 m_pDialog->setFormConnection( OAccessRegulator(), _rxConn, _bAutoDispose );
112 const OControlWizardContext& OControlWizardPage::getContext() const
114 return m_pDialog->getContext();
117 void OControlWizardPage::fillListBox(weld::TreeView& _rList, const Sequence< OUString >& _rItems)
119 _rList.clear();
120 const OUString* pItems = _rItems.getConstArray();
121 const OUString* pEnd = pItems + _rItems.getLength();
122 sal_Int32 nIndex = 0;
123 for (;pItems < pEnd; ++pItems, ++nIndex)
125 _rList.append(OUString::number(nIndex), *pItems);
129 void OControlWizardPage::fillListBox(weld::ComboBox& _rList, const Sequence< OUString >& _rItems)
131 _rList.clear();
132 const OUString* pItems = _rItems.getConstArray();
133 const OUString* pEnd = pItems + _rItems.getLength();
134 for (;pItems < pEnd; ++pItems)
136 _rList.append_text(*pItems);
140 void OControlWizardPage::enableFormDatasourceDisplay()
142 if (m_xFormContentType)
143 // nothing to do
144 return;
146 m_xFrame = m_xBuilder->weld_frame("sourceframe");
147 m_xFrame->show();
148 m_xFormContentType = m_xBuilder->weld_label("contenttype");
149 m_xFormContentTypeLabel = m_xBuilder->weld_label("contenttypelabel");
150 m_xFormDatasource = m_xBuilder->weld_label("datasource");
151 m_xFormDatasourceLabel = m_xBuilder->weld_label("datasourcelabel");
152 m_xFormTable = m_xBuilder->weld_label("formtable");
154 const OControlWizardContext& rContext = getContext();
155 if ( rContext.bEmbedded )
157 m_xFormDatasourceLabel->hide();
158 m_xFormDatasource->hide();
162 void OControlWizardPage::initializePage()
164 if (m_xFormDatasource && m_xFormContentTypeLabel && m_xFormTable)
166 const OControlWizardContext& rContext = getContext();
167 OUString sDataSource;
168 OUString sCommand;
169 sal_Int32 nCommandType = CommandType::COMMAND;
172 rContext.xForm->getPropertyValue("DataSourceName") >>= sDataSource;
173 rContext.xForm->getPropertyValue("Command") >>= sCommand;
174 rContext.xForm->getPropertyValue("CommandType") >>= nCommandType;
176 catch(const Exception&)
178 TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizardPage::initializePage");
181 INetURLObject aURL( sDataSource );
182 if( aURL.GetProtocol() != INetProtocol::NotValid )
183 sDataSource = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
184 m_xFormDatasource->set_label(sDataSource);
185 m_xFormTable->set_label(sCommand);
187 TranslateId pCommandTypeResourceId;
188 switch (nCommandType)
190 case CommandType::TABLE:
191 pCommandTypeResourceId = RID_STR_TYPE_TABLE;
192 break;
194 case CommandType::QUERY:
195 pCommandTypeResourceId = RID_STR_TYPE_QUERY;
196 break;
198 default:
199 pCommandTypeResourceId = RID_STR_TYPE_COMMAND;
200 break;
202 m_xFormContentType->set_label(compmodule::ModuleRes(pCommandTypeResourceId));
205 OControlWizardPage_Base::initializePage();
208 OControlWizard::OControlWizard(weld::Window* _pParent,
209 const Reference< XPropertySet >& _rxObjectModel, const Reference< XComponentContext >& _rxContext )
210 : WizardMachine(_pParent, WizardButtonFlags::CANCEL | WizardButtonFlags::PREVIOUS | WizardButtonFlags::NEXT | WizardButtonFlags::FINISH)
211 , m_xContext(_rxContext)
213 m_aContext.xObjectModel = _rxObjectModel;
214 initContext();
216 defaultButton(WizardButtonFlags::NEXT);
217 enableButtons(WizardButtonFlags::FINISH, false);
220 OControlWizard::~OControlWizard()
224 short OControlWizard::run()
226 // get the class id of the control we're dealing with
227 sal_Int16 nClassId = FormComponentType::CONTROL;
230 getContext().xObjectModel->getPropertyValue("ClassId") >>= nClassId;
232 catch(const Exception&)
234 OSL_FAIL("OControlWizard::activate: could not obtain the class id!");
236 if (!approveControl(nClassId))
238 // TODO: MessageBox or exception
239 return RET_CANCEL;
242 ActivatePage();
244 m_xAssistant->set_current_page(0);
246 return OControlWizard_Base::run();
249 void OControlWizard::implDetermineShape()
251 Reference< XIndexAccess > xPageObjects = m_aContext.xDrawPage;
252 DBG_ASSERT(xPageObjects.is(), "OControlWizard::implDetermineShape: invalid page!");
254 // for comparing the model
255 Reference< XControlModel > xModelCompare(m_aContext.xObjectModel, UNO_QUERY);
257 if (!xPageObjects.is())
258 return;
260 // loop through all objects of the page
261 sal_Int32 nObjects = xPageObjects->getCount();
262 Reference< XControlShape > xControlShape;
263 Reference< XControlModel > xControlModel;
264 for (sal_Int32 i=0; i<nObjects; ++i)
266 if (xPageObjects->getByIndex(i) >>= xControlShape)
267 { // it _is_ a control shape
268 xControlModel = xControlShape->getControl();
269 DBG_ASSERT(xControlModel.is(), "OControlWizard::implDetermineShape: control shape without model!");
270 if (xModelCompare.get() == xControlModel.get())
272 m_aContext.xObjectShape = xControlShape;
273 break;
280 void OControlWizard::implDetermineForm()
282 Reference< XChild > xModelAsChild(m_aContext.xObjectModel, UNO_QUERY);
283 Reference< XInterface > xControlParent;
284 if (xModelAsChild.is())
285 xControlParent = xModelAsChild->getParent();
287 m_aContext.xForm.set(xControlParent, UNO_QUERY);
288 m_aContext.xRowSet.set(xControlParent, UNO_QUERY);
289 DBG_ASSERT(m_aContext.xForm.is() && m_aContext.xRowSet.is(),
290 "OControlWizard::implDetermineForm: missing some interfaces of the control parent!");
295 void OControlWizard::implDeterminePage()
299 // get the document model
300 Reference< XChild > xControlAsChild(m_aContext.xObjectModel, UNO_QUERY);
301 Reference< XChild > xModelSearch(xControlAsChild->getParent(), UNO_QUERY);
303 Reference< XModel > xModel(xModelSearch, UNO_QUERY);
304 while (xModelSearch.is() && !xModel.is())
306 xModelSearch.set(xModelSearch->getParent(), UNO_QUERY);
307 xModel.set(xModelSearch, UNO_QUERY);
310 Reference< XDrawPage > xPage;
311 if (xModel.is())
313 m_aContext.xDocumentModel = xModel;
315 Reference< XDrawPageSupplier > xPageSupp(xModel, UNO_QUERY);
316 if (xPageSupp.is())
317 { // it's a document with only one page -> Writer
318 xPage = xPageSupp->getDrawPage();
320 else
322 // get the controller currently working on this model
323 Reference< XController > xController = xModel->getCurrentController();
324 DBG_ASSERT(xController.is(), "OControlWizard::implDeterminePage: no current controller!");
326 // maybe it's a spreadsheet
327 Reference< XSpreadsheetView > xView(xController, UNO_QUERY);
328 if (xView.is())
329 { // okay, it is one
330 Reference< XSpreadsheet > xSheet = xView->getActiveSheet();
331 xPageSupp.set(xSheet, UNO_QUERY);
332 DBG_ASSERT(xPageSupp.is(), "OControlWizard::implDeterminePage: a spreadsheet which is no page supplier!");
333 if (xPageSupp.is())
334 xPage = xPageSupp->getDrawPage();
336 else
337 { // can be a draw/impress doc only
338 Reference< XDrawView > xDrawView(xController, UNO_QUERY);
339 DBG_ASSERT(xDrawView.is(), "OControlWizard::implDeterminePage: no alternatives left ... can't determine the page!");
340 if (xDrawView.is())
341 xPage = xDrawView->getCurrentPage();
345 else
347 DBG_ASSERT(xPage.is(), "OControlWizard::implDeterminePage: can't determine the page (no model)!");
349 m_aContext.xDrawPage = xPage;
351 catch(const Exception&)
353 TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizard::implDeterminePage");
358 void OControlWizard::implGetDSContext()
362 DBG_ASSERT(m_xContext.is(), "OControlWizard::implGetDSContext: invalid service factory!");
364 m_aContext.xDatasourceContext = DatabaseContext::create(m_xContext);
366 catch(const Exception&)
368 OSL_FAIL("OControlWizard::implGetDSContext: invalid database context!");
373 Reference< XConnection > OControlWizard::getFormConnection(const OAccessRegulator&) const
375 return getFormConnection();
378 Reference< XConnection > OControlWizard::getFormConnection() const
380 Reference< XConnection > xConn;
383 if ( !::dbtools::isEmbeddedInDatabase(m_aContext.xForm,xConn) )
384 m_aContext.xForm->getPropertyValue("ActiveConnection") >>= xConn;
386 catch(const Exception&)
388 TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizard::getFormConnection");
390 return xConn;
394 void OControlWizard::setFormConnection( const OAccessRegulator& _rAccess, const Reference< XConnection >& _rxConn, bool _bAutoDispose )
398 Reference< XConnection > xOldConn = getFormConnection(_rAccess);
399 if (xOldConn.get() == _rxConn.get())
400 return;
402 disposeComponent(xOldConn);
404 // set the new connection
405 if ( _bAutoDispose )
407 // for this, use an AutoDisposer (so the conn is cleaned up when the form dies or gets another connection)
408 Reference< XRowSet > xFormRowSet( m_aContext.xForm, UNO_QUERY );
409 new OAutoConnectionDisposer( xFormRowSet, _rxConn );
411 else
413 m_aContext.xForm->setPropertyValue("ActiveConnection", Any( _rxConn ) );
416 catch(const Exception&)
418 TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::setFormConnection");
423 bool OControlWizard::updateContext(const OAccessRegulator&)
425 return initContext();
428 Reference< XInteractionHandler > OControlWizard::getInteractionHandler(weld::Window* _pWindow) const
430 Reference< XInteractionHandler > xHandler;
433 xHandler.set( InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW );
435 catch(const Exception&) { }
436 if (!xHandler.is())
438 ShowServiceNotAvailableError(_pWindow, u"com.sun.star.task.InteractionHandler", true);
440 return xHandler;
443 bool OControlWizard::initContext()
445 DBG_ASSERT(m_aContext.xObjectModel.is(), "OGroupBoxWizard::initContext: have no control model to work with!");
446 if (!m_aContext.xObjectModel.is())
447 return false;
449 // reset the context
450 m_aContext.xForm.clear();
451 m_aContext.xRowSet.clear();
452 m_aContext.xDocumentModel.clear();
453 m_aContext.xDrawPage.clear();
454 m_aContext.xObjectShape.clear();
455 m_aContext.aFieldNames.realloc(0);
457 m_aContext.xObjectContainer.clear();
458 m_aContext.aTypes.clear();
459 m_aContext.bEmbedded = false;
461 Any aSQLException;
462 Reference< XPreparedStatement > xStatement;
465 // get the datasource context
466 implGetDSContext();
468 // first, determine the form the control belongs to
469 implDetermineForm();
471 // need the page, too
472 implDeterminePage();
474 // the shape of the control
475 implDetermineShape();
477 // get the columns of the object the settings refer to
478 Reference< XNameAccess > xColumns;
480 if (m_aContext.xForm.is())
482 // collect some properties of the form
483 OUString sObjectName = ::comphelper::getString(m_aContext.xForm->getPropertyValue("Command"));
484 sal_Int32 nObjectType = ::comphelper::getINT32(m_aContext.xForm->getPropertyValue("CommandType"));
486 // calculate the connection the rowset is working with
487 Reference< XConnection > xConnection;
488 m_aContext.bEmbedded = ::dbtools::isEmbeddedInDatabase( m_aContext.xForm, xConnection );
489 if ( !m_aContext.bEmbedded )
490 xConnection = ::dbtools::connectRowset( m_aContext.xRowSet, m_xContext, nullptr );
492 // get the fields
493 if (xConnection.is())
495 switch (nObjectType)
497 case 0:
499 Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
500 if (xSupplyTables.is() && xSupplyTables->getTables().is() && xSupplyTables->getTables()->hasByName(sObjectName))
502 Reference< XColumnsSupplier > xSupplyColumns;
503 m_aContext.xObjectContainer = xSupplyTables->getTables();
504 m_aContext.xObjectContainer->getByName(sObjectName) >>= xSupplyColumns;
505 DBG_ASSERT(xSupplyColumns.is(), "OControlWizard::initContext: invalid table columns!");
506 xColumns = xSupplyColumns->getColumns();
509 break;
510 case 1:
512 Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
513 if (xSupplyQueries.is() && xSupplyQueries->getQueries().is() && xSupplyQueries->getQueries()->hasByName(sObjectName))
515 Reference< XColumnsSupplier > xSupplyColumns;
516 m_aContext.xObjectContainer = xSupplyQueries->getQueries();
517 m_aContext.xObjectContainer->getByName(sObjectName) >>= xSupplyColumns;
518 DBG_ASSERT(xSupplyColumns.is(), "OControlWizard::initContext: invalid query columns!");
519 xColumns = xSupplyColumns->getColumns();
522 break;
523 default:
525 xStatement = xConnection->prepareStatement(sObjectName);
527 // not interested in any results, only in the fields
528 Reference< XPropertySet > xStatementProps(xStatement, UNO_QUERY);
529 xStatementProps->setPropertyValue("MaxRows", Any(sal_Int32(0)));
531 // TODO: think about handling local SQLExceptions here ...
532 Reference< XColumnsSupplier > xSupplyCols(xStatement->executeQuery(), UNO_QUERY);
533 if (xSupplyCols.is())
534 xColumns = xSupplyCols->getColumns();
540 if (xColumns.is())
542 m_aContext.aFieldNames = xColumns->getElementNames();
543 const OUString* pBegin = m_aContext.aFieldNames.getConstArray();
544 const OUString* pEnd = pBegin + m_aContext.aFieldNames.getLength();
545 for(;pBegin != pEnd;++pBegin)
547 sal_Int32 nFieldType = DataType::OTHER;
550 Reference< XPropertySet > xColumn;
551 xColumns->getByName(*pBegin) >>= xColumn;
552 xColumn->getPropertyValue("Type") >>= nFieldType;
554 catch(const Exception&)
556 TOOLS_WARN_EXCEPTION(
557 "extensions.dbpilots",
558 "unexpected exception while gathering column information!");
560 m_aContext.aTypes.emplace(*pBegin,nFieldType);
564 catch(const SQLContext& e) { aSQLException <<= e; }
565 catch(const SQLWarning& e) { aSQLException <<= e; }
566 catch(const SQLException& e) { aSQLException <<= e; }
567 catch(const Exception&)
569 TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::initContext: could not retrieve the control context");
572 ::comphelper::disposeComponent(xStatement);
574 if (aSQLException.hasValue())
575 { // an SQLException (or derivee) was thrown ...
577 // prepend an extra SQLContext explaining what we were doing
578 SQLContext aContext(compmodule::ModuleRes(RID_STR_COULDNOTOPENTABLE), {}, {}, 0,
579 aSQLException, {});
581 // create an interaction handler to display this exception
582 Reference< XInteractionHandler > xHandler = getInteractionHandler(m_xAssistant.get());
583 if ( !xHandler.is() )
584 return false;
586 Reference< XInteractionRequest > xRequest = new OInteractionRequest(Any(aContext));
589 xHandler->handle(xRequest);
591 catch(const Exception&) { }
592 return false;
595 return m_aContext.aFieldNames.hasElements();
599 void OControlWizard::commitControlSettings(OControlWizardSettings const * _pSettings)
601 DBG_ASSERT(m_aContext.xObjectModel.is(), "OControlWizard::commitControlSettings: have no control model to work with!");
602 if (!m_aContext.xObjectModel.is())
603 return;
605 // the only thing we have at the moment is the label
608 Reference< XPropertySetInfo > xInfo = m_aContext.xObjectModel->getPropertySetInfo();
609 if (xInfo.is() && xInfo->hasPropertyByName("Label"))
611 OUString sControlLabel(_pSettings->sControlLabel);
612 m_aContext.xObjectModel->setPropertyValue(
613 "Label",
614 Any(sControlLabel)
618 catch(const Exception&)
620 TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::commitControlSettings: could not commit the basic control settings!");
625 void OControlWizard::initControlSettings(OControlWizardSettings* _pSettings)
627 DBG_ASSERT(m_aContext.xObjectModel.is(), "OControlWizard::initControlSettings: have no control model to work with!");
628 if (!m_aContext.xObjectModel.is())
629 return;
631 // initialize some settings from the control model give
634 OUString sLabelPropertyName("Label");
635 Reference< XPropertySetInfo > xInfo = m_aContext.xObjectModel->getPropertySetInfo();
636 if (xInfo.is() && xInfo->hasPropertyByName(sLabelPropertyName))
638 OUString sControlLabel;
639 m_aContext.xObjectModel->getPropertyValue(sLabelPropertyName) >>= sControlLabel;
640 _pSettings->sControlLabel = sControlLabel;
643 catch(const Exception&)
645 TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::initControlSettings: could not retrieve the basic control settings!");
650 bool OControlWizard::needDatasourceSelection()
652 // lemme see ...
653 return !getContext().aFieldNames.hasElements();
654 // if we got fields, the data source is valid ...
658 } // namespace dbp
661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */