1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "controlwizard.hxx"
21 #include <tools/debug.hxx>
22 #include <com/sun/star/container/XNameAccess.hpp>
23 #include <com/sun/star/sdbcx/XTablesSupplier.hpp>
24 #include <com/sun/star/sdb/DatabaseContext.hpp>
25 #include <com/sun/star/sdb/XQueriesSupplier.hpp>
26 #include <com/sun/star/sdbc/XPreparedStatement.hpp>
27 #include <com/sun/star/container/XChild.hpp>
28 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/sheet/XSpreadsheetView.hpp>
31 #include <com/sun/star/drawing/XDrawView.hpp>
32 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
33 #include <com/sun/star/sdb/CommandType.hpp>
34 #include <com/sun/star/sdbc/SQLWarning.hpp>
35 #include <com/sun/star/sdb/SQLContext.hpp>
36 #include <com/sun/star/task/InteractionHandler.hpp>
37 #include <comphelper/types.hxx>
38 #include <connectivity/dbtools.hxx>
39 #include <comphelper/interaction.hxx>
40 #include <vcl/stdtext.hxx>
41 #include <connectivity/conncleanup.hxx>
42 #include <com/sun/star/sdbc/DataType.hpp>
43 #include <tools/urlobj.hxx>
44 #include <osl/diagnose.h>
46 #define WIZARD_SIZE_X 60
47 #define WIZARD_SIZE_Y 23
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
;
72 OAccessRegulator() { }
75 OControlWizardPage::OControlWizardPage(weld::Container
* pPage
, OControlWizard
* pWizard
, const OUString
& rUIXMLDescription
, const OString
& rID
)
76 : OControlWizardPage_Base(pPage
, pWizard
, rUIXMLDescription
, rID
)
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()
92 const OControlWizard
* OControlWizardPage::getDialog() const
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
)
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
)
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
)
146 m_xFrame
= m_xBuilder
->weld_frame("sourceframe");
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");
153 m_xFormTableLabel
= m_xBuilder
->weld_label("formtablelabel");
155 const OControlWizardContext
& rContext
= getContext();
156 if ( rContext
.bEmbedded
)
158 m_xFormDatasourceLabel
->hide();
159 m_xFormDatasource
->hide();
163 void OControlWizardPage::initializePage()
165 if (m_xFormDatasource
&& m_xFormContentTypeLabel
&& m_xFormTable
)
167 const OControlWizardContext
& rContext
= getContext();
168 OUString sDataSource
;
170 sal_Int32 nCommandType
= CommandType::COMMAND
;
173 rContext
.xForm
->getPropertyValue("DataSourceName") >>= sDataSource
;
174 rContext
.xForm
->getPropertyValue("Command") >>= sCommand
;
175 rContext
.xForm
->getPropertyValue("CommandType") >>= nCommandType
;
177 catch(const Exception
&)
179 OSL_FAIL("OControlWizardPage::initializePage: caught an exception!");
182 INetURLObject
aURL( sDataSource
);
183 if( aURL
.GetProtocol() != INetProtocol::NotValid
)
184 sDataSource
= aURL
.GetLastName(INetURLObject::DecodeMechanism::WithCharset
);
185 m_xFormDatasource
->set_label(sDataSource
);
186 m_xFormTable
->set_label(sCommand
);
188 const char* pCommandTypeResourceId
= nullptr;
189 switch (nCommandType
)
191 case CommandType::TABLE
:
192 pCommandTypeResourceId
= RID_STR_TYPE_TABLE
;
195 case CommandType::QUERY
:
196 pCommandTypeResourceId
= RID_STR_TYPE_QUERY
;
200 pCommandTypeResourceId
= RID_STR_TYPE_COMMAND
;
203 m_xFormContentType
->set_label(compmodule::ModuleRes(pCommandTypeResourceId
));
206 OControlWizardPage_Base::initializePage();
209 OControlWizard::OControlWizard(weld::Window
* _pParent
,
210 const Reference
< XPropertySet
>& _rxObjectModel
, const Reference
< XComponentContext
>& _rxContext
)
211 : WizardMachine(_pParent
, WizardButtonFlags::CANCEL
| WizardButtonFlags::PREVIOUS
| WizardButtonFlags::NEXT
| WizardButtonFlags::FINISH
)
212 , m_xContext(_rxContext
)
214 m_aContext
.xObjectModel
= _rxObjectModel
;
217 defaultButton(WizardButtonFlags::NEXT
);
218 enableButtons(WizardButtonFlags::FINISH
, false);
221 OControlWizard::~OControlWizard()
225 short OControlWizard::run()
227 // get the class id of the control we're dealing with
228 sal_Int16 nClassId
= FormComponentType::CONTROL
;
231 getContext().xObjectModel
->getPropertyValue("ClassId") >>= nClassId
;
233 catch(const Exception
&)
235 OSL_FAIL("OControlWizard::activate: could not obtain the class id!");
237 if (!approveControl(nClassId
))
239 // TODO: MessageBox or exception
245 m_xAssistant
->set_current_page(0);
247 return OControlWizard_Base::run();
250 void OControlWizard::implDetermineShape()
252 Reference
< XIndexAccess
> xPageObjects
= m_aContext
.xDrawPage
;
253 DBG_ASSERT(xPageObjects
.is(), "OControlWizard::implDetermineShape: invalid page!");
255 // for comparing the model
256 Reference
< XControlModel
> xModelCompare(m_aContext
.xObjectModel
, UNO_QUERY
);
258 if (xPageObjects
.is())
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
;
281 void OControlWizard::implDetermineForm()
283 Reference
< XChild
> xModelAsChild(m_aContext
.xObjectModel
, UNO_QUERY
);
284 Reference
< XInterface
> xControlParent
;
285 if (xModelAsChild
.is())
286 xControlParent
= xModelAsChild
->getParent();
288 m_aContext
.xForm
.set(xControlParent
, UNO_QUERY
);
289 m_aContext
.xRowSet
.set(xControlParent
, UNO_QUERY
);
290 DBG_ASSERT(m_aContext
.xForm
.is() && m_aContext
.xRowSet
.is(),
291 "OControlWizard::implDetermineForm: missing some interfaces of the control parent!");
296 void OControlWizard::implDeterminePage()
300 // get the document model
301 Reference
< XChild
> xControlAsChild(m_aContext
.xObjectModel
, UNO_QUERY
);
302 Reference
< XChild
> xModelSearch(xControlAsChild
->getParent(), UNO_QUERY
);
304 Reference
< XModel
> xModel(xModelSearch
, UNO_QUERY
);
305 while (xModelSearch
.is() && !xModel
.is())
307 xModelSearch
.set(xModelSearch
->getParent(), UNO_QUERY
);
308 xModel
.set(xModelSearch
, UNO_QUERY
);
311 Reference
< XDrawPage
> xPage
;
314 m_aContext
.xDocumentModel
= xModel
;
316 Reference
< XDrawPageSupplier
> xPageSupp(xModel
, UNO_QUERY
);
318 { // it's a document with only one page -> Writer
319 xPage
= xPageSupp
->getDrawPage();
323 // get the controller currently working on this model
324 Reference
< XController
> xController
= xModel
->getCurrentController();
325 DBG_ASSERT(xController
.is(), "OControlWizard::implDeterminePage: no current controller!");
327 // maybe it's a spreadsheet
328 Reference
< XSpreadsheetView
> xView(xController
, UNO_QUERY
);
331 Reference
< XSpreadsheet
> xSheet
= xView
->getActiveSheet();
332 xPageSupp
.set(xSheet
, UNO_QUERY
);
333 DBG_ASSERT(xPageSupp
.is(), "OControlWizard::implDeterminePage: a spreadsheet which is no page supplier!");
335 xPage
= xPageSupp
->getDrawPage();
338 { // can be a draw/impress doc only
339 Reference
< XDrawView
> xDrawView(xController
, UNO_QUERY
);
340 DBG_ASSERT(xDrawView
.is(), "OControlWizard::implDeterminePage: no alternatives left ... can't determine the page!");
342 xPage
= xDrawView
->getCurrentPage();
348 DBG_ASSERT(xPage
.is(), "OControlWizard::implDeterminePage: can't determine the page (no model)!");
350 m_aContext
.xDrawPage
= xPage
;
352 catch(const Exception
&)
354 OSL_FAIL("OControlWizard::implDeterminePage: caught an exception!");
359 void OControlWizard::implGetDSContext()
363 DBG_ASSERT(m_xContext
.is(), "OControlWizard::implGetDSContext: invalid service factory!");
365 m_aContext
.xDatasourceContext
= DatabaseContext::create(m_xContext
);
367 catch(const Exception
&)
369 OSL_FAIL("OControlWizard::implGetDSContext: invalid database context!");
374 Reference
< XConnection
> OControlWizard::getFormConnection(const OAccessRegulator
&) const
376 return getFormConnection();
379 Reference
< XConnection
> OControlWizard::getFormConnection() const
381 Reference
< XConnection
> xConn
;
384 if ( !::dbtools::isEmbeddedInDatabase(m_aContext
.xForm
,xConn
) )
385 m_aContext
.xForm
->getPropertyValue("ActiveConnection") >>= xConn
;
387 catch(const Exception
&)
389 OSL_FAIL("OControlWizard::getFormConnection: caught an exception!");
395 void OControlWizard::setFormConnection( const OAccessRegulator
& _rAccess
, const Reference
< XConnection
>& _rxConn
, bool _bAutoDispose
)
399 Reference
< XConnection
> xOldConn
= getFormConnection(_rAccess
);
400 if (xOldConn
.get() == _rxConn
.get())
403 disposeComponent(xOldConn
);
405 // set the new connection
408 // for this, use an AutoDisposer (so the conn is cleaned up when the form dies or gets another connection)
409 Reference
< XRowSet
> xFormRowSet( m_aContext
.xForm
, UNO_QUERY
);
410 rtl::Reference
<OAutoConnectionDisposer
> pAutoDispose
= new OAutoConnectionDisposer( xFormRowSet
, _rxConn
);
414 m_aContext
.xForm
->setPropertyValue("ActiveConnection", makeAny( _rxConn
) );
417 catch(const Exception
&)
419 OSL_FAIL("OControlWizard::setFormConnection: caught an exception!");
424 bool OControlWizard::updateContext(const OAccessRegulator
&)
426 return initContext();
429 Reference
< XInteractionHandler
> OControlWizard::getInteractionHandler(weld::Window
* _pWindow
) const
431 Reference
< XInteractionHandler
> xHandler
;
434 xHandler
.set( InteractionHandler::createWithParent(m_xContext
, nullptr), UNO_QUERY_THROW
);
436 catch(const Exception
&) { }
439 const OUString
sInteractionHandlerServiceName("com.sun.star.task.InteractionHandler");
440 ShowServiceNotAvailableError(_pWindow
, sInteractionHandlerServiceName
, true);
445 bool OControlWizard::initContext()
447 DBG_ASSERT(m_aContext
.xObjectModel
.is(), "OGroupBoxWizard::initContext: have no control model to work with!");
448 if (!m_aContext
.xObjectModel
.is())
452 m_aContext
.xForm
.clear();
453 m_aContext
.xRowSet
.clear();
454 m_aContext
.xDocumentModel
.clear();
455 m_aContext
.xDrawPage
.clear();
456 m_aContext
.xObjectShape
.clear();
457 m_aContext
.aFieldNames
.realloc(0);
459 m_aContext
.xObjectContainer
.clear();
460 m_aContext
.aTypes
.clear();
461 m_aContext
.bEmbedded
= false;
464 Reference
< XPreparedStatement
> xStatement
;
467 // get the datasource context
470 // first, determine the form the control belongs to
473 // need the page, too
476 // the shape of the control
477 implDetermineShape();
479 // get the columns of the object the settings refer to
480 Reference
< XNameAccess
> xColumns
;
482 if (m_aContext
.xForm
.is())
484 // collect some properties of the form
485 OUString sObjectName
= ::comphelper::getString(m_aContext
.xForm
->getPropertyValue("Command"));
486 sal_Int32 nObjectType
= ::comphelper::getINT32(m_aContext
.xForm
->getPropertyValue("CommandType"));
488 // calculate the connection the rowset is working with
489 Reference
< XConnection
> xConnection
;
490 m_aContext
.bEmbedded
= ::dbtools::isEmbeddedInDatabase( m_aContext
.xForm
, xConnection
);
491 if ( !m_aContext
.bEmbedded
)
492 xConnection
= ::dbtools::connectRowset( m_aContext
.xRowSet
, m_xContext
, nullptr );
495 if (xConnection
.is())
501 Reference
< XTablesSupplier
> xSupplyTables(xConnection
, UNO_QUERY
);
502 if (xSupplyTables
.is() && xSupplyTables
->getTables().is() && xSupplyTables
->getTables()->hasByName(sObjectName
))
504 Reference
< XColumnsSupplier
> xSupplyColumns
;
505 m_aContext
.xObjectContainer
= xSupplyTables
->getTables();
506 m_aContext
.xObjectContainer
->getByName(sObjectName
) >>= xSupplyColumns
;
507 DBG_ASSERT(xSupplyColumns
.is(), "OControlWizard::initContext: invalid table columns!");
508 xColumns
= xSupplyColumns
->getColumns();
514 Reference
< XQueriesSupplier
> xSupplyQueries(xConnection
, UNO_QUERY
);
515 if (xSupplyQueries
.is() && xSupplyQueries
->getQueries().is() && xSupplyQueries
->getQueries()->hasByName(sObjectName
))
517 Reference
< XColumnsSupplier
> xSupplyColumns
;
518 m_aContext
.xObjectContainer
= xSupplyQueries
->getQueries();
519 m_aContext
.xObjectContainer
->getByName(sObjectName
) >>= xSupplyColumns
;
520 DBG_ASSERT(xSupplyColumns
.is(), "OControlWizard::initContext: invalid query columns!");
521 xColumns
= xSupplyColumns
->getColumns();
527 xStatement
= xConnection
->prepareStatement(sObjectName
);
529 // not interested in any results, only in the fields
530 Reference
< XPropertySet
> xStatementProps(xStatement
, UNO_QUERY
);
531 xStatementProps
->setPropertyValue("MaxRows", makeAny(sal_Int32(0)));
533 // TODO: think about handling local SQLExceptions here ...
534 Reference
< XColumnsSupplier
> xSupplyCols(xStatement
->executeQuery(), UNO_QUERY
);
535 if (xSupplyCols
.is())
536 xColumns
= xSupplyCols
->getColumns();
544 m_aContext
.aFieldNames
= xColumns
->getElementNames();
545 const OUString
* pBegin
= m_aContext
.aFieldNames
.getConstArray();
546 const OUString
* pEnd
= pBegin
+ m_aContext
.aFieldNames
.getLength();
547 for(;pBegin
!= pEnd
;++pBegin
)
549 sal_Int32 nFieldType
= DataType::OTHER
;
552 Reference
< XPropertySet
> xColumn
;
553 xColumns
->getByName(*pBegin
) >>= xColumn
;
554 xColumn
->getPropertyValue("Type") >>= nFieldType
;
556 catch(const Exception
&)
558 OSL_FAIL("OControlWizard::initContext: 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 OSL_FAIL("OControlWizard::initContext: could not retrieve the control context (caught an exception)!");
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
579 aContext
.Message
= compmodule::ModuleRes(RID_STR_COULDNOTOPENTABLE
);
580 aContext
.NextException
= aSQLException
;
582 // create an interaction handler to display this exception
583 Reference
< XInteractionHandler
> xHandler
= getInteractionHandler(m_xAssistant
.get());
584 if ( !xHandler
.is() )
587 Reference
< XInteractionRequest
> xRequest
= new OInteractionRequest(makeAny(aContext
));
590 xHandler
->handle(xRequest
);
592 catch(const Exception
&) { }
596 return m_aContext
.aFieldNames
.hasElements();
600 void OControlWizard::commitControlSettings(OControlWizardSettings
const * _pSettings
)
602 DBG_ASSERT(m_aContext
.xObjectModel
.is(), "OControlWizard::commitControlSettings: have no control model to work with!");
603 if (!m_aContext
.xObjectModel
.is())
606 // the only thing we have at the moment is the label
609 Reference
< XPropertySetInfo
> xInfo
= m_aContext
.xObjectModel
->getPropertySetInfo();
610 if (xInfo
.is() && xInfo
->hasPropertyByName("Label"))
612 OUString
sControlLabel(_pSettings
->sControlLabel
);
613 m_aContext
.xObjectModel
->setPropertyValue(
615 makeAny(sControlLabel
)
619 catch(const Exception
&)
621 OSL_FAIL("OControlWizard::commitControlSettings: could not commit the basic control settings!");
626 void OControlWizard::initControlSettings(OControlWizardSettings
* _pSettings
)
628 DBG_ASSERT(m_aContext
.xObjectModel
.is(), "OControlWizard::initControlSettings: have no control model to work with!");
629 if (!m_aContext
.xObjectModel
.is())
632 // initialize some settings from the control model give
635 OUString
sLabelPropertyName("Label");
636 Reference
< XPropertySetInfo
> xInfo
= m_aContext
.xObjectModel
->getPropertySetInfo();
637 if (xInfo
.is() && xInfo
->hasPropertyByName(sLabelPropertyName
))
639 OUString sControlLabel
;
640 m_aContext
.xObjectModel
->getPropertyValue(sLabelPropertyName
) >>= sControlLabel
;
641 _pSettings
->sControlLabel
= sControlLabel
;
644 catch(const Exception
&)
646 OSL_FAIL("OControlWizard::initControlSettings: could not retrieve the basic control settings!");
651 bool OControlWizard::needDatasourceSelection()
654 return !getContext().aFieldNames
.hasElements();
655 // if we got fields, the data source is valid ...
662 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */