/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "controlwizard.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WIZARD_SIZE_X 60 #define WIZARD_SIZE_Y 23 namespace dbp { using namespace ::com::sun::star::uno; using namespace ::com::sun::star::awt; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::sdb; using namespace ::com::sun::star::sdbc; using namespace ::com::sun::star::sdbcx; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::container; using namespace ::com::sun::star::drawing; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::sheet; using namespace ::com::sun::star::form; using namespace ::com::sun::star::task; using namespace ::comphelper; using namespace ::dbtools; struct OAccessRegulator { friend class OControlWizardPage; protected: OAccessRegulator() { } }; OControlWizardPage::OControlWizardPage(weld::Container* pPage, OControlWizard* pWizard, const OUString& rUIXMLDescription, const OUString& rID) : OControlWizardPage_Base(pPage, pWizard, rUIXMLDescription, rID) , m_pDialog(pWizard) { m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * WIZARD_SIZE_X, m_xContainer->get_text_height() * WIZARD_SIZE_Y); } OControlWizardPage::~OControlWizardPage() { } OControlWizard* OControlWizardPage::getDialog() { return m_pDialog; } const OControlWizard* OControlWizardPage::getDialog() const { return m_pDialog; } bool OControlWizardPage::updateContext() { return m_pDialog->updateContext(OAccessRegulator()); } Reference< XConnection > OControlWizardPage::getFormConnection() const { return m_pDialog->getFormConnection(OAccessRegulator()); } void OControlWizardPage::setFormConnection( const Reference< XConnection >& _rxConn, bool _bAutoDispose ) { m_pDialog->setFormConnection( OAccessRegulator(), _rxConn, _bAutoDispose ); } const OControlWizardContext& OControlWizardPage::getContext() const { return m_pDialog->getContext(); } void OControlWizardPage::fillListBox(weld::TreeView& _rList, const Sequence< OUString >& _rItems) { _rList.clear(); const OUString* pItems = _rItems.getConstArray(); const OUString* pEnd = pItems + _rItems.getLength(); sal_Int32 nIndex = 0; for (;pItems < pEnd; ++pItems, ++nIndex) { _rList.append(OUString::number(nIndex), *pItems); } } void OControlWizardPage::fillListBox(weld::ComboBox& _rList, const Sequence< OUString >& _rItems) { _rList.clear(); const OUString* pItems = _rItems.getConstArray(); const OUString* pEnd = pItems + _rItems.getLength(); for (;pItems < pEnd; ++pItems) { _rList.append_text(*pItems); } } void OControlWizardPage::enableFormDatasourceDisplay() { if (m_xFormContentType) // nothing to do return; m_xFrame = m_xBuilder->weld_frame("sourceframe"); m_xFrame->show(); m_xFormContentType = m_xBuilder->weld_label("contenttype"); m_xFormContentTypeLabel = m_xBuilder->weld_label("contenttypelabel"); m_xFormDatasource = m_xBuilder->weld_label("datasource"); m_xFormDatasourceLabel = m_xBuilder->weld_label("datasourcelabel"); m_xFormTable = m_xBuilder->weld_label("formtable"); const OControlWizardContext& rContext = getContext(); if ( rContext.bEmbedded ) { m_xFormDatasourceLabel->hide(); m_xFormDatasource->hide(); } } void OControlWizardPage::initializePage() { if (m_xFormDatasource && m_xFormContentTypeLabel && m_xFormTable) { const OControlWizardContext& rContext = getContext(); OUString sDataSource; OUString sCommand; sal_Int32 nCommandType = CommandType::COMMAND; try { rContext.xForm->getPropertyValue("DataSourceName") >>= sDataSource; rContext.xForm->getPropertyValue("Command") >>= sCommand; rContext.xForm->getPropertyValue("CommandType") >>= nCommandType; } catch(const Exception&) { TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizardPage::initializePage"); } INetURLObject aURL( sDataSource ); if( aURL.GetProtocol() != INetProtocol::NotValid ) sDataSource = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset); m_xFormDatasource->set_label(sDataSource); m_xFormTable->set_label(sCommand); TranslateId pCommandTypeResourceId; switch (nCommandType) { case CommandType::TABLE: pCommandTypeResourceId = RID_STR_TYPE_TABLE; break; case CommandType::QUERY: pCommandTypeResourceId = RID_STR_TYPE_QUERY; break; default: pCommandTypeResourceId = RID_STR_TYPE_COMMAND; break; } m_xFormContentType->set_label(compmodule::ModuleRes(pCommandTypeResourceId)); } OControlWizardPage_Base::initializePage(); } OControlWizard::OControlWizard(weld::Window* _pParent, const Reference< XPropertySet >& _rxObjectModel, const Reference< XComponentContext >& _rxContext ) : WizardMachine(_pParent, WizardButtonFlags::CANCEL | WizardButtonFlags::PREVIOUS | WizardButtonFlags::NEXT | WizardButtonFlags::FINISH) , m_xContext(_rxContext) { m_aContext.xObjectModel = _rxObjectModel; initContext(); defaultButton(WizardButtonFlags::NEXT); enableButtons(WizardButtonFlags::FINISH, false); } OControlWizard::~OControlWizard() { } short OControlWizard::run() { // get the class id of the control we're dealing with sal_Int16 nClassId = FormComponentType::CONTROL; try { getContext().xObjectModel->getPropertyValue("ClassId") >>= nClassId; } catch(const Exception&) { OSL_FAIL("OControlWizard::activate: could not obtain the class id!"); } if (!approveControl(nClassId)) { // TODO: MessageBox or exception return RET_CANCEL; } ActivatePage(); m_xAssistant->set_current_page(0); return OControlWizard_Base::run(); } void OControlWizard::implDetermineShape() { Reference< XIndexAccess > xPageObjects = m_aContext.xDrawPage; DBG_ASSERT(xPageObjects.is(), "OControlWizard::implDetermineShape: invalid page!"); // for comparing the model Reference< XControlModel > xModelCompare(m_aContext.xObjectModel, UNO_QUERY); if (!xPageObjects.is()) return; // loop through all objects of the page sal_Int32 nObjects = xPageObjects->getCount(); Reference< XControlShape > xControlShape; Reference< XControlModel > xControlModel; for (sal_Int32 i=0; igetByIndex(i) >>= xControlShape) { // it _is_ a control shape xControlModel = xControlShape->getControl(); DBG_ASSERT(xControlModel.is(), "OControlWizard::implDetermineShape: control shape without model!"); if (xModelCompare.get() == xControlModel.get()) { m_aContext.xObjectShape = xControlShape; break; } } } } void OControlWizard::implDetermineForm() { Reference< XChild > xModelAsChild(m_aContext.xObjectModel, UNO_QUERY); Reference< XInterface > xControlParent; if (xModelAsChild.is()) xControlParent = xModelAsChild->getParent(); m_aContext.xForm.set(xControlParent, UNO_QUERY); m_aContext.xRowSet.set(xControlParent, UNO_QUERY); DBG_ASSERT(m_aContext.xForm.is() && m_aContext.xRowSet.is(), "OControlWizard::implDetermineForm: missing some interfaces of the control parent!"); } void OControlWizard::implDeterminePage() { try { // get the document model Reference< XChild > xControlAsChild(m_aContext.xObjectModel, UNO_QUERY); Reference< XChild > xModelSearch(xControlAsChild->getParent(), UNO_QUERY); Reference< XModel > xModel(xModelSearch, UNO_QUERY); while (xModelSearch.is() && !xModel.is()) { xModelSearch.set(xModelSearch->getParent(), UNO_QUERY); xModel.set(xModelSearch, UNO_QUERY); } Reference< XDrawPage > xPage; if (xModel.is()) { m_aContext.xDocumentModel = xModel; Reference< XDrawPageSupplier > xPageSupp(xModel, UNO_QUERY); if (xPageSupp.is()) { // it's a document with only one page -> Writer xPage = xPageSupp->getDrawPage(); } else { // get the controller currently working on this model Reference< XController > xController = xModel->getCurrentController(); DBG_ASSERT(xController.is(), "OControlWizard::implDeterminePage: no current controller!"); // maybe it's a spreadsheet Reference< XSpreadsheetView > xView(xController, UNO_QUERY); if (xView.is()) { // okay, it is one Reference< XSpreadsheet > xSheet = xView->getActiveSheet(); xPageSupp.set(xSheet, UNO_QUERY); DBG_ASSERT(xPageSupp.is(), "OControlWizard::implDeterminePage: a spreadsheet which is no page supplier!"); if (xPageSupp.is()) xPage = xPageSupp->getDrawPage(); } else { // can be a draw/impress doc only Reference< XDrawView > xDrawView(xController, UNO_QUERY); DBG_ASSERT(xDrawView.is(), "OControlWizard::implDeterminePage: no alternatives left ... can't determine the page!"); if (xDrawView.is()) xPage = xDrawView->getCurrentPage(); } } } else { DBG_ASSERT(xPage.is(), "OControlWizard::implDeterminePage: can't determine the page (no model)!"); } m_aContext.xDrawPage = xPage; } catch(const Exception&) { TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizard::implDeterminePage"); } } void OControlWizard::implGetDSContext() { try { DBG_ASSERT(m_xContext.is(), "OControlWizard::implGetDSContext: invalid service factory!"); m_aContext.xDatasourceContext = DatabaseContext::create(m_xContext); } catch(const Exception&) { OSL_FAIL("OControlWizard::implGetDSContext: invalid database context!"); } } Reference< XConnection > OControlWizard::getFormConnection(const OAccessRegulator&) const { return getFormConnection(); } Reference< XConnection > OControlWizard::getFormConnection() const { Reference< XConnection > xConn; try { if ( !::dbtools::isEmbeddedInDatabase(m_aContext.xForm,xConn) ) m_aContext.xForm->getPropertyValue("ActiveConnection") >>= xConn; } catch(const Exception&) { TOOLS_WARN_EXCEPTION("extensions.dbpilots", "OControlWizard::getFormConnection"); } return xConn; } void OControlWizard::setFormConnection( const OAccessRegulator& _rAccess, const Reference< XConnection >& _rxConn, bool _bAutoDispose ) { try { Reference< XConnection > xOldConn = getFormConnection(_rAccess); if (xOldConn.get() == _rxConn.get()) return; disposeComponent(xOldConn); // set the new connection if ( _bAutoDispose ) { // for this, use an AutoDisposer (so the conn is cleaned up when the form dies or gets another connection) Reference< XRowSet > xFormRowSet( m_aContext.xForm, UNO_QUERY ); new OAutoConnectionDisposer( xFormRowSet, _rxConn ); } else { m_aContext.xForm->setPropertyValue("ActiveConnection", Any( _rxConn ) ); } } catch(const Exception&) { TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::setFormConnection"); } } bool OControlWizard::updateContext(const OAccessRegulator&) { return initContext(); } Reference< XInteractionHandler > OControlWizard::getInteractionHandler(weld::Window* _pWindow) const { Reference< XInteractionHandler > xHandler; try { xHandler.set( InteractionHandler::createWithParent(m_xContext, nullptr), UNO_QUERY_THROW ); } catch(const Exception&) { } if (!xHandler.is()) { ShowServiceNotAvailableError(_pWindow, u"com.sun.star.task.InteractionHandler", true); } return xHandler; } bool OControlWizard::initContext() { DBG_ASSERT(m_aContext.xObjectModel.is(), "OGroupBoxWizard::initContext: have no control model to work with!"); if (!m_aContext.xObjectModel.is()) return false; // reset the context m_aContext.xForm.clear(); m_aContext.xRowSet.clear(); m_aContext.xDocumentModel.clear(); m_aContext.xDrawPage.clear(); m_aContext.xObjectShape.clear(); m_aContext.aFieldNames.realloc(0); m_aContext.xObjectContainer.clear(); m_aContext.aTypes.clear(); m_aContext.bEmbedded = false; Any aSQLException; Reference< XPreparedStatement > xStatement; try { // get the datasource context implGetDSContext(); // first, determine the form the control belongs to implDetermineForm(); // need the page, too implDeterminePage(); // the shape of the control implDetermineShape(); // get the columns of the object the settings refer to Reference< XNameAccess > xColumns; if (m_aContext.xForm.is()) { // collect some properties of the form OUString sObjectName = ::comphelper::getString(m_aContext.xForm->getPropertyValue("Command")); sal_Int32 nObjectType = ::comphelper::getINT32(m_aContext.xForm->getPropertyValue("CommandType")); // calculate the connection the rowset is working with Reference< XConnection > xConnection; m_aContext.bEmbedded = ::dbtools::isEmbeddedInDatabase( m_aContext.xForm, xConnection ); if ( !m_aContext.bEmbedded ) xConnection = ::dbtools::connectRowset( m_aContext.xRowSet, m_xContext, nullptr ); // get the fields if (xConnection.is()) { switch (nObjectType) { case 0: { Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY); if (xSupplyTables.is() && xSupplyTables->getTables().is() && xSupplyTables->getTables()->hasByName(sObjectName)) { Reference< XColumnsSupplier > xSupplyColumns; m_aContext.xObjectContainer = xSupplyTables->getTables(); m_aContext.xObjectContainer->getByName(sObjectName) >>= xSupplyColumns; DBG_ASSERT(xSupplyColumns.is(), "OControlWizard::initContext: invalid table columns!"); xColumns = xSupplyColumns->getColumns(); } } break; case 1: { Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY); if (xSupplyQueries.is() && xSupplyQueries->getQueries().is() && xSupplyQueries->getQueries()->hasByName(sObjectName)) { Reference< XColumnsSupplier > xSupplyColumns; m_aContext.xObjectContainer = xSupplyQueries->getQueries(); m_aContext.xObjectContainer->getByName(sObjectName) >>= xSupplyColumns; DBG_ASSERT(xSupplyColumns.is(), "OControlWizard::initContext: invalid query columns!"); xColumns = xSupplyColumns->getColumns(); } } break; default: { xStatement = xConnection->prepareStatement(sObjectName); // not interested in any results, only in the fields Reference< XPropertySet > xStatementProps(xStatement, UNO_QUERY); xStatementProps->setPropertyValue("MaxRows", Any(sal_Int32(0))); // TODO: think about handling local SQLExceptions here ... Reference< XColumnsSupplier > xSupplyCols(xStatement->executeQuery(), UNO_QUERY); if (xSupplyCols.is()) xColumns = xSupplyCols->getColumns(); } } } } if (xColumns.is()) { m_aContext.aFieldNames = xColumns->getElementNames(); const OUString* pBegin = m_aContext.aFieldNames.getConstArray(); const OUString* pEnd = pBegin + m_aContext.aFieldNames.getLength(); for(;pBegin != pEnd;++pBegin) { sal_Int32 nFieldType = DataType::OTHER; try { Reference< XPropertySet > xColumn; xColumns->getByName(*pBegin) >>= xColumn; xColumn->getPropertyValue("Type") >>= nFieldType; } catch(const Exception&) { TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "unexpected exception while gathering column information!"); } m_aContext.aTypes.emplace(*pBegin,nFieldType); } } } catch(const SQLContext& e) { aSQLException <<= e; } catch(const SQLWarning& e) { aSQLException <<= e; } catch(const SQLException& e) { aSQLException <<= e; } catch(const Exception&) { TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::initContext: could not retrieve the control context"); } ::comphelper::disposeComponent(xStatement); if (aSQLException.hasValue()) { // an SQLException (or derivee) was thrown ... // prepend an extra SQLContext explaining what we were doing SQLContext aContext(compmodule::ModuleRes(RID_STR_COULDNOTOPENTABLE), {}, {}, 0, aSQLException, {}); // create an interaction handler to display this exception Reference< XInteractionHandler > xHandler = getInteractionHandler(m_xAssistant.get()); if ( !xHandler.is() ) return false; Reference< XInteractionRequest > xRequest = new OInteractionRequest(Any(aContext)); try { xHandler->handle(xRequest); } catch(const Exception&) { } return false; } return m_aContext.aFieldNames.hasElements(); } void OControlWizard::commitControlSettings(OControlWizardSettings const * _pSettings) { DBG_ASSERT(m_aContext.xObjectModel.is(), "OControlWizard::commitControlSettings: have no control model to work with!"); if (!m_aContext.xObjectModel.is()) return; // the only thing we have at the moment is the label try { Reference< XPropertySetInfo > xInfo = m_aContext.xObjectModel->getPropertySetInfo(); if (xInfo.is() && xInfo->hasPropertyByName("Label")) { OUString sControlLabel(_pSettings->sControlLabel); m_aContext.xObjectModel->setPropertyValue( "Label", Any(sControlLabel) ); } } catch(const Exception&) { TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::commitControlSettings: could not commit the basic control settings!"); } } void OControlWizard::initControlSettings(OControlWizardSettings* _pSettings) { DBG_ASSERT(m_aContext.xObjectModel.is(), "OControlWizard::initControlSettings: have no control model to work with!"); if (!m_aContext.xObjectModel.is()) return; // initialize some settings from the control model give try { OUString sLabelPropertyName("Label"); Reference< XPropertySetInfo > xInfo = m_aContext.xObjectModel->getPropertySetInfo(); if (xInfo.is() && xInfo->hasPropertyByName(sLabelPropertyName)) { OUString sControlLabel; m_aContext.xObjectModel->getPropertyValue(sLabelPropertyName) >>= sControlLabel; _pSettings->sControlLabel = sControlLabel; } } catch(const Exception&) { TOOLS_WARN_EXCEPTION( "extensions.dbpilots", "OControlWizard::initControlSettings: could not retrieve the basic control settings!"); } } bool OControlWizard::needDatasourceSelection() { // lemme see ... return !getContext().aFieldNames.hasElements(); // if we got fields, the data source is valid ... } } // namespace dbp /* vim:set shiftwidth=4 softtabstop=4 expandtab: */