diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /dbaccess/source/ui/app | |
parent | Initial commit. (diff) | |
download | libreoffice-1ad18e38974bb28c3d98d0be8f7d8c18fc56de29.tar.xz libreoffice-1ad18e38974bb28c3d98d0be8f7d8c18fc56de29.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dbaccess/source/ui/app')
19 files changed, 10016 insertions, 0 deletions
diff --git a/dbaccess/source/ui/app/AppController.cxx b/dbaccess/source/ui/app/AppController.cxx new file mode 100644 index 000000000..402880eed --- /dev/null +++ b/dbaccess/source/ui/app/AppController.cxx @@ -0,0 +1,2809 @@ +/* -*- 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 <memory> +#include "AppController.hxx" +#include <core_resource.hxx> +#include <strings.hxx> +#include <advancedsettingsdlg.hxx> +#include "subcomponentmanager.hxx" +#include <uiservices.hxx> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XHierarchicalNameContainer.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/ErrorMessageDialog.hpp> +#include <com/sun/star/sdb/XOfficeDatabaseDocument.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <com/sun/star/sdbc/SQLWarning.hpp> +#include <com/sun/star/sdbc/XDataSource.hpp> +#include <com/sun/star/sdbcx/XAlterView.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/util/XFlushable.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XModifyBroadcaster.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/sdb/application/DatabaseObject.hpp> +#include <com/sun/star/sdb/application/DatabaseObjectContainer.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <osl/diagnose.h> + +#include <svl/filenotation.hxx> +#include <vcl/treelistbox.hxx> +#include <vcl/transfer.hxx> +#include <svtools/cliplistener.hxx> + +#include <comphelper/sequence.hxx> +#include <comphelper/uno3.hxx> +#include <comphelper/types.hxx> +#include <comphelper/interaction.hxx> +#include <comphelper/processfactory.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <unotools/closeveto.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/historyoptions.hxx> + +#include <sfx2/mailmodelapi.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/QuerySaveDocument.hxx> + +#include <cppuhelper/exc_hlp.hxx> + +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> + +#include <svx/dbaexchange.hxx> +#include <svx/dbaobjectex.hxx> +#include <svx/svxdlg.hxx> + +#include <osl/mutex.hxx> +#include "AppView.hxx" +#include <browserids.hxx> +#include <dbu_reghelper.hxx> +#include <strings.hrc> +#include <defaultobjectnamecheck.hxx> +#include <databaseobjectview.hxx> +#include "AppDetailView.hxx" +#include <linkeddocuments.hxx> +#include <UITools.hxx> +#include <dsntypes.hxx> +#include <dlgsave.hxx> +#include <dbaccess_slotid.hrc> + +extern "C" void createRegistryInfo_ODBApplication() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::OApplicationController > aAutoRegistration; +} + +namespace dbaui +{ +using namespace ::dbtools; +using namespace ::svx; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::task; +using ::com::sun::star::document::XEmbeddedScripts; +using ::com::sun::star::document::XDocumentEventBroadcaster; +using ::com::sun::star::sdb::application::NamedDatabaseObject; + +namespace DatabaseObject = ::com::sun::star::sdb::application::DatabaseObject; +namespace DatabaseObjectContainer = ::com::sun::star::sdb::application::DatabaseObjectContainer; + +OUString SAL_CALL OApplicationController::getImplementationName() +{ + return getImplementationName_Static(); +} + +OUString OApplicationController::getImplementationName_Static() +{ + return SERVICE_SDB_APPLICATIONCONTROLLER; +} + +Sequence< OUString> OApplicationController::getSupportedServiceNames_Static() +{ + Sequence<OUString> aSupported { "com.sun.star.sdb.application.DefaultViewController" }; + return aSupported; +} + +Sequence< OUString> SAL_CALL OApplicationController::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +Reference< XInterface > OApplicationController::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + return *(new OApplicationController( comphelper::getComponentContext(_rxFactory))); +} + +namespace { + +class SelectionGuard; + +} + +// OApplicationController +class SelectionNotifier +{ +private: + ::comphelper::OInterfaceContainerHelper2 m_aSelectionListeners; + ::cppu::OWeakObject& m_rContext; + sal_Int32 m_nSelectionNestingLevel; + +public: + SelectionNotifier( ::osl::Mutex& _rMutex, ::cppu::OWeakObject& _rContext ) + :m_aSelectionListeners( _rMutex ) + ,m_rContext( _rContext ) + ,m_nSelectionNestingLevel( 0 ) + { + } + + SelectionNotifier(const SelectionNotifier&) = delete; + const SelectionNotifier& operator=(const SelectionNotifier&) = delete; + + void addListener( const Reference< XSelectionChangeListener >& Listener ) + { + m_aSelectionListeners.addInterface( Listener ); + } + + void removeListener( const Reference< XSelectionChangeListener >& Listener ) + { + m_aSelectionListeners.removeInterface( Listener ); + } + + void disposing() + { + EventObject aEvent( m_rContext ); + m_aSelectionListeners.disposeAndClear( aEvent ); + } + + struct SelectionGuardAccess { friend SelectionGuard; private: SelectionGuardAccess() { } }; + + /** enters a block which modifies the selection of our owner. + + Can be called multiple times, the only important thing is to call leaveSelection + equally often. + */ + void enterSelection( SelectionGuardAccess ) + { + ++m_nSelectionNestingLevel; + } + + /** leaves a block which modifies the selection of our owner + + Must be paired with enterSelection calls. + + When the last block is left, i.e. the last leaveSelection call is made on the current stack, + then our SelectionChangeListeners are notified + */ + void leaveSelection( SelectionGuardAccess ) + { + if ( --m_nSelectionNestingLevel == 0 ) + { + EventObject aEvent( m_rContext ); + m_aSelectionListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aEvent ); + } + } +}; + +namespace { + +class SelectionGuard +{ +public: + explicit SelectionGuard( SelectionNotifier& _rNotifier ) + :m_rNotifier( _rNotifier ) + { + m_rNotifier.enterSelection( SelectionNotifier::SelectionGuardAccess() ); + } + + ~SelectionGuard() + { + m_rNotifier.leaveSelection( SelectionNotifier::SelectionGuardAccess() ); + } + + SelectionGuard(const SelectionGuard&) = delete; + const SelectionGuard& operator=(const SelectionGuard&) = delete; + +private: + SelectionNotifier& m_rNotifier; +}; + +} + +// OApplicationController +OApplicationController::OApplicationController(const Reference< XComponentContext >& _rxORB) + :OGenericUnoController( _rxORB ) + ,m_aContextMenuInterceptors( getMutex() ) + ,m_pSubComponentManager( new SubComponentManager( *this, getSharedMutex() ) ) + ,m_aTypeCollection( _rxORB ) + ,m_aTableCopyHelper(this) + ,m_nAsyncDrop(nullptr) + ,m_aSelectContainerEvent( LINK( this, OApplicationController, OnSelectContainer ) ) + ,m_ePreviewMode(E_PREVIEWNONE) + ,m_eCurrentType(E_NONE) + ,m_bNeedToReconnect(false) + ,m_bSuspended( false ) + ,m_pSelectionNotifier( new SelectionNotifier( getMutex(), *this ) ) +{ +} + +OApplicationController::~OApplicationController() +{ + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + OSL_FAIL("Please check who doesn't dispose this component!"); + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + clearView(); +} + +IMPLEMENT_FORWARD_XTYPEPROVIDER2(OApplicationController,OGenericUnoController,OApplicationController_Base) +IMPLEMENT_FORWARD_XINTERFACE2(OApplicationController,OGenericUnoController,OApplicationController_Base) +void OApplicationController::disconnect() +{ + if ( m_xDataSourceConnection.is() ) + stopConnectionListening( m_xDataSourceConnection ); + + try + { + // temporary (hopefully!) hack for #i55274# + Reference< XFlushable > xFlush( m_xDataSourceConnection, UNO_QUERY ); + if ( xFlush.is() && m_xMetaData.is() && !m_xMetaData->isReadOnly() ) + xFlush->flush(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xDataSourceConnection.clear(); + m_xMetaData.clear(); + + InvalidateAll(); +} + +void SAL_CALL OApplicationController::disposing() +{ + for( const auto& rContainerListener : m_aCurrentContainers ) + { + if( rContainerListener.is() ) + { + rContainerListener->removeContainerListener( this ); + } + } + + m_aCurrentContainers.clear(); + m_pSubComponentManager->disposing(); + m_pSelectionNotifier->disposing(); + + if ( getView() ) + { + getContainer()->showPreview(nullptr); + m_pClipboardNotifier->ClearCallbackLink(); + m_pClipboardNotifier->RemoveListener( getView() ); + m_pClipboardNotifier.clear(); + } + + disconnect(); + try + { + Reference < XFrame > xFrame; + attachFrame( xFrame ); + + if ( m_xDataSource.is() ) + { + m_xDataSource->removePropertyChangeListener(OUString(), this); + m_xDataSource->removePropertyChangeListener(PROPERTY_INFO, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_URL, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_ISPASSWORDREQUIRED, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_LAYOUTINFORMATION, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_SUPPRESSVERSIONCL, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_TABLEFILTER, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_TABLETYPEFILTER, this); + m_xDataSource->removePropertyChangeListener(PROPERTY_USER, this); + m_xDataSource = nullptr; + } + + Reference< XModifyBroadcaster > xBroadcaster( m_xModel, UNO_QUERY ); + if ( xBroadcaster.is() ) + xBroadcaster->removeModifyListener(static_cast<XModifyListener*>(this)); + + if ( m_xModel.is() ) + { + OUString sUrl = m_xModel->getURL(); + if ( !sUrl.isEmpty() ) + { + ::comphelper::NamedValueCollection aArgs( m_xModel->getArgs() ); + if ( aArgs.getOrDefault( "PickListEntry", true ) ) + { + OUString aFilter; + INetURLObject aURL( m_xModel->getURL() ); + std::shared_ptr<const SfxFilter> pFilter = getStandardDatabaseFilter(); + if ( pFilter ) + aFilter = pFilter->GetFilterName(); + + // add to svtool history options + SvtHistoryOptions().AppendItem( ePICKLIST, + aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ), + aFilter, + getStrippedDatabaseName(), + std::nullopt); + + // add to recent document list + if ( aURL.GetProtocol() == INetProtocol::File ) + Application::AddToRecentDocumentList( aURL.GetURLNoPass( INetURLObject::DecodeMechanism::NONE ), + pFilter ? pFilter->GetMimeType() : OUString(), + pFilter ? pFilter->GetServiceName() : OUString() ); + } + } + + m_xModel->disconnectController( this ); + + m_xModel.clear(); + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + clearView(); + OGenericUnoController::disposing(); // here the m_refCount must be equal 5 +} + +bool OApplicationController::Construct(vcl::Window* _pParent) +{ + setView( VclPtr<OApplicationView>::Create( _pParent, getORB(), *this, m_ePreviewMode ) ); + + // late construction + bool bSuccess = false; + try + { + getContainer()->Construct(); + bSuccess = true; + } + catch(const SQLException&) + { + } + catch(const Exception&) + { + OSL_FAIL("OApplicationController::Construct : the construction of UnoDataBrowserView failed !"); + } + + if ( !bSuccess ) + { + clearView(); + return false; + } + + // now that we have a view we can create the clipboard listener + m_aSystemClipboard = TransferableDataHelper::CreateFromSystemClipboard( getView() ); + m_aSystemClipboard.StartClipboardListening( ); + + m_pClipboardNotifier = new TransferableClipboardListener( LINK( this, OApplicationController, OnClipboardChanged ) ); + m_pClipboardNotifier->AddListener( getView() ); + + OGenericUnoController::Construct( _pParent ); + getView()->Show(); + + return true; +} + +void SAL_CALL OApplicationController::disposing(const EventObject& _rSource) +{ + ::osl::MutexGuard aGuard( getMutex() ); + Reference<XConnection> xCon(_rSource.Source, UNO_QUERY); + if ( xCon.is() ) + { + OSL_ENSURE( m_xDataSourceConnection == xCon, + "OApplicationController::disposing: which connection does this come from?" ); + + if ( getContainer() && getContainer()->getElementType() == E_TABLE ) + getContainer()->clearPages(); + if ( m_xDataSourceConnection == xCon ) + { + m_xMetaData.clear(); + m_xDataSourceConnection.clear(); + } + } + else if ( _rSource.Source == m_xModel ) + { + m_xModel.clear(); + } + else if ( _rSource.Source == m_xDataSource ) + { + m_xDataSource = nullptr; + } + else + { + Reference<XContainer> xContainer( _rSource.Source, UNO_QUERY ); + if ( xContainer.is() ) + { + TContainerVector::iterator aFind = std::find(m_aCurrentContainers.begin(),m_aCurrentContainers.end(),xContainer); + if ( aFind != m_aCurrentContainers.end() ) + m_aCurrentContainers.erase(aFind); + } + OGenericUnoController::disposing( _rSource ); + } +} + +sal_Bool SAL_CALL OApplicationController::suspend(sal_Bool bSuspend) +{ + // notify the OnPrepareViewClosing event (before locking any mutex) + Reference< XDocumentEventBroadcaster > xBroadcaster( m_xModel, UNO_QUERY ); + if ( xBroadcaster.is() ) + { + xBroadcaster->notifyDocumentEvent( + "OnPrepareViewClosing", + this, + Any() + ); + } + + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( getView() && getView()->IsInModalMode() ) + return false; + + bool bCanSuspend = true; + + if ( m_bSuspended != bool(bSuspend) ) + { + if ( bSuspend && !closeSubComponents() ) + return false; + + Reference<XModifiable> xModi(m_xModel,UNO_QUERY); + Reference<XStorable> xStor(getModel(),UNO_QUERY); + + if ( bSuspend + && xStor.is() + && !xStor->isReadonly() + && ( xModi.is() + && xModi->isModified() + ) + ) + { + switch (ExecuteQuerySaveDocument(getFrameWeld(), getStrippedDatabaseName())) + { + case RET_YES: + Execute(ID_BROWSER_SAVEDOC,Sequence<PropertyValue>()); + bCanSuspend = !xModi->isModified(); + // when we save the document this must be false else some press cancel + break; + case RET_CANCEL: + bCanSuspend = false; + break; + default: + break; + } + } + } + + if ( bCanSuspend ) + m_bSuspended = bSuspend; + + return bCanSuspend; +} + +FeatureState OApplicationController::GetState(sal_uInt16 _nId) const +{ + FeatureState aReturn; + aReturn.bEnabled = false; + // check this first + if ( !getContainer() || m_bReadOnly ) + return aReturn; + + try + { + switch (_nId) + { + case SID_OPENURL: + aReturn.bEnabled = true; + if ( m_xModel.is() ) + aReturn.sTitle = m_xModel->getURL(); + break; + case ID_BROWSER_COPY: + { + sal_Int32 nCount = getContainer()->getSelectionCount(); + aReturn.bEnabled = nCount >= 1; + if ( aReturn.bEnabled && nCount == 1 && getContainer()->getElementType() == E_TABLE ) + aReturn.bEnabled = getContainer()->isALeafSelected(); + } + break; + case ID_BROWSER_CUT: + aReturn.bEnabled = !isDataSourceReadOnly() && getContainer()->getSelectionCount() >= 1; + aReturn.bEnabled = aReturn.bEnabled && (getContainer()->getElementType() != E_TABLE || getContainer()->isCutAllowed()); + break; + case ID_BROWSER_PASTE: + switch( getContainer()->getElementType() ) + { + case E_TABLE: + aReturn.bEnabled = !isDataSourceReadOnly() && !isConnectionReadOnly() && isTableFormat(); + break; + case E_QUERY: + aReturn.bEnabled = !isDataSourceReadOnly() && getViewClipboard().HasFormat(SotClipboardFormatId::DBACCESS_QUERY); + break; + default: + aReturn.bEnabled = !isDataSourceReadOnly() && OComponentTransferable::canExtractComponentDescriptor(getViewClipboard().GetDataFlavorExVector(),getContainer()->getElementType() == E_FORM); + } + break; + case SID_DB_APP_PASTE_SPECIAL: + aReturn.bEnabled = getContainer()->getElementType() == E_TABLE && !isDataSourceReadOnly() && !isConnectionReadOnly() && isTableFormat(); + break; + case SID_OPENDOC: + aReturn.bEnabled = true; + break; + case ID_BROWSER_SAVEDOC: + aReturn.bEnabled = !isDataSourceReadOnly(); + break; + case ID_BROWSER_SAVEASDOC: + aReturn.bEnabled = true; + break; + case ID_BROWSER_SORTUP: + aReturn.bEnabled = getContainer()->isFilled() && getContainer()->getElementCount(); + aReturn.bChecked = aReturn.bEnabled && getContainer()->isSortUp(); + break; + case ID_BROWSER_SORTDOWN: + aReturn.bEnabled = getContainer()->isFilled() && getContainer()->getElementCount(); + aReturn.bChecked = aReturn.bEnabled && !getContainer()->isSortUp(); + break; + + case SID_NEWDOC: + case SID_APP_NEW_FORM: + case ID_DOCUMENT_CREATE_REPWIZ: + aReturn.bEnabled = !isDataSourceReadOnly() && SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::WRITER); + break; + case SID_APP_NEW_REPORT: + aReturn.bEnabled = !isDataSourceReadOnly() + && SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::WRITER); + if ( aReturn.bEnabled ) + { + Reference< XContentEnumerationAccess > xEnumAccess(m_xContext->getServiceManager(), UNO_QUERY); + aReturn.bEnabled = xEnumAccess.is(); + if ( aReturn.bEnabled ) + { + const OUString sReportEngineServiceName = ::dbtools::getDefaultReportEngineServiceName(m_xContext); + aReturn.bEnabled = !sReportEngineServiceName.isEmpty(); + if ( aReturn.bEnabled ) + { + const Reference< XEnumeration > xEnumDrivers = xEnumAccess->createContentEnumeration(sReportEngineServiceName); + aReturn.bEnabled = xEnumDrivers.is() && xEnumDrivers->hasMoreElements(); + } + } + } + break; + case SID_DB_APP_VIEW_TABLES: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer()->getElementType() == E_TABLE; + break; + case SID_DB_APP_VIEW_QUERIES: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer()->getElementType() == E_QUERY; + break; + case SID_DB_APP_VIEW_FORMS: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer()->getElementType() == E_FORM; + break; + case SID_DB_APP_VIEW_REPORTS: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer()->getElementType() == E_REPORT; + break; + case ID_NEW_QUERY_DESIGN: + case ID_NEW_QUERY_SQL: + case ID_APP_NEW_QUERY_AUTO_PILOT: + case SID_DB_FORM_NEW_PILOT: + aReturn.bEnabled = !isDataSourceReadOnly(); + break; + case ID_NEW_VIEW_DESIGN: + case SID_DB_NEW_VIEW_SQL: + case ID_NEW_VIEW_DESIGN_AUTO_PILOT: + aReturn.bEnabled = !isDataSourceReadOnly() && !isConnectionReadOnly(); + if ( aReturn.bEnabled ) + { + Reference<XViewsSupplier> xViewsSup( getConnection(), UNO_QUERY ); + aReturn.bEnabled = xViewsSup.is(); + } + break; + case ID_NEW_TABLE_DESIGN: + case ID_NEW_TABLE_DESIGN_AUTO_PILOT: + aReturn.bEnabled = !isDataSourceReadOnly() && !isConnectionReadOnly(); + break; + case ID_DIRECT_SQL: + aReturn.bEnabled = true; + break; + case SID_APP_NEW_FOLDER: + aReturn.bEnabled = !isDataSourceReadOnly() && getContainer()->getSelectionCount() <= 1; + if ( aReturn.bEnabled ) + { + const ElementType eType = getContainer()->getElementType(); + aReturn.bEnabled = eType == E_REPORT || eType == E_FORM; + } + break; + case SID_FORM_CREATE_REPWIZ_PRE_SEL: + case SID_REPORT_CREATE_REPWIZ_PRE_SEL: + case SID_APP_NEW_REPORT_PRE_SEL: + aReturn.bEnabled = !isDataSourceReadOnly() + && SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::WRITER) + && getContainer()->isALeafSelected(); + if ( aReturn.bEnabled ) + { + ElementType eType = getContainer()->getElementType(); + aReturn.bEnabled = eType == E_QUERY || eType == E_TABLE; + if ( aReturn.bEnabled && SID_APP_NEW_REPORT_PRE_SEL == _nId ) + { + Reference< XContentEnumerationAccess > xEnumAccess(m_xContext->getServiceManager(), UNO_QUERY); + aReturn.bEnabled = xEnumAccess.is(); + if ( aReturn.bEnabled ) + { + static const char s_sReportDesign[] = "org.libreoffice.report.pentaho.SOReportJobFactory"; + Reference< XEnumeration > xEnumDrivers = xEnumAccess->createContentEnumeration(s_sReportDesign); + aReturn.bEnabled = xEnumDrivers.is() && xEnumDrivers->hasMoreElements(); + } + } + } + break; + case SID_DB_APP_DELETE: + case SID_DB_APP_RENAME: + aReturn.bEnabled = isRenameDeleteAllowed(getContainer()->getElementType(), _nId == SID_DB_APP_DELETE); + break; + case SID_DB_APP_TABLE_DELETE: + case SID_DB_APP_TABLE_RENAME: + aReturn.bEnabled = isRenameDeleteAllowed(E_TABLE, _nId == SID_DB_APP_TABLE_DELETE); + break; + case SID_DB_APP_QUERY_DELETE: + case SID_DB_APP_QUERY_RENAME: + aReturn.bEnabled = isRenameDeleteAllowed(E_QUERY, _nId == SID_DB_APP_QUERY_DELETE); + break; + case SID_DB_APP_FORM_DELETE: + case SID_DB_APP_FORM_RENAME: + aReturn.bEnabled = isRenameDeleteAllowed(E_FORM, _nId == SID_DB_APP_FORM_DELETE); + break; + case SID_DB_APP_REPORT_DELETE: + case SID_DB_APP_REPORT_RENAME: + aReturn.bEnabled = isRenameDeleteAllowed(E_REPORT, _nId == SID_DB_APP_REPORT_DELETE); + break; + + case SID_SELECTALL: + aReturn.bEnabled = getContainer()->getElementCount() > 0 && getContainer()->getSelectionCount() != getContainer()->getElementCount(); + break; + case SID_DB_APP_EDIT: + case SID_DB_APP_TABLE_EDIT: + case SID_DB_APP_QUERY_EDIT: + case SID_DB_APP_FORM_EDIT: + case SID_DB_APP_REPORT_EDIT: + aReturn.bEnabled = !isDataSourceReadOnly() && getContainer()->getSelectionCount() > 0 + && getContainer()->isALeafSelected(); + break; + case SID_DB_APP_EDIT_SQL_VIEW: + if ( isDataSourceReadOnly() ) + aReturn.bEnabled = false; + else + { + switch ( getContainer()->getElementType() ) + { + case E_QUERY: + aReturn.bEnabled = ( getContainer()->getSelectionCount() > 0 ) + && ( getContainer()->isALeafSelected() ); + break; + case E_TABLE: + aReturn.bEnabled = false; + // there's one exception: views which support altering their underlying + // command can be edited in SQL view, too + if ( ( getContainer()->getSelectionCount() > 0 ) + && ( getContainer()->isALeafSelected() ) + ) + { + std::vector< OUString > aSelected; + getSelectionElementNames( aSelected ); + bool bAlterableViews = true; + for (auto const& selectedName : aSelected) + { + bAlterableViews &= impl_isAlterableView_nothrow(selectedName); + if (!bAlterableViews) + break; + } + aReturn.bEnabled = bAlterableViews; + } + break; + default: + break; + } + } + break; + case SID_DB_APP_OPEN: + case SID_DB_APP_TABLE_OPEN: + case SID_DB_APP_QUERY_OPEN: + case SID_DB_APP_FORM_OPEN: + case SID_DB_APP_REPORT_OPEN: + aReturn.bEnabled = getContainer()->getSelectionCount() > 0 && getContainer()->isALeafSelected(); + break; + case SID_DB_APP_DSUSERADMIN: + aReturn.bEnabled = !dbaccess::ODsnTypeCollection::isEmbeddedDatabase(::comphelper::getString(m_xDataSource->getPropertyValue(PROPERTY_URL))); + break; + case SID_DB_APP_DSRELDESIGN: + aReturn.bEnabled = true; + break; + case SID_DB_APP_TABLEFILTER: + aReturn.bEnabled = !isDataSourceReadOnly(); + break; + case SID_DB_APP_REFRESH_TABLES: + aReturn.bEnabled = getContainer()->getElementType() == E_TABLE && isConnected(); + break; + case SID_DB_APP_DSPROPS: + aReturn.bEnabled = m_xDataSource.is() && dbaccess::ODsnTypeCollection::isShowPropertiesEnabled(::comphelper::getString(m_xDataSource->getPropertyValue(PROPERTY_URL))); + break; + case SID_DB_APP_DSCONNECTION_TYPE: + aReturn.bEnabled = !isDataSourceReadOnly() && m_xDataSource.is() && !dbaccess::ODsnTypeCollection::isEmbeddedDatabase(::comphelper::getString(m_xDataSource->getPropertyValue(PROPERTY_URL))); + break; + case SID_DB_APP_DSADVANCED_SETTINGS: + aReturn.bEnabled = m_xDataSource.is() && AdvancedSettingsDialog::doesHaveAnyAdvancedSettings( m_aTypeCollection.getType(::comphelper::getString( m_xDataSource->getPropertyValue( PROPERTY_URL ) )) ); + break; + case SID_DB_APP_CONVERTTOVIEW: + aReturn.bEnabled = !isDataSourceReadOnly(); + if ( aReturn.bEnabled ) + { + ElementType eType = getContainer()->getElementType(); + aReturn.bEnabled = eType == E_QUERY && getContainer()->getSelectionCount() > 0; + if ( aReturn.bEnabled ) + { + Reference<XViewsSupplier> xViewSup( getConnection(), UNO_QUERY ); + aReturn.bEnabled = xViewSup.is() && Reference<XAppend>(xViewSup->getViews(),UNO_QUERY).is(); + } + } + break; + case SID_DB_APP_DISABLE_PREVIEW: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer()->getPreviewMode() == E_PREVIEWNONE; + break; + case SID_DB_APP_VIEW_DOCINFO_PREVIEW: + { + ElementType eType = getContainer()->getElementType(); + aReturn.bEnabled = (E_REPORT == eType || E_FORM == eType); + aReturn.bChecked = getContainer()->getPreviewMode() == E_DOCUMENTINFO; + } + break; + case SID_DB_APP_VIEW_DOC_PREVIEW: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer()->getPreviewMode() == E_DOCUMENT; + break; + case ID_BROWSER_UNDO: + aReturn.bEnabled = false; + break; + case SID_MAIL_SENDDOC: + aReturn.bEnabled = true; + break; + case SID_DB_APP_SENDREPORTASMAIL: + { + ElementType eType = getContainer()->getElementType(); + aReturn.bEnabled = E_REPORT == eType && getContainer()->getSelectionCount() > 0 && getContainer()->isALeafSelected(); + } + break; + case SID_DB_APP_SENDREPORTTOWRITER: + case SID_DB_APP_DBADMIN: + aReturn.bEnabled = false; + break; + case SID_DB_APP_STATUS_TYPE: + aReturn.bEnabled = m_xDataSource.is(); + if ( aReturn.bEnabled ) + { + OUString sURL; + m_xDataSource->getPropertyValue(PROPERTY_URL) >>= sURL; + OUString sDSTypeName; + if ( dbaccess::ODsnTypeCollection::isEmbeddedDatabase( sURL ) ) + { + sDSTypeName = DBA_RES(RID_STR_EMBEDDED_DATABASE); + } + else + { + sDSTypeName = m_aTypeCollection.getTypeDisplayName(sURL); + } + aReturn.sTitle = sDSTypeName; + } + break; + case SID_DB_APP_STATUS_DBNAME: + aReturn.bEnabled = m_xDataSource.is(); + if ( aReturn.bEnabled ) + { + OUString sURL; + m_xDataSource->getPropertyValue(PROPERTY_URL) >>= sURL; + OUString sDatabaseName; + OUString sHostName; + sal_Int32 nPortNumber( -1 ); + + m_aTypeCollection.extractHostNamePort( sURL, sDatabaseName, sHostName, nPortNumber ); + + if ( sDatabaseName.isEmpty() ) + sDatabaseName = m_aTypeCollection.cutPrefix( sURL ); + if ( m_aTypeCollection.isFileSystemBased(sURL) ) + { + sDatabaseName = SvtPathOptions().SubstituteVariable( sDatabaseName ); + if ( !sDatabaseName.isEmpty() ) + { + ::svt::OFileNotation aFileNotation(sDatabaseName); + // set this decoded URL as text + sDatabaseName = aFileNotation.get(::svt::OFileNotation::N_SYSTEM); + } + } + + if ( sDatabaseName.isEmpty() ) + sDatabaseName = m_aTypeCollection.getTypeDisplayName( sURL ); + + aReturn.sTitle = sDatabaseName; + } + break; + case SID_DB_APP_STATUS_USERNAME: + aReturn.bEnabled = m_xDataSource.is(); + if ( aReturn.bEnabled ) + m_xDataSource->getPropertyValue( PROPERTY_USER ) >>= aReturn.sTitle; + break; + case SID_DB_APP_STATUS_HOSTNAME: + aReturn.bEnabled = m_xDataSource.is(); + if ( aReturn.bEnabled ) + { + OUString sURL; + m_xDataSource->getPropertyValue( PROPERTY_URL ) >>= sURL; + + OUString sHostName, sDatabaseName; + sal_Int32 nPortNumber = -1; + m_aTypeCollection.extractHostNamePort( sURL, sDatabaseName, sHostName, nPortNumber ); + aReturn.sTitle = sHostName; + } + break; + default: + aReturn = OGenericUnoController::GetState(_nId); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return aReturn; +} + +namespace +{ + bool lcl_handleException_nothrow( const Reference< XModel >& _rxDocument, const Any& _rException ) + { + bool bHandled = false; + + // try handling the error with an interaction handler + ::comphelper::NamedValueCollection aArgs( _rxDocument->getArgs() ); + Reference< XInteractionHandler > xHandler( aArgs.getOrDefault( "InteractionHandler", Reference< XInteractionHandler >() ) ); + if ( xHandler.is() ) + { + rtl::Reference pRequest( new ::comphelper::OInteractionRequest( _rException ) ); + rtl::Reference pApprove( new ::comphelper::OInteractionApprove ); + pRequest->addContinuation( pApprove.get() ); + + try + { + xHandler->handle( pRequest.get() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + bHandled = pApprove->wasSelected(); + } + return bHandled; + } +} + +void OApplicationController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( isUserDefinedFeature( _nId ) ) + { + OGenericUnoController::Execute( _nId, aArgs ); + return; + } + + if ( !getContainer() || m_bReadOnly ) + return; // return without execution + + try + { + switch(_nId) + { + case ID_BROWSER_CUT: + getContainer()->cut(); + break; + case ID_BROWSER_COPY: + { + rtl::Reference<TransferableHelper> pTransfer = copyObject(); + if ( pTransfer ) + pTransfer->CopyToClipboard(getView()); + } + break; + case ID_BROWSER_PASTE: + { + const TransferableDataHelper& rTransferData( getViewClipboard() ); + ElementType eType = getContainer()->getElementType(); + + switch( eType ) + { + case E_TABLE: + { + // get the selected tablename + std::vector< OUString > aList; + getSelectionElementNames( aList ); + if ( !aList.empty() ) + m_aTableCopyHelper.SetTableNameForAppend( *aList.begin() ); + else + m_aTableCopyHelper.ResetTableNameForAppend(); + + m_aTableCopyHelper.pasteTable( rTransferData , getDatabaseName(), ensureConnection() ); + } + break; + + case E_QUERY: + if ( rTransferData.HasFormat(SotClipboardFormatId::DBACCESS_QUERY) ) + paste( E_QUERY, ODataAccessObjectTransferable::extractObjectDescriptor( rTransferData ) ); + break; + default: + { + std::vector< OUString> aList; + getSelectionElementNames(aList); + OUString sFolderNameToInsertInto; + if ( !aList.empty() ) + { + Reference< XHierarchicalNameAccess > xContainer(getElements(eType),UNO_QUERY); + if ( xContainer.is() + && xContainer->hasByHierarchicalName(*aList.begin()) + && (xContainer->getByHierarchicalName(*aList.begin()) >>= xContainer) + && xContainer.is() + ) + sFolderNameToInsertInto = *aList.begin(); + } + paste( eType, OComponentTransferable::extractComponentDescriptor( rTransferData ), + sFolderNameToInsertInto ); + } + break; + } + } + break; + case SID_DB_APP_PASTE_SPECIAL: + { + if ( !aArgs.hasElements() ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<SfxAbstractPasteDialog> pDlg(pFact->CreatePasteDialog(getFrameWeld())); + std::vector<SotClipboardFormatId> aFormatIds; + getSupportedFormats(getContainer()->getElementType(),aFormatIds); + for (auto const& formatId : aFormatIds) + pDlg->Insert(formatId,""); + + const TransferableDataHelper& rClipboard = getViewClipboard(); + pasteFormat(pDlg->GetFormat(rClipboard.GetTransferable())); + } + else + { + const PropertyValue* pIter = aArgs.getConstArray(); + const PropertyValue* pEnd = pIter + aArgs.getLength(); + for( ; pIter != pEnd ; ++pIter) + { + if ( pIter->Name == "FormatStringId" ) + { + sal_uLong nTmp; + if ( pIter->Value >>= nTmp ) + pasteFormat(static_cast<SotClipboardFormatId>(nTmp)); + break; + } + } + } + } + break; + case SID_OPENDOC: + { + Reference < XDispatchProvider > xProv( getFrame(), UNO_QUERY ); + if ( xProv.is() ) + { + URL aURL; + aURL.Complete = ".uno:Open"; + + if ( m_xUrlTransformer.is() ) + m_xUrlTransformer->parseStrict( aURL ); + Reference < XDispatch > xDisp = xProv->queryDispatch( aURL, OUString(), 0 ); + if ( xDisp.is() ) + xDisp->dispatch( aURL, Sequence < PropertyValue >() ); + } + } + break; + case ID_BROWSER_SAVEDOC: + { + Reference< XStorable > xStore( m_xModel, UNO_QUERY_THROW ); + try + { + xStore->store(); + } + catch( const Exception& ) + { + lcl_handleException_nothrow( m_xModel, ::cppu::getCaughtException() ); + } + } + break; + + case ID_BROWSER_SAVEASDOC: + { + OUString sUrl; + if ( m_xModel.is() ) + sUrl = m_xModel->getURL(); + if ( sUrl.isEmpty() ) + sUrl = SvtPathOptions().GetWorkPath(); + + ::sfx2::FileDialogHelper aFileDlg( + ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, getFrameWeld()); + aFileDlg.SetDisplayDirectory( sUrl ); + + std::shared_ptr<const SfxFilter> pFilter = getStandardDatabaseFilter(); + if ( pFilter ) + { + aFileDlg.AddFilter(pFilter->GetUIName(),pFilter->GetDefaultExtension()); + aFileDlg.SetCurrentFilter(pFilter->GetUIName()); + } + + if ( aFileDlg.Execute() != ERRCODE_NONE ) + break; + + Reference<XStorable> xStore( m_xModel, UNO_QUERY_THROW ); + INetURLObject aURL( aFileDlg.GetPath() ); + try + { + xStore->storeAsURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), Sequence< PropertyValue >() ); + } + catch( const Exception& ) + { + lcl_handleException_nothrow( m_xModel, ::cppu::getCaughtException() ); + } + + /*updateTitle();*/ + m_bCurrentlyModified = false; + InvalidateFeature(ID_BROWSER_SAVEDOC); + if ( getContainer()->getElementType() == E_NONE ) + { + getContainer()->selectContainer(E_NONE); + getContainer()->selectContainer(E_TABLE); + // #i95524# + getContainer()->Invalidate(); + refreshTables(); + } + + } + break; + case ID_BROWSER_SORTUP: + getContainer()->sortUp(); + InvalidateFeature(ID_BROWSER_SORTDOWN); + break; + case ID_BROWSER_SORTDOWN: + getContainer()->sortDown(); + InvalidateFeature(ID_BROWSER_SORTUP); + break; + + case ID_NEW_TABLE_DESIGN_AUTO_PILOT: + case ID_NEW_VIEW_DESIGN_AUTO_PILOT: + case ID_APP_NEW_QUERY_AUTO_PILOT: + case SID_DB_FORM_NEW_PILOT: + case SID_REPORT_CREATE_REPWIZ_PRE_SEL: + case SID_APP_NEW_REPORT_PRE_SEL: + case SID_FORM_CREATE_REPWIZ_PRE_SEL: + case ID_DOCUMENT_CREATE_REPWIZ: + case SID_APP_NEW_FORM: + case SID_APP_NEW_REPORT: + case ID_NEW_QUERY_SQL: + case ID_NEW_QUERY_DESIGN: + case ID_NEW_TABLE_DESIGN: + { + ElementType eType = E_TABLE; + bool bAutoPilot = false; + ::comphelper::NamedValueCollection aCreationArgs; + + switch( _nId ) + { + case SID_DB_FORM_NEW_PILOT: + case SID_FORM_CREATE_REPWIZ_PRE_SEL: + bAutoPilot = true; + [[fallthrough]]; + case SID_APP_NEW_FORM: + eType = E_FORM; + break; + case ID_DOCUMENT_CREATE_REPWIZ: + case SID_REPORT_CREATE_REPWIZ_PRE_SEL: + bAutoPilot = true; + [[fallthrough]]; + case SID_APP_NEW_REPORT: + case SID_APP_NEW_REPORT_PRE_SEL: + eType = E_REPORT; + break; + case ID_APP_NEW_QUERY_AUTO_PILOT: + bAutoPilot = true; + eType = E_QUERY; + break; + case ID_NEW_QUERY_DESIGN: + aCreationArgs.put( OUString(PROPERTY_GRAPHICAL_DESIGN), true ); + [[fallthrough]]; + case ID_NEW_QUERY_SQL: + eType = E_QUERY; + break; + case ID_NEW_TABLE_DESIGN_AUTO_PILOT: + bAutoPilot = true; + [[fallthrough]]; + case ID_NEW_TABLE_DESIGN: + break; + default: + OSL_FAIL("illegal switch call!"); + } + if ( bAutoPilot ) + getContainer()->PostUserEvent( LINK( this, OApplicationController, OnCreateWithPilot ), reinterpret_cast< void* >( eType ) ); + else + { + Reference< XComponent > xDocDefinition; + newElement( eType, aCreationArgs, xDocDefinition ); + } + } + break; + case SID_APP_NEW_FOLDER: + { + ElementType eType = getContainer()->getElementType(); + OUString sName = getContainer()->getQualifiedName( nullptr ); + insertHierachyElement(eType,sName); + } + break; + case ID_NEW_VIEW_DESIGN: + case SID_DB_NEW_VIEW_SQL: + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() ) + { + QueryDesigner aDesigner( getORB(), this, getFrame(), true ); + + ::comphelper::NamedValueCollection aCreationArgs; + aCreationArgs.put( OUString(PROPERTY_GRAPHICAL_DESIGN), ID_NEW_VIEW_DESIGN == _nId ); + + const Reference< XDataSource > xDataSource( m_xDataSource, UNO_QUERY ); + const Reference< XComponent > xComponent = aDesigner.createNew( xDataSource, aCreationArgs ); + onDocumentOpened( OUString(), E_QUERY, E_OPEN_DESIGN, xComponent, nullptr ); + } + } + break; + case SID_DB_APP_DELETE: + case SID_DB_APP_TABLE_DELETE: + case SID_DB_APP_QUERY_DELETE: + case SID_DB_APP_FORM_DELETE: + case SID_DB_APP_REPORT_DELETE: + deleteEntries(); + break; + case SID_DB_APP_RENAME: + case SID_DB_APP_TABLE_RENAME: + case SID_DB_APP_QUERY_RENAME: + case SID_DB_APP_FORM_RENAME: + case SID_DB_APP_REPORT_RENAME: + renameEntry(); + break; + case SID_DB_APP_EDIT: + case SID_DB_APP_EDIT_SQL_VIEW: + case SID_DB_APP_TABLE_EDIT: + case SID_DB_APP_QUERY_EDIT: + case SID_DB_APP_FORM_EDIT: + case SID_DB_APP_REPORT_EDIT: + doAction( _nId, E_OPEN_DESIGN ); + break; + case SID_DB_APP_OPEN: + case SID_DB_APP_TABLE_OPEN: + case SID_DB_APP_QUERY_OPEN: + case SID_DB_APP_FORM_OPEN: + case SID_DB_APP_REPORT_OPEN: + doAction( _nId, E_OPEN_NORMAL ); + break; + case SID_DB_APP_CONVERTTOVIEW: + doAction( _nId, E_OPEN_NORMAL ); + break; + case SID_SELECTALL: + getContainer()->selectAll(); + InvalidateAll(); + break; + case SID_DB_APP_DSRELDESIGN: + { + Reference< XComponent > xRelationDesigner; + if ( !m_pSubComponentManager->activateSubFrame( OUString(), SID_DB_APP_DSRELDESIGN, E_OPEN_DESIGN, xRelationDesigner ) ) + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() ) + { + RelationDesigner aDesigner( getORB(), this, m_aCurrentFrame.getFrame() ); + + const Reference< XDataSource > xDataSource( m_xDataSource, UNO_QUERY ); + const Reference< XComponent > xComponent = aDesigner.createNew( xDataSource ); + onDocumentOpened( OUString(), SID_DB_APP_DSRELDESIGN, E_OPEN_DESIGN, xComponent, nullptr ); + } + } + } + break; + case SID_DB_APP_DSUSERADMIN: + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() ) + openDialog("com.sun.star.sdb.UserAdministrationDialog"); + } + break; + case SID_DB_APP_TABLEFILTER: + // opens the table filter dialog for the selected data source + openDialog( "com.sun.star.sdb.TableFilterDialog" ); + askToReconnect(); + break; + case SID_DB_APP_REFRESH_TABLES: + refreshTables(); + break; + case SID_DB_APP_DSPROPS: + // opens the administration dialog for the selected data source + openDialog( "com.sun.star.sdb.DatasourceAdministrationDialog" ); + askToReconnect(); + break; + case SID_DB_APP_DSADVANCED_SETTINGS: + openDialog("com.sun.star.sdb.AdvancedDatabaseSettingsDialog"); + askToReconnect(); + break; + case SID_DB_APP_DSCONNECTION_TYPE: + openDialog("com.sun.star.sdb.DataSourceTypeChangeDialog"); + askToReconnect(); + break; + case ID_DIRECT_SQL: + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() ) + // opens the DirectSQLDialog to execute hand made sql statements. + openDialog( SERVICE_SDB_DIRECTSQLDIALOG ); + } + break; + case SID_DB_APP_VIEW_TABLES: + m_aSelectContainerEvent.Call( reinterpret_cast< void* >( E_TABLE ) ); + break; + case SID_DB_APP_VIEW_QUERIES: + m_aSelectContainerEvent.Call( reinterpret_cast< void* >( E_QUERY ) ); + break; + case SID_DB_APP_VIEW_FORMS: + m_aSelectContainerEvent.Call( reinterpret_cast< void* >( E_FORM ) ); + break; + case SID_DB_APP_VIEW_REPORTS: + m_aSelectContainerEvent.Call( reinterpret_cast< void* >( E_REPORT ) ); + break; + case SID_DB_APP_DISABLE_PREVIEW: + m_ePreviewMode = E_PREVIEWNONE; + getContainer()->switchPreview(m_ePreviewMode); + break; + case SID_DB_APP_VIEW_DOCINFO_PREVIEW: + m_ePreviewMode = E_DOCUMENTINFO; + getContainer()->switchPreview(m_ePreviewMode); + break; + case SID_DB_APP_VIEW_DOC_PREVIEW: + m_ePreviewMode = E_DOCUMENT; + getContainer()->switchPreview(m_ePreviewMode); + break; + case SID_MAIL_SENDDOC: + { + SfxMailModel aSendMail; + if ( aSendMail.AttachDocument(getModel(), OUString()) == SfxMailModel::SEND_MAIL_OK ) + aSendMail.Send( getFrame() ); + } + break; + case SID_DB_APP_SENDREPORTASMAIL: + doAction( _nId, E_OPEN_FOR_MAIL ); + break; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + InvalidateFeature(_nId); +} + +void OApplicationController::describeSupportedFeatures() +{ + OGenericUnoController::describeSupportedFeatures(); + + implDescribeSupportedFeature( ".uno:Save", ID_BROWSER_SAVEDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:SaveAs", ID_BROWSER_SAVEASDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:SendMail", SID_MAIL_SENDDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:DBSendReportAsMail",SID_DB_APP_SENDREPORTASMAIL, + CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:DBSendReportToWriter",SID_DB_APP_SENDREPORTTOWRITER, + CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:DBNewForm", SID_APP_NEW_FORM, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewFolder", SID_APP_NEW_FOLDER, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewFormAutoPilot", SID_DB_FORM_NEW_PILOT, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewFormAutoPilotWithPreSelection", + SID_FORM_CREATE_REPWIZ_PRE_SEL, + CommandGroup::APPLICATION ); + + implDescribeSupportedFeature( ".uno:DBNewReport", SID_APP_NEW_REPORT, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewReportAutoPilot", + ID_DOCUMENT_CREATE_REPWIZ, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewReportAutoPilotWithPreSelection", + SID_REPORT_CREATE_REPWIZ_PRE_SEL, + CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:DBNewQuery", ID_NEW_QUERY_DESIGN, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewQuerySql", ID_NEW_QUERY_SQL, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewQueryAutoPilot",ID_APP_NEW_QUERY_AUTO_PILOT, + CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewTable", ID_NEW_TABLE_DESIGN, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewTableAutoPilot",ID_NEW_TABLE_DESIGN_AUTO_PILOT, + CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewView", ID_NEW_VIEW_DESIGN, CommandGroup::INSERT ); + implDescribeSupportedFeature( ".uno:DBNewViewSQL", SID_DB_NEW_VIEW_SQL, CommandGroup::INSERT ); + + implDescribeSupportedFeature( ".uno:DBDelete", SID_DB_APP_DELETE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Delete", SID_DB_APP_DELETE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBRename", SID_DB_APP_RENAME, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBEdit", SID_DB_APP_EDIT, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBEditSqlView", SID_DB_APP_EDIT_SQL_VIEW, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBOpen", SID_DB_APP_OPEN, CommandGroup::EDIT ); + + implDescribeSupportedFeature( ".uno:DBTableDelete", SID_DB_APP_TABLE_DELETE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBTableRename", SID_DB_APP_TABLE_RENAME, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBTableEdit", SID_DB_APP_TABLE_EDIT, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBTableOpen", SID_DB_APP_TABLE_OPEN, CommandGroup::EDIT ); + + implDescribeSupportedFeature( ".uno:DBQueryDelete", SID_DB_APP_QUERY_DELETE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBQueryRename", SID_DB_APP_QUERY_RENAME, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBQueryEdit", SID_DB_APP_QUERY_EDIT, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBQueryOpen", SID_DB_APP_QUERY_OPEN, CommandGroup::EDIT ); + + implDescribeSupportedFeature( ".uno:DBFormDelete", SID_DB_APP_FORM_DELETE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBFormRename", SID_DB_APP_FORM_RENAME, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBFormEdit", SID_DB_APP_FORM_EDIT, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBFormOpen", SID_DB_APP_FORM_OPEN, CommandGroup::EDIT ); + + implDescribeSupportedFeature( ".uno:DBReportDelete", SID_DB_APP_REPORT_DELETE, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBReportRename", SID_DB_APP_REPORT_RENAME, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBReportEdit", SID_DB_APP_REPORT_EDIT, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBReportOpen", SID_DB_APP_REPORT_OPEN, CommandGroup::EDIT ); + + implDescribeSupportedFeature( ".uno:SelectAll", SID_SELECTALL, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Undo", ID_BROWSER_UNDO, CommandGroup::EDIT ); + + implDescribeSupportedFeature( ".uno:Sortup", ID_BROWSER_SORTUP, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:SortDown", ID_BROWSER_SORTDOWN, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBRelationDesign", SID_DB_APP_DSRELDESIGN, CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:DBUserAdmin", SID_DB_APP_DSUSERADMIN, CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:DBTableFilter", SID_DB_APP_TABLEFILTER, CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:DBDSProperties", SID_DB_APP_DSPROPS, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBDSConnectionType", SID_DB_APP_DSCONNECTION_TYPE, + CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBDSAdvancedSettings", + SID_DB_APP_DSADVANCED_SETTINGS, + CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:PasteSpecial", SID_DB_APP_PASTE_SPECIAL, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBConvertToView", SID_DB_APP_CONVERTTOVIEW, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBRefreshTables", SID_DB_APP_REFRESH_TABLES, CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:DBDirectSQL", ID_DIRECT_SQL, CommandGroup::APPLICATION ); + implDescribeSupportedFeature( ".uno:DBViewTables", SID_DB_APP_VIEW_TABLES, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBViewQueries", SID_DB_APP_VIEW_QUERIES, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBViewForms", SID_DB_APP_VIEW_FORMS, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBViewReports", SID_DB_APP_VIEW_REPORTS, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBDisablePreview", SID_DB_APP_DISABLE_PREVIEW,CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBShowDocInfoPreview", + SID_DB_APP_VIEW_DOCINFO_PREVIEW, + CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBShowDocPreview", SID_DB_APP_VIEW_DOC_PREVIEW, + CommandGroup::VIEW ); + + implDescribeSupportedFeature( ".uno:OpenUrl", SID_OPENURL, CommandGroup::APPLICATION ); + + // this one should not appear under Tools->Customize->Keyboard + implDescribeSupportedFeature( ".uno:DBNewReportWithPreSelection", + SID_APP_NEW_REPORT_PRE_SEL ); + implDescribeSupportedFeature( ".uno:DBDSImport", SID_DB_APP_DSIMPORT); + implDescribeSupportedFeature( ".uno:DBDSExport", SID_DB_APP_DSEXPORT); + implDescribeSupportedFeature( ".uno:DBDBAdmin", SID_DB_APP_DBADMIN); + + // status info + implDescribeSupportedFeature( ".uno:DBStatusType", SID_DB_APP_STATUS_TYPE); + implDescribeSupportedFeature( ".uno:DBStatusDBName", SID_DB_APP_STATUS_DBNAME); + implDescribeSupportedFeature( ".uno:DBStatusUserName", SID_DB_APP_STATUS_USERNAME); + implDescribeSupportedFeature( ".uno:DBStatusHostName", SID_DB_APP_STATUS_HOSTNAME); +} + +OApplicationView* OApplicationController::getContainer() const +{ + return static_cast< OApplicationView* >( getView() ); +} + +// css::container::XContainerListener +void SAL_CALL OApplicationController::elementInserted( const ContainerEvent& _rEvent ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + Reference< XContainer > xContainer(_rEvent.Source, UNO_QUERY); + if ( std::find(m_aCurrentContainers.begin(),m_aCurrentContainers.end(),xContainer) == m_aCurrentContainers.end() ) + return; + + OSL_ENSURE(getContainer(),"View is NULL! -> GPF"); + if ( !getContainer() ) + return; + + OUString sName; + _rEvent.Accessor >>= sName; + ElementType eType = getElementType(xContainer); + + switch( eType ) + { + case E_TABLE: + ensureConnection(); + break; + case E_FORM: + case E_REPORT: + { + Reference< XContainer > xSubContainer(_rEvent.Element,UNO_QUERY); + if ( xSubContainer.is() ) + containerFound(xSubContainer); + } + break; + default: + break; + } + getContainer()->elementAdded(eType,sName,_rEvent.Element); +} + +void SAL_CALL OApplicationController::elementRemoved( const ContainerEvent& _rEvent ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + Reference< XContainer > xContainer(_rEvent.Source, UNO_QUERY); + if ( std::find(m_aCurrentContainers.begin(),m_aCurrentContainers.end(),xContainer) == m_aCurrentContainers.end() ) + return; + + OSL_ENSURE(getContainer(),"View is NULL! -> GPF"); + OUString sName; + _rEvent.Accessor >>= sName; + ElementType eType = getElementType(xContainer); + switch( eType ) + { + case E_TABLE: + ensureConnection(); + break; + case E_FORM: + case E_REPORT: + { + Reference<XContent> xContent(xContainer,UNO_QUERY); + if ( xContent.is() ) + { + sName = xContent->getIdentifier()->getContentIdentifier() + "/" + sName; + } + } + break; + default: + break; + } + getContainer()->elementRemoved(eType,sName); +} + +void SAL_CALL OApplicationController::elementReplaced( const ContainerEvent& _rEvent ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + Reference< XContainer > xContainer(_rEvent.Source, UNO_QUERY); + if ( std::find(m_aCurrentContainers.begin(),m_aCurrentContainers.end(),xContainer) == m_aCurrentContainers.end() ) + return; + + OSL_ENSURE(getContainer(),"View is NULL! -> GPF"); + OUString sName; + try + { + _rEvent.Accessor >>= sName; + Reference<XPropertySet> xProp(_rEvent.Element,UNO_QUERY); + OUString sNewName; + + ElementType eType = getElementType(xContainer); + switch( eType ) + { + case E_TABLE: + { + ensureConnection(); + if ( xProp.is() && m_xMetaData.is() ) + sNewName = ::dbaui::composeTableName( m_xMetaData, xProp, ::dbtools::EComposeRule::InDataManipulation, false ); + } + break; + case E_FORM: + case E_REPORT: + { + Reference<XContent> xContent(xContainer,UNO_QUERY); + if ( xContent.is() ) + { + sName = xContent->getIdentifier()->getContentIdentifier() + "/" + sName; + } + } + break; + default: + break; + } + // getContainer()->elementReplaced(getContainer()->getElementType(),sName,sNewName); + } + catch( Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +namespace +{ + OUString lcl_getToolBarResource(ElementType _eType) + { + OUString sToolbar; + switch(_eType) + { + case E_TABLE: + sToolbar = "private:resource/toolbar/tableobjectbar"; + break; + case E_QUERY: + sToolbar = "private:resource/toolbar/queryobjectbar"; + break; + case E_FORM: + sToolbar = "private:resource/toolbar/formobjectbar"; + break; + case E_REPORT: + sToolbar = "private:resource/toolbar/reportobjectbar"; + break; + case E_NONE: + break; + default: + OSL_FAIL("Invalid ElementType!"); + break; + } + return sToolbar; + } +} + +bool OApplicationController::onContainerSelect(ElementType _eType) +{ + OSL_ENSURE(getContainer(),"View is NULL! -> GPF"); + + if ( m_eCurrentType != _eType && _eType != E_NONE ) + { + SelectionGuard aSelGuard( *m_pSelectionNotifier ); + + if ( _eType == E_TABLE ) + { + try + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() && getContainer()->getDetailView() ) + { + getContainer()->getDetailView()->createTablesPage(xConnection); + Reference<XTablesSupplier> xTabSup(xConnection,UNO_QUERY); + if ( xTabSup.is() ) + addContainerListener(xTabSup->getTables()); + } + else + { + return false; + } + } + catch( const Exception& ) + { + return false; + } + } + else if ( _eType == E_QUERY ) + { + // tdf#126578: retrieve connection to be able to call "Create as View" + ensureConnection(); + } + Reference< XLayoutManager > xLayoutManager = getLayoutManager( getFrame() ); + if ( xLayoutManager.is() ) + { + OUString sToolbar = lcl_getToolBarResource(_eType); + OUString sDestroyToolbar = lcl_getToolBarResource(m_eCurrentType); + + xLayoutManager->lock(); + xLayoutManager->destroyElement( sDestroyToolbar ); + if ( !sToolbar.isEmpty() ) + { + xLayoutManager->createElement( sToolbar ); + xLayoutManager->requestElement( sToolbar ); + } + xLayoutManager->unlock(); + xLayoutManager->doLayout(); + } + + if ( _eType != E_TABLE && getContainer()->getDetailView() ) + { + Reference< XNameAccess > xContainer = getElements(_eType); + addContainerListener(xContainer); + getContainer()->getDetailView()->createPage(_eType,xContainer); + } + + SelectionByElementType::const_iterator pendingSelection = m_aPendingSelection.find( _eType ); + if ( pendingSelection != m_aPendingSelection.end() ) + { + getContainer()->selectElements( comphelper::containerToSequence(pendingSelection->second) ); + + m_aPendingSelection.erase( pendingSelection ); + } + + InvalidateAll(); + } + m_eCurrentType = _eType; + + return true; +} + +bool OApplicationController::onEntryDoubleClick( SvTreeListBox const & _rTree ) +{ + if ( getContainer() && getContainer()->isLeaf( _rTree.GetHdlEntry() ) ) + { + try + { + // opens a new frame with either the table or the query or report or form or view + openElementWithArguments( + getContainer()->getQualifiedName( _rTree.GetHdlEntry() ), + getContainer()->getElementType(), + E_OPEN_NORMAL, + 0, + ::comphelper::NamedValueCollection() ); + return true; // handled + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + return false; // not handled +} + +bool OApplicationController::impl_isAlterableView_nothrow( const OUString& _rTableOrViewName ) const +{ + OSL_PRECOND( m_xDataSourceConnection.is(), "OApplicationController::impl_isAlterableView_nothrow: no connection!" ); + + bool bIsAlterableView( false ); + try + { + Reference< XViewsSupplier > xViewsSupp( m_xDataSourceConnection, UNO_QUERY ); + Reference< XNameAccess > xViews; + if ( xViewsSupp.is() ) + xViews = xViewsSupp->getViews(); + + Reference< XAlterView > xAsAlterableView; + if ( xViews.is() && xViews->hasByName( _rTableOrViewName ) ) + xAsAlterableView.set( xViews->getByName( _rTableOrViewName ), UNO_QUERY ); + + bIsAlterableView = xAsAlterableView.is(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bIsAlterableView; +} + +Reference< XComponent > OApplicationController::openElementWithArguments( const OUString& _sName, ElementType _eType, + ElementOpenMode _eOpenMode, sal_uInt16 _nInstigatorCommand, const ::comphelper::NamedValueCollection& _rAdditionalArguments ) +{ + OSL_PRECOND( getContainer(), "OApplicationController::openElementWithArguments: no view!" ); + if ( !getContainer() ) + return nullptr; + + Reference< XComponent > xRet; + if ( _eOpenMode == E_OPEN_DESIGN ) + { + // https://bz.apache.org/ooo/show_bug.cgi?id=30382 + getContainer()->showPreview(nullptr); + } + + bool isStandaloneDocument = false; + switch ( _eType ) + { + case E_REPORT: + if ( _eOpenMode != E_OPEN_DESIGN ) + { + // reports which are opened in a mode other than design are no sub components of our application + // component, but standalone documents. + isStandaloneDocument = true; + } + [[fallthrough]]; + case E_FORM: + { + if ( isStandaloneDocument || !m_pSubComponentManager->activateSubFrame( _sName, _eType, _eOpenMode, xRet ) ) + { + std::unique_ptr< OLinkedDocumentsAccess > aHelper = getDocumentsAccess( _eType ); + if ( !aHelper->isConnected() ) + break; + + Reference< XComponent > xDefinition; + xRet = aHelper->open( _sName, xDefinition, _eOpenMode, _rAdditionalArguments ); + + if ( !isStandaloneDocument ) + onDocumentOpened( _sName, _eType, _eOpenMode, xRet, xDefinition ); + } + } + break; + + case E_QUERY: + case E_TABLE: + { + if ( !m_pSubComponentManager->activateSubFrame( _sName, _eType, _eOpenMode, xRet ) ) + { + SharedConnection xConnection( ensureConnection() ); + if ( !xConnection.is() ) + break; + + std::unique_ptr< DatabaseObjectView > pDesigner; + ::comphelper::NamedValueCollection aArguments( _rAdditionalArguments ); + + Any aDataSource; + if ( _eOpenMode == E_OPEN_DESIGN ) + { + bool bAddViewTypeArg = false; + + if ( _eType == E_TABLE ) + { + if ( impl_isAlterableView_nothrow( _sName ) ) + { + pDesigner.reset( new QueryDesigner( getORB(), this, m_aCurrentFrame.getFrame(), true ) ); + bAddViewTypeArg = true; + } + else + { + pDesigner.reset( new TableDesigner( getORB(), this, m_aCurrentFrame.getFrame() ) ); + } + } + else if ( _eType == E_QUERY ) + { + pDesigner.reset( new QueryDesigner( getORB(), this, m_aCurrentFrame.getFrame(), false ) ); + bAddViewTypeArg = true; + } + aDataSource <<= m_xDataSource; + + if ( bAddViewTypeArg ) + { + const bool bQueryGraphicalMode =( _nInstigatorCommand != SID_DB_APP_EDIT_SQL_VIEW ); + aArguments.put( OUString(PROPERTY_GRAPHICAL_DESIGN), bQueryGraphicalMode ); + } + + } + else + { + pDesigner.reset( new ResultSetBrowser( getORB(), this, m_aCurrentFrame.getFrame(), _eType == E_TABLE ) ); + + if ( !aArguments.has( OUString(PROPERTY_SHOWMENU) ) ) + aArguments.put( OUString(PROPERTY_SHOWMENU), makeAny( true ) ); + + aDataSource <<= getDatabaseName(); + } + + xRet.set( pDesigner->openExisting( aDataSource, _sName, aArguments ) ); + onDocumentOpened( _sName, _eType, _eOpenMode, xRet, nullptr ); + } + } + break; + + default: + OSL_FAIL( "OApplicationController::openElement: illegal object type!" ); + break; + } + return xRet; +} + +IMPL_LINK( OApplicationController, OnSelectContainer, void*, _pType, void ) +{ + ElementType eType = static_cast<ElementType>(reinterpret_cast< sal_IntPtr >( _pType )); + if (getContainer()) + getContainer()->selectContainer(eType); +} + +IMPL_LINK( OApplicationController, OnCreateWithPilot, void*, _pType, void ) +{ + ElementType eType = static_cast<ElementType>(reinterpret_cast< sal_IntPtr >( _pType )); + newElementWithPilot( eType ); +} + +void OApplicationController::newElementWithPilot( ElementType _eType ) +{ + utl::CloseVeto aKeepDoc( getFrame() ); + // prevent the document being closed while the wizard is open + + OSL_ENSURE( getContainer(), "OApplicationController::newElementWithPilot: without a view?" ); + + switch ( _eType ) + { + case E_REPORT: + case E_FORM: + { + std::unique_ptr<OLinkedDocumentsAccess> aHelper = getDocumentsAccess(_eType); + if ( aHelper->isConnected() ) + { + sal_Int32 nCommandType = -1; + const OUString sCurrentSelected( getCurrentlySelectedName( nCommandType ) ); + if ( E_REPORT == _eType ) + aHelper->newReportWithPilot( nCommandType, sCurrentSelected ); + else + aHelper->newFormWithPilot( nCommandType, sCurrentSelected ); + } + } + break; + case E_QUERY: + case E_TABLE: + { + std::unique_ptr<OLinkedDocumentsAccess> aHelper = getDocumentsAccess(_eType); + if ( aHelper->isConnected() ) + { + if ( E_QUERY == _eType ) + aHelper->newQueryWithPilot(); + else + aHelper->newTableWithPilot(); + } + } + break; + case E_NONE: + break; + } + + // no need for onDocumentOpened, the table wizard opens the created table by using + // XDatabaseDocumentUI::loadComponent method. +} + +Reference< XComponent > OApplicationController::newElement( ElementType _eType, const ::comphelper::NamedValueCollection& i_rAdditionalArguments, + Reference< XComponent >& o_rDocumentDefinition ) +{ + OSL_ENSURE(getContainer(),"View is NULL! -> GPF"); + + Reference< XComponent > xComponent; + o_rDocumentDefinition.clear(); + + switch ( _eType ) + { + case E_FORM: + case E_REPORT: + { + std::unique_ptr<OLinkedDocumentsAccess> aHelper = getDocumentsAccess( _eType ); + if ( !aHelper->isConnected() ) + break; + + xComponent = aHelper->newDocument( _eType == E_FORM ? ID_FORM_NEW_TEXT : ID_REPORT_NEW_TEXT, i_rAdditionalArguments, o_rDocumentDefinition ); + } + break; + + case E_QUERY: + case E_TABLE: + { + std::unique_ptr< DatabaseObjectView > pDesigner; + SharedConnection xConnection( ensureConnection() ); + if ( !xConnection.is() ) + break; + + if ( _eType == E_TABLE ) + { + pDesigner.reset( new TableDesigner( getORB(), this, getFrame() ) ); + } + else if ( _eType == E_QUERY ) + { + pDesigner.reset( new QueryDesigner( getORB(), this, getFrame(), false ) ); + } + + Reference< XDataSource > xDataSource( m_xDataSource, UNO_QUERY ); + xComponent = pDesigner->createNew( xDataSource, i_rAdditionalArguments ); + } + break; + + default: + OSL_FAIL( "OApplicationController::newElement: illegal type!" ); + break; + } + + if ( xComponent.is() ) + onDocumentOpened( OUString(), _eType, E_OPEN_DESIGN, xComponent, o_rDocumentDefinition ); + + return xComponent; +} + +void OApplicationController::addContainerListener(const Reference<XNameAccess>& _xCollection) +{ + try + { + Reference< XContainer > xCont(_xCollection, UNO_QUERY); + if ( xCont.is() ) + { + // add as listener to get notified if elements are inserted or removed + TContainerVector::const_iterator aFind = std::find(m_aCurrentContainers.begin(),m_aCurrentContainers.end(),xCont); + if ( aFind == m_aCurrentContainers.end() ) + { + xCont->addContainerListener(this); + m_aCurrentContainers.push_back(xCont); + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OApplicationController::renameEntry() +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + OSL_ENSURE(getContainer(),"View is NULL! -> GPF"); + std::vector< OUString> aList; + getSelectionElementNames(aList); + + Reference< XNameAccess > xContainer = getElements(getContainer()->getElementType()); + OSL_ENSURE(aList.size() == 1,"Invalid rename call here. More than one element!"); + if ( aList.empty() ) + return; + + try + { + if ( xContainer.is() ) + { + std::unique_ptr< IObjectNameCheck > pNameChecker; + std::unique_ptr<OSaveAsDlg> xDialog; + + Reference<XRename> xRename; + const ElementType eType = getContainer()->getElementType(); + switch( eType ) + { + case E_FORM: + case E_REPORT: + { + Reference<XHierarchicalNameContainer> xHNames(xContainer, UNO_QUERY); + if ( xHNames.is() ) + { + OUString sLabel; + if ( eType == E_FORM ) + sLabel = DBA_RES(STR_FRM_LABEL); + else + sLabel = DBA_RES(STR_RPT_LABEL); + + OUString sName = *aList.begin(); + if ( xHNames->hasByHierarchicalName(sName) ) + { + xRename.set(xHNames->getByHierarchicalName(sName),UNO_QUERY); + Reference<XChild> xChild(xRename,UNO_QUERY); + if ( xChild.is() ) + { + Reference<XHierarchicalNameContainer> xParent(xChild->getParent(),UNO_QUERY); + if ( xParent.is() ) + { + xHNames = xParent; + Reference<XPropertySet>(xRename,UNO_QUERY_THROW)->getPropertyValue(PROPERTY_NAME) >>= sName; + } + } + pNameChecker.reset( new HierarchicalNameCheck( xHNames.get(), OUString() ) ); + xDialog.reset(new OSaveAsDlg( + getFrameWeld(), getORB(), sName, sLabel, *pNameChecker, SADFlags::TitleRename)); + } + } + } + break; + case E_TABLE: + ensureConnection(); + if ( !getConnection().is() ) + break; + [[fallthrough]]; + case E_QUERY: + if ( xContainer->hasByName(*aList.begin()) ) + { + xRename.set(xContainer->getByName(*aList.begin()),UNO_QUERY); + sal_Int32 nCommandType = eType == E_QUERY ? CommandType::QUERY : CommandType::TABLE; + + ensureConnection(); + pNameChecker.reset( new DynamicTableOrQueryNameCheck( getConnection(), nCommandType ) ); + xDialog.reset(new OSaveAsDlg(getFrameWeld(), nCommandType, getORB(), getConnection(), + *aList.begin(), *pNameChecker, SADFlags::TitleRename)); + } + break; + default: + break; + } + + if (xRename.is() && xDialog) + { + + bool bTryAgain = true; + while( bTryAgain ) + { + if (xDialog->run() == RET_OK) + { + try + { + OUString sNewName; + if ( eType == E_TABLE ) + { + OUString sName = xDialog->getName(); + OUString sCatalog = xDialog->getCatalog(); + OUString sSchema = xDialog->getSchema(); + + sNewName = ::dbtools::composeTableName( m_xMetaData, sCatalog, sSchema, sName, false, ::dbtools::EComposeRule::InDataManipulation ); + } + else + sNewName = xDialog->getName(); + + OUString sOldName = *aList.begin(); + if ( eType == E_FORM || eType == E_REPORT ) + { + Reference<XContent> xContent(xRename,UNO_QUERY); + if ( xContent.is() ) + { + sOldName = xContent->getIdentifier()->getContentIdentifier(); + } + } + + xRename->rename(sNewName); + + if ( eType == E_TABLE ) + { + Reference<XPropertySet> xProp(xRename,UNO_QUERY); + sNewName = ::dbaui::composeTableName( m_xMetaData, xProp, ::dbtools::EComposeRule::InDataManipulation, false ); + } + getContainer()->elementReplaced( eType , sOldName, sNewName ); + + bTryAgain = false; + } + catch(const SQLException& ) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + + } + catch(const ElementExistException& e) + { + OUString sMsg(DBA_RES(STR_NAME_ALREADY_EXISTS)); + showError(SQLExceptionInfo(SQLException(sMsg.replaceAll("#", e.Message), e.Context, "S1000", 0, Any()))); + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + else + bTryAgain = false; + } + } + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OApplicationController::onSelectionChanged() +{ + InvalidateAll(); + + SelectionGuard aSelGuard( *m_pSelectionNotifier ); + + OApplicationView* pView = getContainer(); + if ( !pView ) + return; + + if ( pView->getSelectionCount() == 1 ) + { + const ElementType eType = pView->getElementType(); + if ( pView->isALeafSelected() ) + { + const OUString sName = pView->getQualifiedName( nullptr /* means 'first selected' */ ); + showPreviewFor( eType, sName ); + } + } +} + +void OApplicationController::showPreviewFor(const ElementType _eType,const OUString& _sName) +{ + if ( m_ePreviewMode == E_PREVIEWNONE ) + return; + + OApplicationView* pView = getContainer(); + if ( !pView ) + return; + + try + { + switch( _eType ) + { + case E_FORM: + case E_REPORT: + { + Reference< XHierarchicalNameAccess > xContainer( getElements( _eType ), UNO_QUERY_THROW ); + Reference< XContent> xContent( xContainer->getByHierarchicalName( _sName ), UNO_QUERY_THROW ); + pView->showPreview( xContent ); + } + break; + + case E_TABLE: + case E_QUERY: + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() ) + pView->showPreview( getDatabaseName(), xConnection, _sName, _eType == E_TABLE ); + } + return; + + default: + OSL_FAIL( "OApplicationController::showPreviewFor: unexpected element type!" ); + break; + } + } + catch( const SQLException& ) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +IMPL_LINK_NOARG(OApplicationController, OnClipboardChanged, TransferableDataHelper*, void) +{ + OnInvalidateClipboard(); +} + +void OApplicationController::OnInvalidateClipboard() +{ + InvalidateFeature(ID_BROWSER_CUT); + InvalidateFeature(ID_BROWSER_COPY); + InvalidateFeature(ID_BROWSER_PASTE); + InvalidateFeature(SID_DB_APP_PASTE_SPECIAL); +} + +void OApplicationController::onCopyEntry() +{ + Execute(ID_BROWSER_COPY,Sequence<PropertyValue>()); +} + +void OApplicationController::onPasteEntry() +{ + Execute(ID_BROWSER_PASTE,Sequence<PropertyValue>()); +} + +void OApplicationController::onDeleteEntry() +{ + ElementType eType = getContainer()->getElementType(); + sal_uInt16 nId = 0; + switch(eType) + { + case E_TABLE: + nId = SID_DB_APP_TABLE_DELETE; + break; + case E_QUERY: + nId = SID_DB_APP_QUERY_DELETE; + break; + case E_FORM: + nId = SID_DB_APP_FORM_DELETE; + break; + case E_REPORT: + nId = SID_DB_APP_REPORT_DELETE; + break; + default: + OSL_FAIL("Invalid ElementType!"); + break; + } + executeChecked(nId,Sequence<PropertyValue>()); +} + +OUString OApplicationController::getContextMenuResourceName( Control& /*_rControl*/ ) const +{ + return "edit"; +} + +IController& OApplicationController::getCommandController() +{ + return *this; +} + +::comphelper::OInterfaceContainerHelper2* OApplicationController::getContextMenuInterceptors() +{ + return &m_aContextMenuInterceptors; +} + +Any OApplicationController::getCurrentSelection( Control& _rControl ) const +{ + Sequence< NamedDatabaseObject > aSelection; + getContainer()->describeCurrentSelectionForControl( _rControl, aSelection ); + return makeAny( aSelection ); +} + +bool OApplicationController::requestQuickHelp( const SvTreeListEntry* /*_pEntry*/, OUString& /*_rText*/ ) const +{ + return false; +} + +bool OApplicationController::requestDrag( const Point& /*_rPosPixel*/ ) +{ + rtl::Reference<TransferableHelper> pTransfer; + if ( getContainer() && getContainer()->getSelectionCount() ) + { + try + { + pTransfer = copyObject( ); + + if ( pTransfer && getContainer()->getDetailView() ) + { + ElementType eType = getContainer()->getElementType(); + pTransfer->StartDrag( getContainer()->getDetailView()->getTreeWindow(), ((eType == E_FORM || eType == E_REPORT) ? DND_ACTION_COPYMOVE : DND_ACTION_COPY) ); + } + } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + return pTransfer.is(); +} + +sal_Int8 OApplicationController::queryDrop( const AcceptDropEvent& _rEvt, const DataFlavorExVector& _rFlavors ) +{ + sal_Int8 nActionAskedFor = _rEvt.mnAction; + // check if we're a table or query container + OApplicationView* pView = getContainer(); + if ( pView && !isDataSourceReadOnly() ) + { + ElementType eType = pView->getElementType(); + if ( eType != E_NONE && (eType != E_TABLE || !isConnectionReadOnly()) ) + { + // check for the concrete type + if(std::any_of(_rFlavors.begin(),_rFlavors.end(),TAppSupportedSotFunctor(eType))) + return DND_ACTION_COPY; + if ( eType == E_FORM || eType == E_REPORT ) + { + sal_Int8 nAction = OComponentTransferable::canExtractComponentDescriptor(_rFlavors,eType == E_FORM) ? DND_ACTION_COPY : DND_ACTION_NONE; + if ( nAction != DND_ACTION_NONE ) + { + SvTreeListEntry* pHitEntry = pView->getEntry(_rEvt.maPosPixel); + OUString sName; + if ( pHitEntry ) + { + sName = pView->getQualifiedName( pHitEntry ); + if ( !sName.isEmpty() ) + { + Reference< XHierarchicalNameAccess > xContainer(getElements(pView->getElementType()),UNO_QUERY); + if ( xContainer.is() && xContainer->hasByHierarchicalName(sName) ) + { + Reference< XHierarchicalNameAccess > xHitObject(xContainer->getByHierarchicalName(sName),UNO_QUERY); + if ( xHitObject.is() ) + nAction = nActionAskedFor & DND_ACTION_COPYMOVE; + } + else + nAction = DND_ACTION_NONE; + } + } + } + return nAction; + } + } + } + + return DND_ACTION_NONE; +} + +sal_Int8 OApplicationController::executeDrop( const ExecuteDropEvent& _rEvt ) +{ + OApplicationView* pView = getContainer(); + if ( !pView || pView->getElementType() == E_NONE ) + { + OSL_FAIL("OApplicationController::executeDrop: what the hell did queryDrop do?"); + // queryDrop should not have allowed us to reach this situation... + return DND_ACTION_NONE; + } + + // a TransferableDataHelper for accessing the dropped data + TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable); + + // reset the data of the previous async drop (if any) + if ( m_nAsyncDrop ) + Application::RemoveUserEvent(m_nAsyncDrop); + + m_nAsyncDrop = nullptr; + m_aAsyncDrop.aDroppedData.clear(); + m_aAsyncDrop.nType = pView->getElementType(); + m_aAsyncDrop.nAction = _rEvt.mnAction; + m_aAsyncDrop.bError = false; + m_aAsyncDrop.bHtml = false; + m_aAsyncDrop.aUrl.clear(); + + // loop through the available formats and see what we can do... + // first we have to check if it is our own format, if not we have to copy the stream :-( + if ( ODataAccessObjectTransferable::canExtractObjectDescriptor(aDroppedData.GetDataFlavorExVector()) ) + { + m_aAsyncDrop.aDroppedData = ODataAccessObjectTransferable::extractObjectDescriptor(aDroppedData); + + // asynchron because we some dialogs and we aren't allowed to show them while in D&D + m_nAsyncDrop = Application::PostUserEvent(LINK(this, OApplicationController, OnAsyncDrop)); + return DND_ACTION_COPY; + } + else if ( OComponentTransferable::canExtractComponentDescriptor(aDroppedData.GetDataFlavorExVector(),m_aAsyncDrop.nType == E_FORM) ) + { + m_aAsyncDrop.aDroppedData = OComponentTransferable::extractComponentDescriptor(aDroppedData); + SvTreeListEntry* pHitEntry = pView->getEntry(_rEvt.maPosPixel); + if ( pHitEntry ) + m_aAsyncDrop.aUrl = pView->getQualifiedName( pHitEntry ); + + sal_Int8 nAction = _rEvt.mnAction; + Reference<XContent> xContent; + m_aAsyncDrop.aDroppedData[DataAccessDescriptorProperty::Component] >>= xContent; + if ( xContent.is() ) + { + OUString sName = xContent->getIdentifier()->getContentIdentifier(); + sName = sName.copy(sName.indexOf('/') + 1); + if ( m_aAsyncDrop.aUrl.getLength() >= sName.getLength() && m_aAsyncDrop.aUrl.startsWith(sName) ) + { + m_aAsyncDrop.aDroppedData.clear(); + return DND_ACTION_NONE; + } + + // check if move is allowed, if another object with the same name exists only copy is allowed + Reference< XHierarchicalNameAccess > xContainer(getElements(m_aAsyncDrop.nType),UNO_QUERY); + Reference<XNameAccess> xNameAccess(xContainer,UNO_QUERY); + + if ( !m_aAsyncDrop.aUrl.isEmpty() && xContainer.is() && xContainer->hasByHierarchicalName(m_aAsyncDrop.aUrl) ) + xNameAccess.set(xContainer->getByHierarchicalName(m_aAsyncDrop.aUrl),UNO_QUERY); + + if ( xNameAccess.is() ) + { + Reference<XPropertySet> xProp(xContent,UNO_QUERY); + if ( xProp.is() ) + { + xProp->getPropertyValue(PROPERTY_NAME) >>= sName; + if ( xNameAccess.is() && xNameAccess->hasByName(sName) ) + nAction &= ~DND_ACTION_MOVE; + } + else + nAction &= ~DND_ACTION_MOVE; + } + } + if ( nAction != DND_ACTION_NONE ) + { + m_aAsyncDrop.nAction = nAction; + // asynchron because we some dialogs and we aren't allowed to show them while in D&D + m_nAsyncDrop = Application::PostUserEvent(LINK(this, OApplicationController, OnAsyncDrop)); + } + else + m_aAsyncDrop.aDroppedData.clear(); + return nAction; + } + else + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() && m_aTableCopyHelper.copyTagTable( aDroppedData, m_aAsyncDrop, xConnection ) ) + { + // asynchron because we some dialogs and we aren't allowed to show them while in D&D + m_nAsyncDrop = Application::PostUserEvent(LINK(this, OApplicationController, OnAsyncDrop)); + return DND_ACTION_COPY; + } + } + + return DND_ACTION_NONE; +} + +Reference< XModel > SAL_CALL OApplicationController::getModel() +{ + return m_xModel; +} + +void OApplicationController::onAttachedFrame() +{ + sal_Int32 nConnectedControllers( 0 ); + try + { + Reference< XModel2 > xModel( m_xModel, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumControllers( xModel->getControllers(), UNO_SET_THROW ); + while ( xEnumControllers->hasMoreElements() ) + { + Reference< XController > xController( xEnumControllers->nextElement(), UNO_QUERY_THROW ); + ++nConnectedControllers; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( nConnectedControllers > 1 ) + { // we are not the first connected controller, there were already others + return; + } + + OnFirstControllerConnected(); +} + +void OApplicationController::OnFirstControllerConnected() +{ + if ( !m_xModel.is() ) + { + OSL_FAIL( "OApplicationController::OnFirstControllerConnected: too late!" ); + } + + // if we have forms or reports which contain macros/scripts, then show a warning + // which suggests the user to migrate them to the database document + Reference< XEmbeddedScripts > xDocumentScripts( m_xModel, UNO_QUERY ); + if ( xDocumentScripts.is() ) + { + // no need to show this warning, obviously the document supports embedding scripts + // into itself, so there are no "old-style" forms/reports which have macros/scripts + // themselves + return; + } + + try + { + // If the migration just happened, but was not successful, the document is reloaded. + // In this case, we should not show the warning, again. + ::comphelper::NamedValueCollection aModelArgs( m_xModel->getArgs() ); + if ( aModelArgs.getOrDefault( "SuppressMigrationWarning", false ) ) + return; + + // also, if the document is read-only, then no migration is possible, and the + // respective menu entry is hidden. So, don't show the warning in this case, too. + if ( Reference< XStorable >( m_xModel, UNO_QUERY_THROW )->isReadonly() ) + return; + + SQLWarning aWarning; + aWarning.Message = DBA_RES(STR_SUB_DOCS_WITH_SCRIPTS); + SQLException aDetail; + aDetail.Message = DBA_RES(STR_SUB_DOCS_WITH_SCRIPTS_DETAIL); + aWarning.NextException <<= aDetail; + + Reference< XExecutableDialog > xDialog = ErrorMessageDialog::create( getORB(), "", nullptr, makeAny( aWarning ) ); + xDialog->execute(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void SAL_CALL OApplicationController::attachFrame( const Reference< XFrame > & i_rxFrame ) +{ + SolarMutexGuard aSolarGuard; // avoid deadlock in XModel calls + ::osl::MutexGuard aGuard( getMutex() ); + + OGenericUnoController::attachFrame( i_rxFrame ); + if ( getFrame().is() ) + onAttachedFrame(); +} + +sal_Bool SAL_CALL OApplicationController::attachModel(const Reference< XModel > & _rxModel) +{ + ::osl::MutexGuard aGuard( getMutex() ); + const Reference< XOfficeDatabaseDocument > xOfficeDoc( _rxModel, UNO_QUERY ); + const Reference< XModifiable > xDocModify( _rxModel, UNO_QUERY ); + if ( ( !xOfficeDoc.is() || !xDocModify.is() ) && _rxModel.is() ) + { + OSL_FAIL( "OApplicationController::attachModel: invalid model!" ); + return false; + } + + if ( m_xModel.is() && ( m_xModel != _rxModel ) && ( _rxModel.is() ) ) + { + OSL_ENSURE( false, "OApplicationController::attachModel: missing implementation: setting a new model while we have another one!" ); + // we'd need to completely update our view here, close sub components, and the like + return false; + } + + const OUString aPropertyNames[] = + { + OUString(PROPERTY_URL), OUString(PROPERTY_USER) + }; + + // disconnect from old model + try + { + if ( m_xDataSource.is() ) + { + for (const auto & aPropertyName : aPropertyNames) + { + m_xDataSource->removePropertyChangeListener( aPropertyName, this ); + } + } + + Reference< XModifyBroadcaster > xBroadcaster( m_xModel, UNO_QUERY ); + if ( xBroadcaster.is() ) + xBroadcaster->removeModifyListener( this ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + m_xModel = _rxModel; + m_xDataSource.set( xOfficeDoc.is() ? xOfficeDoc->getDataSource() : Reference< XDataSource >(), UNO_QUERY ); + + // connect to new model + try + { + if ( m_xDataSource.is() ) + { + for (const auto & aPropertyName : aPropertyNames) + { + m_xDataSource->addPropertyChangeListener( aPropertyName, this ); + } + } + + Reference< XModifyBroadcaster > xBroadcaster( m_xModel, UNO_QUERY_THROW ); + xBroadcaster->addModifyListener( this ); + + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + // initial preview mode + if ( m_xDataSource.is() ) + { + try + { + // to get the 'modified' for the data source + ::comphelper::NamedValueCollection aLayoutInfo( m_xDataSource->getPropertyValue( PROPERTY_LAYOUTINFORMATION ) ); + if ( aLayoutInfo.has( OUString(INFO_PREVIEW) ) ) + { + const sal_Int32 nPreviewMode( aLayoutInfo.getOrDefault( INFO_PREVIEW, sal_Int32(0) ) ); + m_ePreviewMode = static_cast< PreviewMode >( nPreviewMode ); + if ( getView() ) + getContainer()->switchPreview( m_ePreviewMode ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + return true; +} + +void OApplicationController::containerFound( const Reference< XContainer >& _xContainer) +{ + try + { + if ( _xContainer.is() ) + { + m_aCurrentContainers.push_back(_xContainer); + _xContainer->addContainerListener(this); + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +OUString OApplicationController::getCurrentlySelectedName(sal_Int32& _rnCommandType) const +{ + _rnCommandType = ( (getContainer()->getElementType() == E_QUERY) + ? CommandType::QUERY : ( (getContainer()->getElementType() == E_TABLE) ? CommandType::TABLE : -1 )); + + OUString sName; + if ( _rnCommandType != -1 ) + { + try + { + sName = getContainer()->getQualifiedName( nullptr ); + OSL_ENSURE( !sName.isEmpty(), "OApplicationController::getCurrentlySelectedName: no name given!" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + return sName; +} + +void SAL_CALL OApplicationController::addSelectionChangeListener( const Reference< view::XSelectionChangeListener >& Listener ) +{ + m_pSelectionNotifier->addListener( Listener ); +} + +void SAL_CALL OApplicationController::removeSelectionChangeListener( const Reference< view::XSelectionChangeListener >& Listener ) +{ + m_pSelectionNotifier->removeListener( Listener ); +} + +sal_Bool SAL_CALL OApplicationController::select( const Any& _aSelection ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + Sequence< OUString> aSelection; + if ( !_aSelection.hasValue() || !getView() ) + { + getContainer()->selectElements(aSelection); + return true; + } + + // BEGIN compatibility + Sequence< NamedValue > aCurrentSelection; + if ( (_aSelection >>= aCurrentSelection) && aCurrentSelection.hasElements() ) + { + ElementType eType = E_NONE; + const NamedValue* pIter = aCurrentSelection.getConstArray(); + const NamedValue* pEnd = pIter + aCurrentSelection.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( pIter->Name == "Type" ) + { + sal_Int32 nType = 0; + pIter->Value >>= nType; + if ( nType < DatabaseObject::TABLE || nType > DatabaseObject::REPORT ) + throw IllegalArgumentException(); + eType = static_cast< ElementType >( nType ); + } + else if ( pIter->Name == "Selection" ) + pIter->Value >>= aSelection; + } + + m_aSelectContainerEvent.CancelCall(); // just in case the async select request was running + getContainer()->selectContainer(eType); + getContainer()->selectElements(aSelection); + return true; + } + // END compatibility + + Sequence< NamedDatabaseObject > aSelectedObjects; + if ( !( _aSelection >>= aSelectedObjects ) ) + { + aSelectedObjects.realloc( 1 ); + if ( !( _aSelection >>= aSelectedObjects[0] ) ) + throw IllegalArgumentException(); + } + + SelectionByElementType aSelectedElements; + ElementType eSelectedCategory = E_NONE; + for ( const NamedDatabaseObject* pObject = aSelectedObjects.getConstArray(); + pObject != aSelectedObjects.getConstArray() + aSelectedObjects.getLength(); + ++pObject + ) + { + switch ( pObject->Type ) + { + case DatabaseObject::TABLE: + case DatabaseObjectContainer::SCHEMA: + case DatabaseObjectContainer::CATALOG: + aSelectedElements[ E_TABLE ].push_back( pObject->Name ); + break; + case DatabaseObject::QUERY: + aSelectedElements[ E_QUERY ].push_back( pObject->Name ); + break; + case DatabaseObject::FORM: + case DatabaseObjectContainer::FORMS_FOLDER: + aSelectedElements[ E_FORM ].push_back( pObject->Name ); + break; + case DatabaseObject::REPORT: + case DatabaseObjectContainer::REPORTS_FOLDER: + aSelectedElements[ E_REPORT ].push_back( pObject->Name ); + break; + case DatabaseObjectContainer::TABLES: + case DatabaseObjectContainer::QUERIES: + case DatabaseObjectContainer::FORMS: + case DatabaseObjectContainer::REPORTS: + if ( eSelectedCategory != E_NONE ) + throw IllegalArgumentException( + DBA_RES(RID_STR_NO_DIFF_CAT), + *this, sal_Int16( pObject - aSelectedObjects.getConstArray() ) ); + eSelectedCategory = + ( pObject->Type == DatabaseObjectContainer::TABLES ) ? E_TABLE + : ( pObject->Type == DatabaseObjectContainer::QUERIES ) ? E_QUERY + : ( pObject->Type == DatabaseObjectContainer::FORMS ) ? E_FORM + : ( pObject->Type == DatabaseObjectContainer::REPORTS ) ? E_REPORT + : E_NONE; + break; + + default: + case DatabaseObjectContainer::DATA_SOURCE: + { + OUString sMessage( + DBA_RES(RID_STR_UNSUPPORTED_OBJECT_TYPE). + replaceFirst("$type$", OUString::number(pObject->Type))); + throw IllegalArgumentException(sMessage, *this, sal_Int16( pObject - aSelectedObjects.getConstArray() )); + } + } + } + for (auto const& selectedElement : aSelectedElements) + { + if ( selectedElement.first == m_eCurrentType ) + { + getContainer()->selectElements( comphelper::containerToSequence(selectedElement.second) ); + } + else + { + m_aPendingSelection[ selectedElement.first ] = selectedElement.second; + } + } + + m_aSelectContainerEvent.CancelCall(); // just in case the async select request was running + getContainer()->selectContainer( eSelectedCategory ); + + return true; +} + +Any SAL_CALL OApplicationController::getSelection( ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + Sequence< NamedDatabaseObject > aCurrentSelection; + const ElementType eType( getContainer()->getElementType() ); + if ( eType != E_NONE ) + { + getContainer()->describeCurrentSelectionForType( eType, aCurrentSelection ); + if ( !aCurrentSelection.hasElements() ) + { // if no objects are selected, add an entry to the sequence which describes the overall category + // which is selected currently + aCurrentSelection.realloc(1); + aCurrentSelection[0].Name = getDatabaseName(); + switch ( eType ) + { + case E_TABLE: aCurrentSelection[0].Type = DatabaseObjectContainer::TABLES; break; + case E_QUERY: aCurrentSelection[0].Type = DatabaseObjectContainer::QUERIES; break; + case E_FORM: aCurrentSelection[0].Type = DatabaseObjectContainer::FORMS; break; + case E_REPORT: aCurrentSelection[0].Type = DatabaseObjectContainer::REPORTS; break; + default: + OSL_FAIL( "OApplicationController::getSelection: unexpected current element type!" ); + break; + } + } + } + return makeAny( aCurrentSelection ); +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppController.hxx b/dbaccess/source/ui/app/AppController.hxx new file mode 100644 index 000000000..2edda8efd --- /dev/null +++ b/dbaccess/source/ui/app/AppController.hxx @@ -0,0 +1,526 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPCONTROLLER_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPCONTROLLER_HXX + +#include <AppElementType.hxx> +#include <callbacks.hxx> +#include <commontypes.hxx> +#include <dsntypes.hxx> +#include <dbaccess/genericcontroller.hxx> +#include <linkeddocuments.hxx> +#include <TableCopyHelper.hxx> + +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp> +#include <com/sun/star/ui/XContextMenuInterception.hpp> + +#include <comphelper/namedvaluecollection.hxx> +#include <comphelper/uno3.hxx> +#include <cppuhelper/implbase5.hxx> +#include <comphelper/interfacecontainer2.hxx> +#include <vcl/transfer.hxx> +#include <svx/dataaccessdescriptor.hxx> + +#include <memory> + +class SvTreeListEntry; +class SvTreeListBox; +class TransferableHelper; +class TransferableClipboardListener; + +namespace com::sun::star { + namespace container { + class XNameContainer; + class XContainer; + } + namespace ucb { + class XContent; + } +} + +namespace dbaui +{ + class SubComponentManager; + class OApplicationController; + class OApplicationView; + class OLinkedDocumentsAccess; + class SelectionNotifier; + + typedef ::cppu::ImplHelper5 < css::container::XContainerListener + , css::beans::XPropertyChangeListener + , css::sdb::application::XDatabaseDocumentUI + , css::ui::XContextMenuInterception + , css::view::XSelectionSupplier + > OApplicationController_Base; + + + class OApplicationController + :public OGenericUnoController + ,public OApplicationController_Base + ,public IControlActionListener + ,public IContextMenuProvider + { + public: + typedef std::vector< css::uno::Reference< css::container::XContainer > > TContainerVector; + + private: + + OTableCopyHelper::DropDescriptor m_aAsyncDrop; + + SharedConnection m_xDataSourceConnection; + css::uno::Reference< css::sdbc::XDatabaseMetaData > + m_xMetaData; + + TransferableDataHelper m_aSystemClipboard; // content of the clipboard + css::uno::Reference< css::beans::XPropertySet > + m_xDataSource; + css::uno::Reference< css::frame::XModel > + m_xModel; + ::comphelper::OInterfaceContainerHelper2 + m_aContextMenuInterceptors; + + TContainerVector m_aCurrentContainers; // the containers where we are listener on + ::rtl::Reference< SubComponentManager > + m_pSubComponentManager; + ::dbaccess::ODsnTypeCollection + m_aTypeCollection; + OTableCopyHelper m_aTableCopyHelper; + rtl::Reference<TransferableClipboardListener> + m_pClipboardNotifier; // notifier for changes in the clipboard + ImplSVEvent * m_nAsyncDrop; + OAsynchronousLink m_aSelectContainerEvent; + PreviewMode m_ePreviewMode; // the mode of the preview + ElementType m_eCurrentType; + bool m_bNeedToReconnect; // true when the settings of the data source were modified and the connection is no longer up to date + bool m_bSuspended; // is true when the controller was already suspended + + std::unique_ptr< SelectionNotifier > + m_pSelectionNotifier; + typedef std::map< ElementType, std::vector< OUString > > SelectionByElementType; + SelectionByElementType m_aPendingSelection; + + private: + + OApplicationView* getContainer() const; + + /** returns the database name + @return + the database name + */ + OUString getDatabaseName() const; + + /** returns the stripped database name. + @return + The stripped database name either the registered name or if it is a file url the last segment. + */ + OUString getStrippedDatabaseName() const; + + /** return the element type for given container + @param _xContainer The container where the element type has to be found + @return the element type corresponding to the given container + */ + static ElementType getElementType(const css::uno::Reference< css::container::XContainer >& _xContainer); + + /** opens a new sub frame with a table/query/form/report/view, passing additional arguments + */ + css::uno::Reference< css::lang::XComponent > openElementWithArguments( + const OUString& _sName, + ElementType _eType, + ElementOpenMode _eOpenMode, + sal_uInt16 _nInstigatorCommand, + const ::comphelper::NamedValueCollection& _rAdditionalArguments + ); + + /** opens a new frame for creation or auto pilot + @param _eType + Defines the type to open + @param i_rAdditionalArguments + Additional arguments to pass when creating the component + */ + css::uno::Reference< css::lang::XComponent > + newElement( + ElementType _eType, + const ::comphelper::NamedValueCollection& i_rAdditionalArguments, + css::uno::Reference< css::lang::XComponent >& o_rDocumentDefinition + ); + + /** creates a new database object, using an auto pilot + @param _eType + Defines the type of the object to create + @precond + Our mutex must not be locked. + @since #i39203# + */ + void newElementWithPilot( ElementType _eType ); + + /** converts the query to a view + @param _sName + The name of the query. + */ + void convertToView(const OUString& _sName); + + /** checks if the connection for the selected data source is read only. If the connection doesn't exist, <TRUE/> will be returned. + @return + <TRUE/> if read only or doesn't exist, otherwise <FALSE/> + */ + bool isConnectionReadOnly() const; + + /// fills the list with the selected entries. + void getSelectionElementNames( std::vector< OUString>& _rNames ) const; + + /// deletes the entries selected. + void deleteEntries(); + + /// renames the selected entry in the detail page + void renameEntry(); + + /** deletes queries, forms, or reports + @param _eType + the type of the objects + @param _rList + The names of the elements to delete + @param _bConfirm + determines whether the user must confirm the deletion + */ + void deleteObjects( ElementType _eType, + const std::vector< OUString>& _rList, + bool _bConfirm ); + + /** deletes tables. + @param _rList + The list of tables. + */ + void deleteTables(const std::vector< OUString>& _rList); + + /// copies the current object into clipboard + TransferableHelper* copyObject(); + + /// returns the nameaccess + css::uno::Reference< css::container::XNameAccess > getElements(ElementType _eType); + + /** returns the document access for the specific type + @param _eType + the type + @return std::unique_ptr<OLinkedDocumentsAccess> + */ + std::unique_ptr<OLinkedDocumentsAccess> getDocumentsAccess(ElementType _eType); + + /// returns the query definitions of the active data source. + css::uno::Reference< css::container::XNameContainer> getQueryDefinitions() const; + + /** pastes a special format from the system clipboard to the currently selected object types + @param _nFormatId + The format to be copied. + */ + void pasteFormat(SotClipboardFormatId _nFormatId); + + /** pastes a query, form or report into the data source + @param _eType + The type of the object to paste. + @param _rPasteData + The data descriptor. + @param _sParentFolder + The name of the parent folder if it exists. + @param _bMove + if <TRUE/> the name of the content must be inserted without any change, otherwise not. + @return + <TRUE/> if the paste operations was successful, otherwise <FALSE/>. + */ + bool paste( ElementType _eType, const svx::ODataAccessDescriptor& _rPasteData, const OUString& _sParentFolder = OUString(), bool _bMove = false); + + /// returns the system clipboard. + const TransferableDataHelper& getViewClipboard() const { return m_aSystemClipboard; } + + /// returns <TRUE/> if the clipboard supports a table format, otherwise <FALSE/>. + bool isTableFormat() const; + + /** fills the vector with all supported formats + @param _eType + The type for which we need the formats + @param _rFormatIds + The vector to be filled up. + */ + static void getSupportedFormats(ElementType _eType,std::vector<SotClipboardFormatId>& _rFormatIds); + + /** adds a listener to the current name access. + @param _xCollection + The collection where we want to listen on. + */ + void addContainerListener(const css::uno::Reference< css::container::XNameAccess>& _xCollection); + + /** opens a uno dialog with the currently selected data source as initialize argument + @param _sServiceName + The service name of the dialog to be executed. + */ + void openDialog(const OUString& _sServiceName); + + /** when the settings of the data source changed, + it opens a dialog which ask to close all depending documents, then recreate the connection. + The SolarMutex has to be locked before calling this. + */ + void askToReconnect(); + + /** remember a newly opened sub document for later access + */ + void onDocumentOpened( + const OUString& _rName, + const sal_Int32 _nType, + const ElementOpenMode _eMode, + const css::uno::Reference< css::lang::XComponent >& _xDocument, + const css::uno::Reference< css::lang::XComponent >& _xDefinition + ); + + /** Inserts a new object into the hierarchy given be the type. + @param _eType + Where to insert the new item. + @param _sParentFolder + The name of the parent folder if it exists. + @param _xContent + The content to insert. + @param _bMove + if <TRUE/> the name of the content must be inserted without any change, otherwise not. + @return + <TRUE/> if the insert operations was successful, otherwise <FALSE/>. + */ + bool insertHierachyElement( ElementType _eType + ,const OUString& _sParentFolder + ,bool _bCollection = true + ,const css::uno::Reference< css::ucb::XContent>& _xContent = css::uno::Reference< css::ucb::XContent>() + ,bool _bMove = false); + /** checks if delete command or rename command is allowed + @param _eType + The element type. + @param _bDelete + If <TRUE> then the delete command should be checked. + @return + <TRUE> if the command is allowed + */ + bool isRenameDeleteAllowed(ElementType _eType, bool _bDelete) const; + /** all selected entries will be opened, or edited, or converted to a view + @param _nId + The slot which should be executed. + @param _eOpenMode + Defines the mode of opening. @see ElementOpenMode + */ + void doAction(sal_uInt16 _nId, ElementOpenMode _eOpenMode); + + /** returns the currently selected table or query name. + * + * \return the name of the currently table or query. If the tables or query container is selected otherwise an empty string will be returned. + */ + OUString getCurrentlySelectedName(sal_Int32& _rnCommandType) const; + + /** shows the preview for the given entry + */ + void showPreviewFor( const ElementType _eType,const OUString& _sName ); + + /** called we were attached to a frame + + In particular, this is called *after* the controller has been announced to the model + (XModel::connectController) + */ + void onAttachedFrame(); + + /// determines whether the given table name denotes a view which can be altered + bool impl_isAlterableView_nothrow( const OUString& _rTableOrViewName ) const; + + /** verifies the object type denotes a valid DatabaseObject, and the object name denotes an existing + object of this type. Throws if not. + */ + void impl_validateObjectTypeAndName_throw( const sal_Int32 _nObjectType, const ::std::optional< OUString >& i_rObjectName ); + + protected: + // initializing members + + // state of a feature. 'feature' may be the handle of a css::util::URL somebody requested a dispatch interface for OR a toolbar slot. + virtual FeatureState GetState(sal_uInt16 nId) const override; + // execute a feature + virtual void Execute(sal_uInt16 nId, const css::uno::Sequence< css::beans::PropertyValue>& aArgs) override; + + // OGenericUnoController + virtual void onLoadedMenu( const css::uno::Reference< css::frame::XLayoutManager >& _xLayoutManager ) override; + + virtual css::uno::Reference< css::frame::XModel > getPrivateModel() const override + { + return m_xModel; + } + + virtual ~OApplicationController() override; + + public: + explicit OApplicationController(const css::uno::Reference< css::uno::XComponentContext >& _rxORB); + + DECLARE_XINTERFACE( ) + DECLARE_XTYPEPROVIDER( ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override; + // need by registration + /// @throws css::uno::RuntimeException + static OUString getImplementationName_Static(); + /// @throws css::uno::RuntimeException + static css::uno::Sequence< OUString > getSupportedServiceNames_Static(); + static css::uno::Reference< css::uno::XInterface > + Create(const css::uno::Reference< css::lang::XMultiServiceFactory >&); + + // css::frame::XController + virtual void SAL_CALL attachFrame(const css::uno::Reference< css::frame::XFrame > & xFrame) override; + virtual sal_Bool SAL_CALL suspend(sal_Bool bSuspend) override; + virtual sal_Bool SAL_CALL attachModel(const css::uno::Reference< css::frame::XModel > & xModel) override; + virtual css::uno::Reference< css::frame::XModel > SAL_CALL getModel() override; + + // css::container::XContainerListener + virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& Event) override; + virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& Event) override; + virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& Event) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + + // XDatabaseDocumentUI + virtual css::uno::Reference< css::sdbc::XDataSource > SAL_CALL getDataSource() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL getApplicationMainWindow() override; + virtual css::uno::Reference< css::sdbc::XConnection > SAL_CALL getActiveConnection() override; + virtual css::uno::Sequence< css::uno::Reference< css::lang::XComponent > > SAL_CALL getSubComponents() override; + virtual sal_Bool SAL_CALL isConnected( ) override; + // DO NOT CALL with getMutex() held!! + virtual void SAL_CALL connect( ) override; + virtual css::beans::Pair< ::sal_Int32, OUString > SAL_CALL identifySubComponent( const css::uno::Reference< css::lang::XComponent >& SubComponent ) override; + virtual sal_Bool SAL_CALL closeSubComponents( ) override; + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponent( ::sal_Int32 ObjectType, const OUString& ObjectName, sal_Bool ForEditing ) override; + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL loadComponentWithArguments( ::sal_Int32 ObjectType, const OUString& ObjectName, sal_Bool ForEditing, const css::uno::Sequence< css::beans::PropertyValue >& Arguments ) override; + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL createComponent( ::sal_Int32 ObjectType, css::uno::Reference< css::lang::XComponent >& o_DocumentDefinition ) override; + virtual css::uno::Reference< css::lang::XComponent > SAL_CALL createComponentWithArguments( ::sal_Int32 ObjectType, const css::uno::Sequence< css::beans::PropertyValue >& Arguments, css::uno::Reference< css::lang::XComponent >& o_DocumentDefinition ) override; + + // XContextMenuInterception + virtual void SAL_CALL registerContextMenuInterceptor( const css::uno::Reference< css::ui::XContextMenuInterceptor >& Interceptor ) override; + virtual void SAL_CALL releaseContextMenuInterceptor( const css::uno::Reference< css::ui::XContextMenuInterceptor >& Interceptor ) override; + + // XSelectionSupplier + virtual sal_Bool SAL_CALL select( const css::uno::Any& xSelection ) override; + virtual css::uno::Any SAL_CALL getSelection( ) override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + /** retrieves the current connection, creates it if necessary + + If an error occurs, then this is either stored in the location pointed to by <arg>_pErrorInfo</arg>, + or, if <code>_pErrorInfo</code> is <NULL/>, then the error is displayed to the user. + + DO NOT CALL with getMutex() held!! + */ + const SharedConnection& ensureConnection( ::dbtools::SQLExceptionInfo* _pErrorInfo = nullptr ); + + /** retrieves the current connection + */ + const SharedConnection& getConnection() const { return m_xDataSourceConnection; } + + /// determines whether we're currently connected to the database + bool isConnected() const { return m_xDataSourceConnection.is(); } + + /** refreshes the tables + */ + void refreshTables(); + + /** called when an entry in a tree list box has been double-clicked + @param _rTree + The tree list box. + @return + <TRUE/> if the double click event has been handled by the called, and should not + be processed further. + */ + bool onEntryDoubleClick(SvTreeListBox const & _rTree); + /** called when a container (category) in the application view has been selected + @param _pTree + The tree list box. + @return + <TRUE/> if the container could be changed otherwise <FALSE/> + */ + bool onContainerSelect(ElementType _eType); + /** called when an entry in a tree view has been selected + @param _pEntry + the selected entry + */ + void onSelectionChanged(); + /** called when a "Copy" command is executed in a tree view + */ + void onCopyEntry(); + /** called when a "Paste" command is executed in a tree view + */ + void onPasteEntry(); + /** called when a "Delete" command is executed in a tree view + */ + void onDeleteEntry(); + /// called when the preview mode was changed + void previewChanged( sal_Int32 _nMode); + /// called when an object container of any kind was found during enumerating tree view elements + void containerFound( const css::uno::Reference< css::container::XContainer >& _xContainer); + + // IController + virtual bool isDataSourceReadOnly() const override; + + // IControlActionListener overridables + virtual bool requestQuickHelp( const SvTreeListEntry* _pEntry, OUString& _rText ) const override; + virtual bool requestDrag( const Point& _rPosPixel ) override; + virtual sal_Int8 queryDrop( const AcceptDropEvent& _rEvt, const DataFlavorExVector& _rFlavors ) override; + virtual sal_Int8 executeDrop( const ExecuteDropEvent& _rEvt ) override; + + // IContextMenuProvider + virtual OUString getContextMenuResourceName( Control& _rControl ) const override; + virtual IController& getCommandController() override; + virtual ::comphelper::OInterfaceContainerHelper2* + getContextMenuInterceptors() override; + virtual css::uno::Any + getCurrentSelection( Control& _rControl ) const override; + + void OnInvalidateClipboard(); + DECL_LINK( OnClipboardChanged, TransferableDataHelper*, void ); + DECL_LINK( OnAsyncDrop, void*, void ); + DECL_LINK( OnCreateWithPilot, void*, void ); + DECL_LINK( OnSelectContainer, void*, void ); + void OnFirstControllerConnected(); + + protected: + using OGenericUnoController::connect; + + /** disconnects from our XConnection, and cleans up this connection + */ + void disconnect(); + + // late construction + virtual bool Construct(vcl::Window* pParent) override; + virtual void describeSupportedFeatures() override; + + protected: + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + + // OComponentHelper + virtual void SAL_CALL disposing() override; + }; + +} // namespace dbaui + +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPCONTROLLER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppControllerDnD.cxx b/dbaccess/source/ui/app/AppControllerDnD.cxx new file mode 100644 index 000000000..98d622584 --- /dev/null +++ b/dbaccess/source/ui/app/AppControllerDnD.cxx @@ -0,0 +1,808 @@ +/* -*- 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 <memory> +#include "AppController.hxx" +#include <comphelper/property.hxx> +#include <core_resource.hxx> +#include <strings.hxx> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/container/XHierarchicalNameContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp> +#include <com/sun/star/sdbcx/XDrop.hpp> +#include <dlgsave.hxx> +#include <vcl/weld.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <connectivity/dbexception.hxx> +#include <sal/log.hxx> +#include "AppView.hxx" +#include <svx/dataaccessdescriptor.hxx> +#include <svx/dbaobjectex.hxx> +#include <strings.hrc> +#include <vcl/svapp.hxx> +#include <linkeddocuments.hxx> +#include <connectivity/dbtools.hxx> +#include <dbexchange.hxx> +#include <UITools.hxx> +#include <algorithm> +#include <iterator> +#include <com/sun/star/sdb/XReportDocumentsSupplier.hpp> +#include <com/sun/star/sdb/XFormDocumentsSupplier.hpp> +#include <svtools/querydelete.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <defaultobjectnamecheck.hxx> +#include <osl/mutex.hxx> +#include "subcomponentmanager.hxx" +#include <set> + +namespace dbaui +{ +using namespace ::dbtools; +using namespace ::svx; +using namespace ::svtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::util; + +void OApplicationController::deleteTables(const std::vector< OUString>& _rList) +{ + SharedConnection xConnection( ensureConnection() ); + + Reference<XTablesSupplier> xSup(xConnection,UNO_QUERY); + OSL_ENSURE(xSup.is(),"OApplicationController::deleteTable: no XTablesSupplier!"); + if ( !xSup.is() ) + return; + + Reference<XNameAccess> xTables = xSup->getTables(); + Reference<XDrop> xDrop(xTables,UNO_QUERY); + if ( xDrop.is() ) + { + bool bConfirm = true; + std::vector< OUString>::const_iterator aEnd = _rList.end(); + for (std::vector< OUString>::const_iterator aIter = _rList.begin(); aIter != aEnd; ++aIter) + { + OUString sTableName = *aIter; + + sal_Int32 nResult = RET_YES; + if ( bConfirm ) + nResult = ::dbaui::askForUserAction(getFrameWeld(), STR_TITLE_CONFIRM_DELETION, STR_QUERY_DELETE_TABLE, _rList.size() > 1 && (aIter+1) != _rList.end(), sTableName); + + bool bUserConfirmedDelete = + ( RET_YES == nResult ) + || ( RET_ALL == nResult ); + if ( bUserConfirmedDelete && m_pSubComponentManager->closeSubFrames( sTableName, E_TABLE ) ) + { + SQLExceptionInfo aErrorInfo; + try + { + if ( xTables->hasByName(sTableName) ) + xDrop->dropByName(sTableName); + else + {// could be a view + Reference<XViewsSupplier> xViewsSup(xConnection,UNO_QUERY); + + Reference<XNameAccess> xViews; + if ( xViewsSup.is() ) + { + xViews = xViewsSup->getViews(); + if ( xViews.is() && xViews->hasByName(sTableName) ) + { + xDrop.set(xViews,UNO_QUERY); + if ( xDrop.is() ) + xDrop->dropByName(sTableName); + } + } + } + } + catch(SQLContext& e) { aErrorInfo = e; } + catch(SQLWarning& e) { aErrorInfo = e; } + catch(SQLException& e) { aErrorInfo = e; } + catch(WrappedTargetException& e) + { + SQLException aSql; + if(e.TargetException >>= aSql) + aErrorInfo = aSql; + else + OSL_FAIL("OApplicationController::implDropTable: something strange happened!"); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( aErrorInfo.isValid() ) + showError(aErrorInfo); + + if ( RET_ALL == nResult ) + bConfirm = false; + } + else + break; + } + } + else + { + OUString sMessage(DBA_RES(STR_MISSING_TABLES_XDROP)); + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(getFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + sMessage)); + xError->run(); + } +} + +void OApplicationController::deleteObjects( ElementType _eType, const std::vector< OUString>& _rList, bool _bConfirm ) +{ + Reference< XNameContainer > xNames( getElements( _eType ), UNO_QUERY ); + Reference< XHierarchicalNameContainer > xHierarchyName( xNames, UNO_QUERY ); + if ( !xNames.is() ) + return; + + short eResult = _bConfirm ? svtools::QUERYDELETE_YES : svtools::QUERYDELETE_ALL; + + // The list of elements to delete is allowed to contain related elements: A given element may + // be the ancestor or child of another element from the list. + // We want to ensure that ancestors get deleted first, so we normalize the list in this respect. + // #i33353# + std::set< OUString > aDeleteNames; + // Note that this implicitly uses std::less< OUString > a comparison operation, which + // results in lexicographical order, which is exactly what we need, because "foo" is *before* + // any "foo/bar" in this order. + std::copy( + _rList.begin(), _rList.end(), + std::insert_iterator< std::set< OUString > >( aDeleteNames, aDeleteNames.begin() ) + ); + + std::set< OUString >::size_type nCount = aDeleteNames.size(); + for ( std::set< OUString >::size_type nObjectsLeft = nCount; !aDeleteNames.empty(); ) + { + std::set< OUString >::const_iterator aThisRound = aDeleteNames.begin(); + + if ( eResult != svtools::QUERYDELETE_ALL ) + { + svtools::QueryDeleteDlg_Impl aDlg(getFrameWeld(), *aThisRound); + + if ( nObjectsLeft > 1 ) + aDlg.EnableAllButton(); + + eResult = aDlg.run(); + } + + bool bSuccess = false; + + bool bUserConfirmedDelete = + ( eResult == svtools::QUERYDELETE_ALL ) + || ( eResult == svtools::QUERYDELETE_YES ); + + if ( bUserConfirmedDelete + && ( _eType != E_QUERY || m_pSubComponentManager->closeSubFrames( *aThisRound, _eType ) ) + ) + { + try + { + if ( xHierarchyName.is() ) + xHierarchyName->removeByHierarchicalName( *aThisRound ); + else + xNames->removeByName( *aThisRound ); + + bSuccess = true; + + // now that we removed the element, care for all its child elements + // which may also be a part of the list + // #i33353# + OSL_ENSURE( aThisRound->getLength() - 1 >= 0, "OApplicationController::deleteObjects: empty name?" ); + OUString sSmallestSiblingName = *aThisRound + OUStringChar( sal_Unicode( '/' + 1) ); + + std::set< OUString >::const_iterator aUpperChildrenBound = aDeleteNames.lower_bound( sSmallestSiblingName ); + for ( std::set< OUString >::const_iterator aObsolete = aThisRound; + aObsolete != aUpperChildrenBound; + ) + { + std::set< OUString >::const_iterator aNextObsolete = aObsolete; ++aNextObsolete; + aDeleteNames.erase( aObsolete ); + --nObjectsLeft; + aObsolete = aNextObsolete; + } + } + catch(const SQLException&) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + } + catch(const WrappedTargetException& e) + { + SQLException aSql; + if ( e.TargetException >>= aSql ) + showError( SQLExceptionInfo( e.TargetException ) ); + else + OSL_FAIL( "OApplicationController::deleteObjects: something strange happened!" ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + if ( !bSuccess ) + { + // okay, this object could not be deleted (or the user did not want to delete it), + // but continue with the rest + aDeleteNames.erase( aThisRound ); + --nObjectsLeft; + } + } +} + +void OApplicationController::deleteEntries() +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( !getContainer() ) + return; + + std::vector< OUString> aList; + getSelectionElementNames(aList); + ElementType eType = getContainer()->getElementType(); + switch(eType) + { + case E_TABLE: + deleteTables(aList); + break; + case E_QUERY: + deleteObjects( E_QUERY, aList, true ); + break; + case E_FORM: + deleteObjects( E_FORM, aList, true ); + break; + case E_REPORT: + deleteObjects( E_REPORT, aList, true ); + break; + case E_NONE: + break; + } +} + +// DO NOT CALL with getMutex() held!! +const SharedConnection& OApplicationController::ensureConnection( ::dbtools::SQLExceptionInfo* _pErrorInfo ) +{ + + // This looks like double checked locking, but it is not, + // because every access (read *or* write) to m_xDataSourceConnection + // is mutexed. + // See http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html + // for what I'm referring to. + // We cannot use the TLS (thread-local storage) solution + // since support for TLS is not up to the snuff on Windows :-( + + { + ::osl::MutexGuard aGuard( getMutex() ); + + if ( m_xDataSourceConnection.is() ) + return m_xDataSourceConnection; + } + + weld::WaitObject aWO(getFrameWeld()); + Reference<XConnection> conn; + { + SolarMutexGuard aSolarGuard; + + OUString sConnectingContext(DBA_RES(STR_COULDNOTCONNECT_DATASOURCE)); + sConnectingContext = sConnectingContext.replaceFirst("$name$", getStrippedDatabaseName()); + + // do the connection *without* holding getMutex() to avoid deadlock + // when we are not in the main thread and we need username/password + // (and thus to display a dialog, which will be done by the main thread) + // and there is an event that needs getMutex() *before* us in the main thread's queue + // See fdo#63391 + conn.set( connect( getDatabaseName(), sConnectingContext, _pErrorInfo ) ); + } + + if (conn.is()) + { + ::osl::MutexGuard aGuard( getMutex() ); + if ( m_xDataSourceConnection.is() ) + { + Reference< XComponent > comp (conn, UNO_QUERY); + if(comp.is()) + { + try + { + comp->dispose(); + } + catch( const Exception& ) + { + OSL_FAIL( "dbaui::OApplicationController::ensureConnection could not dispose of temporary unused connection" ); + } + } + conn.clear(); + } + else + { + m_xDataSourceConnection.reset(conn); + SQLExceptionInfo aError; + try + { + m_xMetaData = m_xDataSourceConnection->getMetaData(); + } + catch( const SQLException& ) + { + aError = ::cppu::getCaughtException(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + if ( aError.isValid() ) + { + if ( _pErrorInfo ) + { + *_pErrorInfo = aError; + } + else + { + SolarMutexGuard aSolarGuard; + showError( aError ); + } + } + } + } + + return m_xDataSourceConnection; +} + +bool OApplicationController::isDataSourceReadOnly() const +{ + Reference<XStorable> xStore(m_xModel,UNO_QUERY); + return !xStore.is() || xStore->isReadonly(); +} + +bool OApplicationController::isConnectionReadOnly() const +{ + bool bIsConnectionReadOnly = true; + if ( m_xMetaData.is() ) + { + try + { + bIsConnectionReadOnly = m_xMetaData->isReadOnly(); + } + catch(const SQLException&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + // TODO check configuration + return bIsConnectionReadOnly; +} + +Reference< XNameAccess > OApplicationController::getElements( ElementType _eType ) +{ + Reference< XNameAccess > xElements; + try + { + switch ( _eType ) + { + case E_REPORT: + { + Reference< XReportDocumentsSupplier > xSupp( m_xModel, UNO_QUERY_THROW ); + xElements.set( xSupp->getReportDocuments(), UNO_SET_THROW ); + } + break; + + case E_FORM: + { + Reference< XFormDocumentsSupplier > xSupp( m_xModel, UNO_QUERY_THROW ); + xElements.set( xSupp->getFormDocuments(), UNO_SET_THROW ); + } + break; + + case E_QUERY: + { + xElements.set( getQueryDefinitions(), UNO_QUERY_THROW ); + } + break; + + case E_TABLE: + { + if ( m_xDataSourceConnection.is() ) + { + Reference< XTablesSupplier > xSup( getConnection(), UNO_QUERY_THROW ); + xElements.set( xSup->getTables(), UNO_SET_THROW ); + } + } + break; + + default: + break; + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return xElements; +} + +void OApplicationController::getSelectionElementNames(std::vector< OUString>& _rNames) const +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + OSL_ENSURE(getContainer(),"View isn't valid! -> GPF"); + + getContainer()->getSelectionElementNames( _rNames ); +} + +std::unique_ptr< OLinkedDocumentsAccess > OApplicationController::getDocumentsAccess( ElementType _eType ) +{ + OSL_ENSURE( ( _eType == E_TABLE ) || ( _eType == E_QUERY ) || ( _eType == E_FORM ) || ( _eType == E_REPORT ), + "OApplicationController::getDocumentsAccess: only forms and reports are supported here!" ); + + SharedConnection xConnection( ensureConnection() ); + Reference< XNameAccess > xDocContainer; + + if ( ( _eType == E_FORM ) || ( _eType == E_REPORT ) ) + { + xDocContainer.set( getElements( _eType ) ); + OSL_ENSURE( xDocContainer.is(), "OApplicationController::getDocumentsAccess: invalid container!" ); + } + + std::unique_ptr< OLinkedDocumentsAccess > pDocuments( new OLinkedDocumentsAccess( + getFrameWeld(), this, getORB(), xDocContainer, xConnection, getDatabaseName() + ) ); + return pDocuments; +} + +TransferableHelper* OApplicationController::copyObject() +{ + try + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ElementType eType = getContainer()->getElementType(); + TransferableHelper* pData = nullptr; + switch( eType ) + { + case E_TABLE: + case E_QUERY: + { + SharedConnection xConnection( ensureConnection() ); + Reference< XDatabaseMetaData> xMetaData; + if ( xConnection.is() ) + xMetaData = xConnection->getMetaData(); + + OUString sName = getContainer()->getQualifiedName( nullptr ); + if ( !sName.isEmpty() ) + { + OUString sDataSource = getDatabaseName(); + + if ( eType == E_TABLE ) + { + pData = new ODataClipboard(sDataSource, CommandType::TABLE, sName, xConnection, getNumberFormatter(xConnection, getORB()), getORB()); + } + else + { + pData = new ODataClipboard(sDataSource, CommandType::QUERY, sName, getNumberFormatter(xConnection, getORB()), getORB()); + } + } + } + break; + case E_FORM: + case E_REPORT: + { + std::vector< OUString> aList; + getSelectionElementNames(aList); + Reference< XHierarchicalNameAccess > xElements(getElements(eType),UNO_QUERY); + if ( xElements.is() && !aList.empty() ) + { + Reference< XContent> xContent(xElements->getByHierarchicalName(*aList.begin()),UNO_QUERY); + pData = new OComponentTransferable( getDatabaseName(), xContent ); + } + } + break; + default: + break; + } + + // the ownership goes to ODataClipboards + return pData; + } + catch(const SQLException&) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return nullptr; +} + +bool OApplicationController::paste( ElementType _eType, const svx::ODataAccessDescriptor& _rPasteData, const OUString& _sParentFolder, bool _bMove) +{ + try + { + if ( _eType == E_QUERY ) + { + sal_Int32 nCommandType = CommandType::TABLE; + if ( _rPasteData.has(DataAccessDescriptorProperty::CommandType) ) + _rPasteData[DataAccessDescriptorProperty::CommandType] >>= nCommandType; + + if ( CommandType::QUERY == nCommandType || CommandType::COMMAND == nCommandType ) + { + // read all necessary data + + OUString sCommand; + bool bEscapeProcessing = true; + + _rPasteData[DataAccessDescriptorProperty::Command] >>= sCommand; + if ( _rPasteData.has(DataAccessDescriptorProperty::EscapeProcessing) ) + _rPasteData[DataAccessDescriptorProperty::EscapeProcessing] >>= bEscapeProcessing; + + // plausibility check + bool bValidDescriptor = false; + OUString sDataSourceName = _rPasteData.getDataSource(); + if (CommandType::QUERY == nCommandType) + bValidDescriptor = sDataSourceName.getLength() && sCommand.getLength(); + else if (CommandType::COMMAND == nCommandType) + bValidDescriptor = !sCommand.isEmpty(); + if (!bValidDescriptor) + { + OSL_FAIL("OApplicationController::paste: invalid descriptor!"); + return false; + } + + // the target object name (as we'll suggest it to the user) + OUString sTargetName; + try + { + if ( CommandType::QUERY == nCommandType ) + sTargetName = sCommand; + + if ( sTargetName.isEmpty() ) + { + OUString sDefaultName = DBA_RES(STR_QRY_TITLE); + sDefaultName = sDefaultName.getToken( 0, ' ' ); + + Reference< XNameAccess > xQueries( getQueryDefinitions(), UNO_QUERY_THROW ); + sTargetName = ::dbtools::createUniqueName( xQueries, sDefaultName, false ); + } + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + Reference< XPropertySet > xQuery; + if (CommandType::QUERY == nCommandType) + { + // need to extract the statement and the escape processing flag from the query object + bool bSuccess = false; + try + { + // the concrete query + Reference< XQueryDefinitionsSupplier > xSourceQuerySup( + getDataSourceByName( sDataSourceName, getFrameWeld(), getORB(), nullptr ), + UNO_QUERY_THROW ); + Reference< XNameAccess > xQueries( xSourceQuerySup->getQueryDefinitions(), UNO_SET_THROW ); + if ( xQueries->hasByName( sCommand ) ) + { + xQuery.set( xQueries->getByName(sCommand), UNO_QUERY_THROW ); + bSuccess = true; + } + } + catch(SQLException&) { throw; } // caught and handled by the outer catch + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if (!bSuccess) + { + OSL_FAIL("OApplicationController::paste: could not extract the source query object!"); + // TODO: maybe this is worth an error message to be displayed to the user... + return false; + } + } + + Reference< XNameContainer > xDestQueries = getQueryDefinitions(); + Reference< XSingleServiceFactory > xQueryFactory(xDestQueries, UNO_QUERY); + if (!xQueryFactory.is()) + { + OSL_FAIL("OApplicationController::paste: invalid destination query container!"); + return false; + } + + // here we have everything needed to create a new query object... + // ... ehm, except a new name + ensureConnection(); + + DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::QUERY ); + ::dbtools::SQLExceptionInfo aDummy; + bool bNeedAskForName = ( sCommand.isEmpty() ) + /* we did not have a source name, so the target name was auto-generated */ + || ( !aNameChecker.isNameValid( sTargetName, aDummy ) ); + /* name is invalid in the target DB (e.g. because it already + has a /table/ with that name) */ + if ( bNeedAskForName ) + { + OSaveAsDlg aAskForName(getFrameWeld(), + CommandType::QUERY, + getORB(), + getConnection(), + sTargetName, + aNameChecker, + SADFlags::AdditionalDescription | SADFlags::TitlePasteAs ); + if ( RET_OK != aAskForName.run() ) + // cancelled by the user + return false; + sTargetName = aAskForName.getName(); + } + + // create a new object + Reference< XPropertySet > xNewQuery(xQueryFactory->createInstance(), UNO_QUERY); + OSL_ENSURE(xNewQuery.is(), "OApplicationController::paste: invalid object created by factory!"); + if (xNewQuery.is()) + { + // initialize + if ( xQuery.is() ) + ::comphelper::copyProperties(xQuery,xNewQuery); + else + { + xNewQuery->setPropertyValue(PROPERTY_COMMAND,makeAny(sCommand)); + xNewQuery->setPropertyValue(PROPERTY_ESCAPE_PROCESSING,makeAny(bEscapeProcessing)); + } + // insert + xDestQueries->insertByName( sTargetName, makeAny(xNewQuery) ); + xNewQuery.set(xDestQueries->getByName( sTargetName),UNO_QUERY); + if ( xQuery.is() && xNewQuery.is() ) + { + Reference<XColumnsSupplier> xSrcColSup(xQuery,UNO_QUERY); + Reference<XColumnsSupplier> xDstColSup(xNewQuery,UNO_QUERY); + if ( xSrcColSup.is() && xDstColSup.is() ) + { + Reference<XNameAccess> xSrcNameAccess = xSrcColSup->getColumns(); + Reference<XNameAccess> xDstNameAccess = xDstColSup->getColumns(); + Reference<XDataDescriptorFactory> xFac(xDstNameAccess,UNO_QUERY); + Reference<XAppend> xAppend(xFac,UNO_QUERY); + if ( xSrcNameAccess.is() && xDstNameAccess.is() && xSrcNameAccess->hasElements() && xAppend.is() ) + { + Reference<XPropertySet> xDstProp(xFac->createDataDescriptor()); + + Sequence< OUString> aSeq = xSrcNameAccess->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for( ; pIter != pEnd ; ++pIter) + { + Reference<XPropertySet> xSrcProp(xSrcNameAccess->getByName(*pIter),UNO_QUERY); + ::comphelper::copyProperties(xSrcProp,xDstProp); + xAppend->appendByDescriptor(xDstProp); + } + } + } + } + } + } + else + SAL_WARN("dbaccess", "There should be a sequence in it!"); + return true; + } + else if ( _rPasteData.has(DataAccessDescriptorProperty::Component) ) // forms or reports + { + Reference<XContent> xContent; + _rPasteData[DataAccessDescriptorProperty::Component] >>= xContent; + return insertHierachyElement(_eType,_sParentFolder,Reference<XNameAccess>(xContent,UNO_QUERY).is(),xContent,_bMove); + } + } + catch(const SQLException&) { showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); } + catch(const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return false; +} + +Reference<XNameContainer> OApplicationController::getQueryDefinitions() const +{ + Reference<XQueryDefinitionsSupplier> xSet(m_xDataSource,UNO_QUERY); + Reference<XNameContainer> xNames; + if ( xSet.is() ) + { + xNames.set(xSet->getQueryDefinitions(),UNO_QUERY); + } + return xNames; +} + +void OApplicationController::getSupportedFormats(ElementType _eType,std::vector<SotClipboardFormatId>& _rFormatIds) +{ + switch( _eType ) + { + case E_TABLE: + _rFormatIds.push_back(SotClipboardFormatId::DBACCESS_TABLE); + _rFormatIds.push_back(SotClipboardFormatId::RTF); + _rFormatIds.push_back(SotClipboardFormatId::HTML); + [[fallthrough]]; + case E_QUERY: + _rFormatIds.push_back(SotClipboardFormatId::DBACCESS_QUERY); + break; + default: + break; + } +} + +bool OApplicationController::isTableFormat() const +{ + return OTableCopyHelper::isTableFormat(getViewClipboard()); +} + +IMPL_LINK_NOARG( OApplicationController, OnAsyncDrop, void*, void ) +{ + m_nAsyncDrop = nullptr; + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( m_aAsyncDrop.nType == E_TABLE ) + { + SharedConnection xConnection( ensureConnection() ); + if ( xConnection.is() ) + m_aTableCopyHelper.asyncCopyTagTable( m_aAsyncDrop, getDatabaseName(), xConnection ); + } + else + { + if ( paste(m_aAsyncDrop.nType,m_aAsyncDrop.aDroppedData,m_aAsyncDrop.aUrl,m_aAsyncDrop.nAction == DND_ACTION_MOVE) + && m_aAsyncDrop.nAction == DND_ACTION_MOVE ) + { + Reference<XContent> xContent; + m_aAsyncDrop.aDroppedData[DataAccessDescriptorProperty::Component] >>= xContent; + std::vector< OUString> aList; + sal_Int32 nIndex = 0; + OUString sName = xContent->getIdentifier()->getContentIdentifier(); + OUString sErase = sName.getToken(0,'/',nIndex); // we don't want to have the "private:forms" part + if ( nIndex != -1 ) + { + aList.push_back(sName.copy(sErase.getLength() + 1)); + deleteObjects( m_aAsyncDrop.nType, aList, false ); + } + } + } + + m_aAsyncDrop.aDroppedData.clear(); +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppControllerGen.cxx b/dbaccess/source/ui/app/AppControllerGen.cxx new file mode 100644 index 000000000..2466f22df --- /dev/null +++ b/dbaccess/source/ui/app/AppControllerGen.cxx @@ -0,0 +1,741 @@ +/* -*- 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 "AppController.hxx" +#include "AppDetailView.hxx" +#include "AppView.hxx" +#include <core_resource.hxx> +#include <dbaccess_slotid.hrc> +#include <strings.hrc> +#include <strings.hxx> +#include <defaultobjectnamecheck.hxx> +#include <dlgsave.hxx> +#include <UITools.hxx> +#include "subcomponentmanager.hxx" + +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/container/XHierarchicalNameContainer.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdbcx/XRename.hpp> +#include <com/sun/star/sdb/ErrorCondition.hpp> +#include <com/sun/star/sdb/application/DatabaseObject.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/util/XRefreshable.hpp> + +#include <cppuhelper/exc_hlp.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/sqlerror.hxx> +#include <sfx2/mailmodelapi.hxx> +#include <svx/dbaexchange.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <vcl/weld.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/svapp.hxx> +#include <vcl/syswin.hxx> +#include <osl/mutex.hxx> + +namespace dbaui +{ +using namespace ::dbtools; +using namespace ::connectivity; +using namespace ::svx; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::awt; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui::dialogs; +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::ucb; + +using ::com::sun::star::ui::XContextMenuInterceptor; + +namespace DatabaseObject = ::com::sun::star::sdb::application::DatabaseObject; +namespace ErrorCondition = ::com::sun::star::sdb::ErrorCondition; + +void OApplicationController::convertToView(const OUString& _sName) +{ + try + { + SharedConnection xConnection( getConnection() ); + Reference< XQueriesSupplier > xSup( xConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xQueries( xSup->getQueries(), UNO_SET_THROW ); + Reference< XPropertySet > xSourceObject( xQueries->getByName( _sName ), UNO_QUERY_THROW ); + + Reference< XTablesSupplier > xTablesSup( xConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xTables( xTablesSup->getTables(), UNO_SET_THROW ); + + Reference< XDatabaseMetaData > xMeta = xConnection->getMetaData(); + + const OUString aDefaultName = ::dbaui::createDefaultName(xMeta, xTables, DBA_RES(STR_TBL_TITLE).getToken(0, ' ')); + + DynamicTableOrQueryNameCheck aNameChecker( xConnection, CommandType::TABLE ); + OSaveAsDlg aDlg(getFrameWeld(), CommandType::TABLE, getORB(), xConnection, aDefaultName, aNameChecker, SADFlags::NONE); + if (aDlg.run() == RET_OK) + { + OUString sName = aDlg.getName(); + OUString sCatalog = aDlg.getCatalog(); + OUString sSchema = aDlg.getSchema(); + OUString sNewName( + ::dbtools::composeTableName( xMeta, sCatalog, sSchema, sName, false, ::dbtools::EComposeRule::InTableDefinitions ) ); + Reference<XPropertySet> xView = ::dbaui::createView(sNewName,xConnection,xSourceObject); + if ( !xView.is() ) + throw SQLException(DBA_RES(STR_NO_TABLE_FORMAT_INSIDE),*this, "S1000",0,Any()); + getContainer()->elementAdded(E_TABLE,sNewName,makeAny(xView)); + } + } + catch(const SQLException& ) + { + showError( SQLExceptionInfo( ::cppu::getCaughtException() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OApplicationController::pasteFormat(SotClipboardFormatId _nFormatId) +{ + if ( _nFormatId == SotClipboardFormatId::NONE ) + return; + + try + { + const TransferableDataHelper& rClipboard = getViewClipboard(); + ElementType eType = getContainer()->getElementType(); + if ( eType == E_TABLE ) + { + m_aTableCopyHelper.pasteTable( _nFormatId, rClipboard, getDatabaseName(), ensureConnection() ); + } + else + paste( eType, ODataAccessObjectTransferable::extractObjectDescriptor( rClipboard ) ); + + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OApplicationController::openDialog( const OUString& _sServiceName ) +{ + try + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + weld::WaitObject aWO(getFrameWeld()); + + Sequence< Any > aArgs(3); + sal_Int32 nArgPos = 0; + + Reference< css::awt::XWindow> xWindow = getTopMostContainerWindow(); + if ( !xWindow.is() ) + { + OSL_ENSURE( getContainer(), "OApplicationController::Construct: have no view!" ); + if ( getContainer() ) + xWindow = VCLUnoHelper::GetInterface(getView()->Window::GetParent()); + } + // the parent window + aArgs[nArgPos++] <<= PropertyValue( "ParentWindow", + 0, + makeAny(xWindow), + PropertyState_DIRECT_VALUE); + + // the initial selection + OUString sInitialSelection; + if ( getContainer() ) + sInitialSelection = getDatabaseName(); + if ( !sInitialSelection.isEmpty() ) + { + aArgs[ nArgPos++ ] <<= PropertyValue( + "InitialSelection", 0, + makeAny( sInitialSelection ), PropertyState_DIRECT_VALUE ); + } + + SharedConnection xConnection( getConnection() ); + if ( xConnection.is() ) + { + aArgs[ nArgPos++ ] <<= PropertyValue( + PROPERTY_ACTIVE_CONNECTION, 0, + makeAny( xConnection ), PropertyState_DIRECT_VALUE ); + } + aArgs.realloc( nArgPos ); + + // create the dialog + Reference< XExecutableDialog > xAdminDialog; + xAdminDialog.set( + getORB()->getServiceManager()->createInstanceWithArgumentsAndContext(_sServiceName, aArgs, getORB()), + UNO_QUERY); + + // execute it + if (xAdminDialog.is()) + xAdminDialog->execute(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OApplicationController::refreshTables() +{ + if ( !(getContainer() && getContainer()->getDetailView()) ) + return; + + weld::WaitObject aWO(getFrameWeld()); + OSL_ENSURE(getContainer()->getElementType() == E_TABLE,"Only allowed when the tables container is selected!"); + try + { + Reference<XRefreshable> xRefresh(getElements(E_TABLE),UNO_QUERY); + if ( xRefresh.is() ) + xRefresh->refresh(); + } + catch(const Exception&) + { + OSL_FAIL("Could not refresh tables!"); + } + + getContainer()->getDetailView()->clearPages(false); + getContainer()->getDetailView()->createTablesPage( ensureConnection() ); +} + +void SAL_CALL OApplicationController::propertyChange( const PropertyChangeEvent& evt ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + if ( evt.PropertyName == PROPERTY_USER ) + { + m_bNeedToReconnect = true; + InvalidateFeature(SID_DB_APP_STATUS_USERNAME); + } + else if ( evt.PropertyName == PROPERTY_URL ) + { + m_bNeedToReconnect = true; + InvalidateFeature(SID_DB_APP_STATUS_DBNAME); + InvalidateFeature(SID_DB_APP_STATUS_TYPE); + InvalidateFeature(SID_DB_APP_STATUS_HOSTNAME); + } + else if ( PROPERTY_NAME == evt.PropertyName ) + { + const ElementType eType = getContainer()->getElementType(); + if ( eType == E_FORM || eType == E_REPORT ) + { + OUString sOldName,sNewName; + evt.OldValue >>= sOldName; + evt.NewValue >>= sNewName; + + // if the old name is empty, then this is a newly inserted content. We're notified of it via the + // elementInserted method, so there's no need to handle it here. + + if ( !sOldName.isEmpty() ) + { + Reference<XChild> xChild(evt.Source,UNO_QUERY); + if ( xChild.is() ) + { + Reference<XContent> xContent(xChild->getParent(),UNO_QUERY); + if ( xContent.is() ) + sOldName = xContent->getIdentifier()->getContentIdentifier() + "/" + sOldName; + } + + getContainer()->elementReplaced( eType , sOldName, sNewName ); + } + } + } + + EventObject aEvt; + aEvt.Source = m_xModel; + modified(aEvt); +} + +Reference< XDataSource > SAL_CALL OApplicationController::getDataSource() +{ + ::osl::MutexGuard aGuard( getMutex() ); + Reference< XDataSource > xDataSource( m_xDataSource, UNO_QUERY ); + return xDataSource; +} + +Reference< XWindow > SAL_CALL OApplicationController::getApplicationMainWindow() +{ + ::osl::MutexGuard aGuard( getMutex() ); + Reference< XFrame > xFrame( getFrame(), UNO_SET_THROW ); + Reference< XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW ); + return xWindow; +} + +Sequence< Reference< XComponent > > SAL_CALL OApplicationController::getSubComponents() +{ + ::osl::MutexGuard aGuard( getMutex() ); + return m_pSubComponentManager->getSubComponents(); +} + +Reference< XConnection > SAL_CALL OApplicationController::getActiveConnection() +{ + ::osl::MutexGuard aGuard( getMutex() ); + return m_xDataSourceConnection.getTyped(); +} + +sal_Bool SAL_CALL OApplicationController::isConnected( ) +{ + ::osl::MutexGuard aGuard( getMutex() ); + return m_xDataSourceConnection.is(); +} + +void SAL_CALL OApplicationController::connect( ) +{ + SQLExceptionInfo aError; + SharedConnection xConnection = ensureConnection( &aError ); + if ( !xConnection.is() ) + { + if ( aError.isValid() ) + aError.doThrow(); + + // no particular error, but nonetheless could not connect -> throw a generic exception + OUString sConnectingContext( DBA_RES( STR_COULDNOTCONNECT_DATASOURCE ) ); + ::dbtools::throwGenericSQLException( sConnectingContext.replaceFirst( "$name$", getStrippedDatabaseName() ), *this ); + } +} + +beans::Pair< ::sal_Int32, OUString > SAL_CALL OApplicationController::identifySubComponent( const Reference< XComponent >& i_rSubComponent ) +{ + ::osl::MutexGuard aGuard( getMutex() ); + + sal_Int32 nType = -1; + OUString sName; + + if ( !m_pSubComponentManager->lookupSubComponent( i_rSubComponent, sName, nType ) ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + if ( nType == SID_DB_APP_DSRELDESIGN ) + // this is somewhat hacky ... we're expected to return a DatabaseObject value. However, there is no such + // value for the relation design. /me thinks we should change the API definition here ... + nType = -1; + + return beans::Pair< ::sal_Int32, OUString >( nType, sName ); +} + +sal_Bool SAL_CALL OApplicationController::closeSubComponents( ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + return m_pSubComponentManager->closeSubComponents(); +} + +namespace +{ + ElementType lcl_objectType2ElementType( const sal_Int32 _nObjectType ) + { + ElementType eType( E_NONE ); + switch ( _nObjectType ) + { + case DatabaseObject::TABLE: eType = E_TABLE; break; + case DatabaseObject::QUERY: eType = E_QUERY; break; + case DatabaseObject::FORM: eType = E_FORM; break; + case DatabaseObject::REPORT: eType = E_REPORT; break; + default: + OSL_FAIL( "lcl_objectType2ElementType: unsupported object type!" ); + // this should have been caught earlier + } + return eType; + } +} + +void OApplicationController::impl_validateObjectTypeAndName_throw( const sal_Int32 _nObjectType, const ::std::optional< OUString >& i_rObjectName ) +{ + // ensure we're connected + if ( !isConnected() ) + { + SQLError aError; + aError.raiseException( ErrorCondition::DB_NOT_CONNECTED, *this ); + } + + // ensure a proper object type + if ( ( _nObjectType != DatabaseObject::TABLE ) + && ( _nObjectType != DatabaseObject::QUERY ) + && ( _nObjectType != DatabaseObject::FORM ) + && ( _nObjectType != DatabaseObject::REPORT ) + ) + throw IllegalArgumentException( OUString(), *this, 1 ); + + if ( !i_rObjectName ) + return; + + // ensure an existing object + Reference< XNameAccess > xContainer( getElements( lcl_objectType2ElementType( _nObjectType ) ) ); + if ( !xContainer.is() ) + // all possible reasons for this (e.g. not being connected currently) should + // have been handled before + throw RuntimeException( OUString(), *this ); + + bool bExistentObject = false; + switch ( _nObjectType ) + { + case DatabaseObject::TABLE: + case DatabaseObject::QUERY: + bExistentObject = xContainer->hasByName( *i_rObjectName ); + break; + case DatabaseObject::FORM: + case DatabaseObject::REPORT: + { + Reference< XHierarchicalNameAccess > xHierarchy( xContainer, UNO_QUERY_THROW ); + bExistentObject = xHierarchy->hasByHierarchicalName( *i_rObjectName ); + } + break; + } + + if ( !bExistentObject ) + throw NoSuchElementException( *i_rObjectName, *this ); +} + +Reference< XComponent > SAL_CALL OApplicationController::loadComponent( ::sal_Int32 ObjectType, + const OUString& ObjectName, sal_Bool ForEditing ) +{ + return loadComponentWithArguments( ObjectType, ObjectName, ForEditing, Sequence< PropertyValue >() ); +} + +Reference< XComponent > SAL_CALL OApplicationController::loadComponentWithArguments( ::sal_Int32 ObjectType, + const OUString& ObjectName, sal_Bool ForEditing, const Sequence< PropertyValue >& Arguments ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + impl_validateObjectTypeAndName_throw( ObjectType, ObjectName ); + + Reference< XComponent > xComponent( openElementWithArguments( + ObjectName, + lcl_objectType2ElementType( ObjectType ), + ForEditing ? E_OPEN_DESIGN : E_OPEN_NORMAL, + ForEditing ? SID_DB_APP_EDIT : SID_DB_APP_OPEN, + ::comphelper::NamedValueCollection( Arguments ) + ) ); + + return xComponent; +} + +Reference< XComponent > SAL_CALL OApplicationController::createComponent( ::sal_Int32 i_nObjectType, Reference< XComponent >& o_DocumentDefinition ) +{ + return createComponentWithArguments( i_nObjectType, Sequence< PropertyValue >(), o_DocumentDefinition ); +} + +Reference< XComponent > SAL_CALL OApplicationController::createComponentWithArguments( ::sal_Int32 i_nObjectType, const Sequence< PropertyValue >& i_rArguments, Reference< XComponent >& o_DocumentDefinition ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + impl_validateObjectTypeAndName_throw( i_nObjectType, ::std::optional< OUString >() ); + + Reference< XComponent > xComponent( newElement( + lcl_objectType2ElementType( i_nObjectType ), + ::comphelper::NamedValueCollection( i_rArguments ), + o_DocumentDefinition + ) ); + + return xComponent; +} + +void SAL_CALL OApplicationController::registerContextMenuInterceptor( const Reference< XContextMenuInterceptor >& Interceptor ) +{ + if ( Interceptor.is() ) + m_aContextMenuInterceptors.addInterface( Interceptor ); +} + +void SAL_CALL OApplicationController::releaseContextMenuInterceptor( const Reference< XContextMenuInterceptor >& Interceptor ) +{ + m_aContextMenuInterceptors.removeInterface( Interceptor ); +} + +void OApplicationController::previewChanged( sal_Int32 _nMode ) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + if ( m_xDataSource.is() && !isDataSourceReadOnly() ) + { + try + { + ::comphelper::NamedValueCollection aLayoutInfo( m_xDataSource->getPropertyValue( PROPERTY_LAYOUTINFORMATION ) ); + sal_Int32 nOldMode = aLayoutInfo.getOrDefault( "Preview", _nMode ); + if ( nOldMode != _nMode ) + { + aLayoutInfo.put( "Preview", _nMode ); + m_xDataSource->setPropertyValue( PROPERTY_LAYOUTINFORMATION, makeAny( aLayoutInfo.getPropertyValues() ) ); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + InvalidateFeature(SID_DB_APP_DISABLE_PREVIEW); + InvalidateFeature(SID_DB_APP_VIEW_DOCINFO_PREVIEW); + InvalidateFeature(SID_DB_APP_VIEW_DOC_PREVIEW); +} + +void OApplicationController::askToReconnect() +{ + if ( !m_bNeedToReconnect ) + return; + + m_bNeedToReconnect = false; + bool bClear = true; + if ( !m_pSubComponentManager->empty() ) + { + std::unique_ptr<weld::MessageDialog> xQry(Application::CreateMessageDialog(getFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + DBA_RES(STR_QUERY_CLOSEDOCUMENTS))); + switch (xQry->run()) + { + case RET_YES: + closeSubComponents(); + break; + default: + bClear = false; + break; + } + } + if ( bClear ) + { + ElementType eType = getContainer()->getElementType(); + disconnect(); + getContainer()->getDetailView()->clearPages(false); + getContainer()->selectContainer(E_NONE); // invalidate the old selection + m_eCurrentType = E_NONE; + getContainer()->selectContainer(eType); // reselect the current one again + } +} + +OUString OApplicationController::getDatabaseName() const +{ + OUString sDatabaseName; + try + { + if ( m_xDataSource.is() ) + { + OSL_VERIFY( m_xDataSource->getPropertyValue( PROPERTY_NAME ) >>= sDatabaseName ); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return sDatabaseName; +} + +OUString OApplicationController::getStrippedDatabaseName() const +{ + OUString sDatabaseName; + return ::dbaui::getStrippedDatabaseName( m_xDataSource, sDatabaseName ); +} + +void OApplicationController::onDocumentOpened( const OUString& _rName, const sal_Int32 _nType, + const ElementOpenMode _eMode, const Reference< XComponent >& _xDocument, const Reference< XComponent >& _rxDefinition ) +{ + if ( !_xDocument.is() ) + return; + + try + { + OSL_ENSURE( _xDocument.is(), "OApplicationController::onDocumentOpened: is there any *valid* scenario where this fails?" ); + m_pSubComponentManager->onSubComponentOpened( _rName, _nType, _eMode, _xDocument.is() ? _xDocument : _rxDefinition ); + + if ( _rxDefinition.is() ) + { + Reference< XPropertySet > xProp( _rxDefinition, UNO_QUERY_THROW ); + Reference< XPropertySetInfo > xPSI( xProp->getPropertySetInfo(), UNO_SET_THROW ); + xProp->addPropertyChangeListener( PROPERTY_NAME, static_cast< XPropertyChangeListener* >( this ) ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +bool OApplicationController::insertHierachyElement(ElementType _eType, const OUString& _sParentFolder, bool _bCollection, const Reference<XContent>& _xContent, bool _bMove) +{ + Reference<XHierarchicalNameContainer> xNames(getElements(_eType), UNO_QUERY); + return dbaui::insertHierachyElement(getFrameWeld() + ,getORB() + ,xNames + ,_sParentFolder + ,_eType == E_FORM + ,_bCollection + ,_xContent + ,_bMove); +} + +bool OApplicationController::isRenameDeleteAllowed(ElementType _eType, bool _bDelete) const +{ + ElementType eType = getContainer()->getElementType(); + bool bEnabled = !isDataSourceReadOnly() && eType == _eType; + if ( bEnabled ) + { + + if ( E_TABLE == eType ) + bEnabled = !isConnectionReadOnly() && getContainer()->isALeafSelected(); + + bool bCompareRes = false; + if ( _bDelete ) + bCompareRes = getContainer()->getSelectionCount() > 0; + else + { + bCompareRes = getContainer()->getSelectionCount() == 1; + if ( bEnabled && bCompareRes && E_TABLE == eType ) + { + std::vector< OUString> aList; + getSelectionElementNames(aList); + + try + { + Reference< XNameAccess > xContainer = const_cast<OApplicationController*>(this)->getElements(eType); + bEnabled = (xContainer.is() && xContainer->hasByName(*aList.begin())); + if ( bEnabled ) + bEnabled = Reference<XRename>(xContainer->getByName(*aList.begin()),UNO_QUERY).is(); + } + catch(Exception&) + { + bEnabled = false; + } + } + } + + bEnabled = bEnabled && bCompareRes; + } + return bEnabled; +} + +void OApplicationController::onLoadedMenu(const Reference< css::frame::XLayoutManager >& _xLayoutManager) +{ + + if ( !_xLayoutManager.is() ) + return; + + static const char s_sStatusbar[] = "private:resource/statusbar/statusbar"; + _xLayoutManager->createElement( s_sStatusbar ); + _xLayoutManager->requestElement( s_sStatusbar ); + + if ( getContainer() ) + { + // we need to share the "mnemonic space": + MnemonicGenerator aMnemonicGenerator; + // - the menu already has mnemonics + SystemWindow* pSystemWindow = getContainer()->GetSystemWindow(); + MenuBar* pMenu = pSystemWindow ? pSystemWindow->GetMenuBar() : nullptr; + if ( pMenu ) + { + sal_uInt16 nMenuItems = pMenu->GetItemCount(); + for ( sal_uInt16 i = 0; i < nMenuItems; ++i ) + aMnemonicGenerator.RegisterMnemonic( pMenu->GetItemText( pMenu->GetItemId( i ) ) ); + } + // - the icons should use automatic ones + getContainer()->createIconAutoMnemonics( aMnemonicGenerator ); + // - as well as the entries in the task pane + getContainer()->setTaskExternalMnemonics( aMnemonicGenerator ); + } + + Execute( SID_DB_APP_VIEW_FORMS, Sequence< PropertyValue >() ); + InvalidateAll(); +} + +void OApplicationController::doAction(sal_uInt16 _nId, const ElementOpenMode _eOpenMode) +{ + std::vector< OUString> aList; + getSelectionElementNames(aList); + ElementType eType = getContainer()->getElementType(); + ::comphelper::NamedValueCollection aArguments; + ElementOpenMode eOpenMode = _eOpenMode; + if ( eType == E_REPORT && E_OPEN_FOR_MAIL == _eOpenMode ) + { + aArguments.put("Hidden",true); + eOpenMode = E_OPEN_NORMAL; + } + + std::vector< std::pair< OUString ,Reference< XModel > > > aComponents; + for (auto const& elem : aList) + { + if ( SID_DB_APP_CONVERTTOVIEW == _nId ) + convertToView(elem); + else + { + Reference< XModel > xModel( openElementWithArguments( elem, eType, eOpenMode, _nId,aArguments ), UNO_QUERY ); + aComponents.emplace_back( elem, xModel ); + } + } + + // special handling for mail, if more than one document is selected attach them all + if ( _eOpenMode != E_OPEN_FOR_MAIL ) + return; + + + SfxMailModel aSendMail; + SfxMailModel::SendMailResult eResult = SfxMailModel::SEND_MAIL_OK; + for (auto const& component : aComponents) + { + try + { + Reference< XModel > xModel = component.second; + + // Send document as e-Mail using stored/default type + eResult = aSendMail.AttachDocument(xModel,component.first); + ::comphelper::disposeComponent(xModel); + if (eResult != SfxMailModel::SEND_MAIL_OK) + break; + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + if ( !aSendMail.IsEmpty() ) + aSendMail.Send( getFrame() ); +} + +ElementType OApplicationController::getElementType(const Reference< XContainer >& _xContainer) +{ + ElementType eRet = E_NONE; + Reference<XServiceInfo> xServiceInfo(_xContainer,UNO_QUERY); + if ( xServiceInfo.is() ) + { + if ( xServiceInfo->supportsService(SERVICE_SDBCX_TABLES) ) + eRet = E_TABLE; + else if ( xServiceInfo->supportsService(SERVICE_NAME_FORM_COLLECTION) ) + eRet = E_FORM; + else if ( xServiceInfo->supportsService(SERVICE_NAME_REPORT_COLLECTION) ) + eRet = E_REPORT; + else + eRet = E_QUERY; + } + return eRet; +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppDetailPageHelper.cxx b/dbaccess/source/ui/app/AppDetailPageHelper.cxx new file mode 100644 index 000000000..502ad243c --- /dev/null +++ b/dbaccess/source/ui/app/AppDetailPageHelper.cxx @@ -0,0 +1,1305 @@ +/* -*- 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 "AppDetailPageHelper.hxx" +#include <tools/diagnose_ex.h> +#include <tabletree.hxx> +#include <dbtreelistbox.hxx> +#include <com/sun/star/awt/XTabController.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/form/XLoadable.hpp> +#include <com/sun/star/frame/thePopupMenuControllerFactory.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/frame/Frame.hpp> +#include <com/sun/star/frame/XFrames.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/sdb/application/XDatabaseDocumentUI.hpp> +#include <com/sun/star/sdb/application/DatabaseObject.hpp> +#include <com/sun/star/sdb/application/DatabaseObjectContainer.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/ucb/Command.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/string.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include "AppView.hxx" +#include <helpids.h> +#include <strings.hxx> +#include <dbaccess_slotid.hrc> +#include <databaseobjectview.hxx> +#include <imageprovider.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/settings.hxx> +#include <vcl/cvtgrf.hxx> +#include <vcl/event.hxx> +#include <toolkit/awt/vclxmenu.hxx> +#include <tools/stream.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/treelistentry.hxx> +#include "AppController.hxx" + +#include <com/sun/star/document/XDocumentProperties.hpp> + +#include <memory> + +using namespace ::dbaui; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::form; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdb::application; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star; +using ::com::sun::star::awt::XTabController; + +namespace dbaui +{ + namespace DatabaseObject = css::sdb::application::DatabaseObject; + namespace DatabaseObjectContainer = css::sdb::application::DatabaseObjectContainer; +} + +namespace +{ + SvTreeListEntry* lcl_findEntry_impl(DBTreeListBox const & rTree, const OUString& _rName, SvTreeListEntry* _pFirst) + { + SvTreeListEntry* pReturn = nullptr; + sal_Int32 nIndex = 0; + OUString sName( _rName.getToken(0,'/',nIndex) ); + + SvTreeListEntry* pEntry = _pFirst; + while( pEntry ) + { + if ( rTree.GetEntryText(pEntry) == sName ) + { + if ( nIndex != -1 ) + { + sName = _rName.getToken(0,'/',nIndex); + pEntry = rTree.FirstChild(pEntry); + } + else + { + pReturn = pEntry; + break; + } + } + else + pEntry = pEntry->NextSibling(); + } + return pReturn; + } + SvTreeListEntry* lcl_findEntry(DBTreeListBox const & rTree, const OUString& _rName,SvTreeListEntry* _pFirst) + { + sal_Int32 nIndex = 0; + OUString sErase = _rName.getToken(0,'/',nIndex); // we don't want to have the "private:forms" part + return (nIndex != -1 ? lcl_findEntry_impl(rTree,_rName.copy(sErase.getLength() + 1),_pFirst) : nullptr); + } + class OTablePreviewWindow : public vcl::Window + { + DECL_LINK(OnDisableInput, void*, void); + void ImplInitSettings(); + protected: + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + public: + OTablePreviewWindow( vcl::Window* pParent, WinBits nStyle ); + virtual bool EventNotify( NotifyEvent& rNEvt ) override; + }; + OTablePreviewWindow::OTablePreviewWindow(vcl::Window* pParent, WinBits nStyle) : Window( pParent, nStyle) + { + ImplInitSettings(); + } + bool OTablePreviewWindow::EventNotify( NotifyEvent& rNEvt ) + { + bool bRet = Window::EventNotify(rNEvt); + if ( rNEvt.GetType() == MouseNotifyEvent::INPUTENABLE && IsInputEnabled() ) + PostUserEvent( LINK( this, OTablePreviewWindow, OnDisableInput), nullptr, true ); + return bRet; + } + IMPL_LINK_NOARG(OTablePreviewWindow, OnDisableInput, void*, void) + { + EnableInput(false); + } + void OTablePreviewWindow::DataChanged( const DataChangedEvent& rDCEvt ) + { + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + ImplInitSettings(); + Invalidate(); + } + } + void OTablePreviewWindow::ImplInitSettings() + { + //FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); + } + +} + +OAppDetailPageHelper::OAppDetailPageHelper(vcl::Window* _pParent,OAppBorderWindow& _rBorderWin,PreviewMode _ePreviewMode) : Window(_pParent,WB_DIALOGCONTROL) + ,m_rBorderWin(_rBorderWin) + ,m_aFL(VclPtr<FixedLine>::Create(this,WB_VERT)) + ,m_aTBPreview(VclPtr<ToolBox>::Create(this,WB_TABSTOP) ) + ,m_aBorder(VclPtr<Window>::Create(this,WB_BORDER | WB_READONLY)) + ,m_aPreview(VclPtr<OPreviewWindow>::Create(m_aBorder.get())) + ,m_aDocumentInfo(VclPtr< ::svtools::ODocumentInfoPreview>::Create(m_aBorder.get(), WB_LEFT | WB_VSCROLL | WB_READONLY) ) + ,m_ePreviewMode(_ePreviewMode) +{ + m_aBorder->SetBorderStyle(WindowBorderStyle::MONO); + + m_aTBPreview->SetOutStyle(TOOLBOX_STYLE_FLAT); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:DBDisablePreview", + "com.sun.star.sdb.OfficeDatabaseDocument"); + m_aTBPreview->InsertItem(SID_DB_APP_DISABLE_PREVIEW, + vcl::CommandInfoProvider::GetLabelForCommand(aProperties), + ToolBoxItemBits::LEFT|ToolBoxItemBits::DROPDOWNONLY|ToolBoxItemBits::AUTOSIZE|ToolBoxItemBits::RADIOCHECK); + m_aTBPreview->SetHelpId(HID_APP_VIEW_PREVIEW_CB); + m_aTBPreview->SetDropdownClickHdl( LINK( this, OAppDetailPageHelper, OnDropdownClickHdl ) ); + m_aTBPreview->Enable(); + + m_aPreview->SetHelpId(HID_APP_VIEW_PREVIEW_1); + + m_pTablePreview.set( VclPtr<OTablePreviewWindow>::Create(m_aBorder.get(), WB_READONLY | WB_DIALOGCONTROL ) ); + m_pTablePreview->SetHelpId(HID_APP_VIEW_PREVIEW_2); + + m_aDocumentInfo->SetHelpId(HID_APP_VIEW_PREVIEW_3); + + m_xWindow = VCLUnoHelper::GetInterface( m_pTablePreview ); + + for (VclPtr<DBTreeListBox> & rpBox : m_pLists) + rpBox = nullptr; + ImplInitSettings(); +} + +OAppDetailPageHelper::~OAppDetailPageHelper() +{ + disposeOnce(); +} + +void OAppDetailPageHelper::dispose() +{ + try + { + Reference< ::util::XCloseable> xCloseable(m_xFrame,UNO_QUERY); + if ( xCloseable.is() ) + xCloseable->close(true); + m_xFrame.clear(); + } + catch(const Exception&) + { + OSL_FAIL("Exception thrown while disposing preview frame!"); + } + + for (VclPtr<DBTreeListBox> & rpBox : m_pLists) + { + if ( rpBox ) + { + rpBox->clearCurrentSelection(); + rpBox->Hide(); + rpBox->clearCurrentSelection(); // why a second time? + rpBox.disposeAndClear(); + } + } + m_pTablePreview.disposeAndClear(); + m_aDocumentInfo.disposeAndClear(); + m_aPreview.disposeAndClear(); + m_aBorder.disposeAndClear(); + m_aTBPreview.disposeAndClear(); + m_aFL.disposeAndClear(); + + vcl::Window::dispose(); +} + +int OAppDetailPageHelper::getVisibleControlIndex() const +{ + int i = 0; + for (; i < E_ELEMENT_TYPE_COUNT ; ++i) + { + if ( m_pLists[i] && m_pLists[i]->IsVisible() ) + break; + } + return i; +} + +void OAppDetailPageHelper::selectAll() +{ + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + { + m_pLists[nPos]->SelectAll(true); + } +} + +void OAppDetailPageHelper::sort(int _nPos,SvSortMode _eSortMode ) +{ + OSL_ENSURE(m_pLists[_nPos],"List can not be NULL! ->GPF"); + SvTreeList* pModel = m_pLists[_nPos]->GetModel(); + SvSortMode eOldSortMode = pModel->GetSortMode(); + pModel->SetSortMode(_eSortMode); + if ( eOldSortMode != _eSortMode ) + pModel->Resort(); +} + +bool OAppDetailPageHelper::isSortUp() const +{ + SvSortMode eSortMode = SortNone; + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + { + SvTreeList* pModel = m_pLists[nPos]->GetModel(); + eSortMode = pModel->GetSortMode(); + } + return eSortMode == SortAscending; +} + +void OAppDetailPageHelper::sortDown() +{ + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + sort(nPos,SortDescending); +} + +void OAppDetailPageHelper::sortUp() +{ + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + sort(nPos,SortAscending); +} + +void OAppDetailPageHelper::getSelectionElementNames( std::vector< OUString>& _rNames ) const +{ + int nPos = getVisibleControlIndex(); + if ( nPos >= E_ELEMENT_TYPE_COUNT ) + return; + + DBTreeListBox& rTree = *m_pLists[nPos]; + sal_Int32 nCount = rTree.GetEntryCount(); + _rNames.reserve(nCount); + SvTreeListEntry* pEntry = rTree.FirstSelected(); + ElementType eType = getElementType(); + while( pEntry ) + { + if ( eType == E_TABLE ) + { + if( rTree.GetChildCount(pEntry) == 0 ) + _rNames.push_back( getQualifiedName( pEntry ) ); + } + else + { + OUString sName = rTree.GetEntryText(pEntry); + SvTreeListEntry* pParent = rTree.GetParent(pEntry); + while(pParent) + { + sName = rTree.GetEntryText(pParent) + "/" + sName; + pParent = rTree.GetParent(pParent); + } + _rNames.push_back(sName); + } + pEntry = rTree.NextSelected(pEntry); + } +} + +void OAppDetailPageHelper::describeCurrentSelectionForControl( const Control& _rControl, Sequence< NamedDatabaseObject >& _out_rSelectedObjects ) +{ + for (size_t i=0; i < E_ELEMENT_TYPE_COUNT; ++i) + { + if ( m_pLists[i] == &_rControl ) + { + describeCurrentSelectionForType(static_cast<ElementType>(i), _out_rSelectedObjects); + return; + } + } + OSL_FAIL( "OAppDetailPageHelper::describeCurrentSelectionForControl: invalid control!" ); +} + +void OAppDetailPageHelper::describeCurrentSelectionForType( const ElementType _eType, Sequence< NamedDatabaseObject >& _out_rSelectedObjects ) +{ + OSL_ENSURE( _eType < E_ELEMENT_TYPE_COUNT, "OAppDetailPageHelper::describeCurrentSelectionForType: invalid type!" ); + DBTreeListBox* pList = ( _eType < E_ELEMENT_TYPE_COUNT ) ? m_pLists[ _eType ].get() : nullptr; + OSL_ENSURE( pList, "OAppDetailPageHelper::describeCurrentSelectionForType: " + "You really should ensure this type has already been viewed before!" ); + if ( !pList ) + return; + + std::vector< NamedDatabaseObject > aSelected; + + SvTreeListEntry* pEntry = pList->FirstSelected(); + while( pEntry ) + { + NamedDatabaseObject aObject; + switch ( _eType ) + { + case E_TABLE: + { + OTableTreeListBox& rTableTree = dynamic_cast< OTableTreeListBox& >( *pList ); + aObject = rTableTree.describeObject( pEntry ); + } + break; + case E_QUERY: + aObject.Type = DatabaseObject::QUERY; + aObject.Name = pList->GetEntryText( pEntry ); + break; + + case E_FORM: + case E_REPORT: + { + OUString sName = pList->GetEntryText(pEntry); + SvTreeListEntry* pParent = pList->GetParent(pEntry); + while ( pParent ) + { + OUStringBuffer buffer; + buffer.append( pList->GetEntryText( pParent ) ); + buffer.append( '/' ); + buffer.append( sName ); + sName = buffer.makeStringAndClear(); + + pParent = pList->GetParent( pParent ); + } + + if ( isLeaf( pEntry ) ) + aObject.Type = ( _eType == E_FORM ) ? DatabaseObject::FORM : DatabaseObject::REPORT; + else + aObject.Type = ( _eType == E_FORM ) ? DatabaseObjectContainer::FORMS_FOLDER : DatabaseObjectContainer::REPORTS_FOLDER; + aObject.Name = sName; + } + break; + default: + OSL_FAIL( "OAppDetailPageHelper::describeCurrentSelectionForType: unexpected type!" ); + break; + } + + if ( !aObject.Name.isEmpty() ) + { + aSelected.push_back( aObject ); + } + + pEntry = pList->NextSelected(pEntry); + } + + _out_rSelectedObjects = comphelper::containerToSequence( aSelected ); +} + +void OAppDetailPageHelper::selectElements(const Sequence< OUString>& _aNames) +{ + int nPos = getVisibleControlIndex(); + if ( nPos >= E_ELEMENT_TYPE_COUNT ) + return; + + DBTreeListBox& rTree = *m_pLists[nPos]; + rTree.SelectAll(false); + const OUString* pIter = _aNames.getConstArray(); + const OUString* pEnd = pIter + _aNames.getLength(); + for(;pIter != pEnd;++pIter) + { + SvTreeListEntry* pEntry = rTree.GetEntryPosByName(*pIter); + if ( pEntry ) + rTree.Select(pEntry); + } +} + +OUString OAppDetailPageHelper::getQualifiedName( SvTreeListEntry* _pEntry ) const +{ + int nPos = getVisibleControlIndex(); + OUString sComposedName; + + if ( nPos >= E_ELEMENT_TYPE_COUNT ) + return sComposedName; + + OSL_ENSURE(m_pLists[nPos],"Tables tree view is NULL! -> GPF"); + DBTreeListBox& rTree = *m_pLists[nPos]; + + SvTreeListEntry* pEntry = _pEntry; + if ( !pEntry ) + pEntry = rTree.FirstSelected(); + + if ( !pEntry ) + return sComposedName; + + if ( getElementType() == E_TABLE ) + { + const OTableTreeListBox& rTreeView = dynamic_cast< const OTableTreeListBox& >( *m_pLists[nPos] ); + sComposedName = rTreeView.getQualifiedTableName( pEntry ); + } + else + { + sComposedName = rTree.GetEntryText(pEntry); + SvTreeListEntry* pParent = rTree.GetParent(pEntry); + while(pParent) + { + sComposedName = rTree.GetEntryText(pParent) + "/" + sComposedName; + pParent = rTree.GetParent(pParent); + } + } + + return sComposedName; +} + +ElementType OAppDetailPageHelper::getElementType() const +{ + int nPos = getVisibleControlIndex(); + return static_cast<ElementType>(nPos); +} + +sal_Int32 OAppDetailPageHelper::getSelectionCount() +{ + sal_Int32 nCount = 0; + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + { + DBTreeListBox& rTree = *m_pLists[nPos]; + SvTreeListEntry* pEntry = rTree.FirstSelected(); + while( pEntry ) + { + ++nCount; + pEntry = rTree.NextSelected(pEntry); + } + } + return nCount; +} + +sal_Int32 OAppDetailPageHelper::getElementCount() const +{ + sal_Int32 nCount = 0; + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + { + nCount = m_pLists[nPos]->GetEntryCount(); + } + return nCount; +} + +bool OAppDetailPageHelper::isLeaf(SvTreeListEntry const * _pEntry) +{ + if ( !_pEntry ) + return false; + sal_Int32 nEntryType = reinterpret_cast< sal_IntPtr >( _pEntry->GetUserData() ); + return !( ( nEntryType == DatabaseObjectContainer::TABLES ) + || ( nEntryType == DatabaseObjectContainer::CATALOG ) + || ( nEntryType == DatabaseObjectContainer::SCHEMA ) + || ( nEntryType == DatabaseObjectContainer::FORMS_FOLDER ) + || ( nEntryType == DatabaseObjectContainer::REPORTS_FOLDER )); +} + +bool OAppDetailPageHelper::isALeafSelected() const +{ + int nPos = getVisibleControlIndex(); + bool bLeafSelected = false; + if ( nPos < E_ELEMENT_TYPE_COUNT ) + { + DBTreeListBox& rTree = *m_pLists[nPos]; + SvTreeListEntry* pEntry = rTree.FirstSelected( ); + while( !bLeafSelected && pEntry ) + { + bLeafSelected = isLeaf( pEntry ); + pEntry = rTree.NextSelected(pEntry); + } + } + return bLeafSelected; +} + +SvTreeListEntry* OAppDetailPageHelper::getEntry( const Point& _aPosPixel) const +{ + SvTreeListEntry* pReturn = nullptr; + int nPos = getVisibleControlIndex(); + if ( nPos < E_ELEMENT_TYPE_COUNT ) + pReturn = m_pLists[nPos]->GetEntry( _aPosPixel, true ); + return pReturn; +} + +void OAppDetailPageHelper::createTablesPage(const Reference< XConnection>& _xConnection) +{ + OSL_ENSURE(_xConnection.is(),"Connection is NULL! -> GPF"); + + if ( !m_pLists[E_TABLE] ) + { + VclPtrInstance<OTableTreeListBox> pTreeView(this, + WB_HASLINES | WB_SORT | WB_HASBUTTONS | WB_HSCROLL |WB_HASBUTTONSATROOT | WB_TABSTOP); + pTreeView->SetHelpId(HID_APP_TABLE_TREE); + m_pLists[E_TABLE] = pTreeView; + + createTree( pTreeView, + ImageProvider::getDefaultImage( DatabaseObject::TABLE ) + ); + + pTreeView->notifyHiContrastChanged(); + m_aBorder->SetZOrder(pTreeView, ZOrderFlags::Behind); + } + if ( !m_pLists[E_TABLE]->GetEntryCount() ) + { + static_cast<OTableTreeListBox*>(m_pLists[E_TABLE].get())->UpdateTableList(_xConnection); + + SvTreeListEntry* pEntry = m_pLists[E_TABLE]->First(); + if ( pEntry ) + m_pLists[E_TABLE]->Expand(pEntry); + m_pLists[E_TABLE]->SelectAll(false); + } + + setDetailPage(m_pLists[E_TABLE]); +} + +OUString OAppDetailPageHelper::getElementIcons(ElementType _eType) +{ + sal_Int32 nDatabaseObjectType( 0 ); + switch(_eType ) + { + case E_FORM: nDatabaseObjectType = DatabaseObject::FORM; break; + case E_REPORT: nDatabaseObjectType = DatabaseObject::REPORT; break; + case E_QUERY: nDatabaseObjectType = DatabaseObject::QUERY; break; + default: + OSL_FAIL( "OAppDetailPageHelper::GetElementIcons: invalid element type!" ); + return OUString(); + } + + return ImageProvider::getDefaultImageResourceID(nDatabaseObjectType); +} + +void OAppDetailPageHelper::createPage(ElementType _eType,const Reference< XNameAccess >& _xContainer) +{ + OSL_ENSURE(E_TABLE != _eType,"E_TABLE isn't allowed."); + + OString sHelpId; + Image aFolderImage; + switch( _eType ) + { + case E_FORM: + sHelpId = HID_APP_FORM_TREE; + aFolderImage = ImageProvider::getFolderImage( DatabaseObject::FORM ); + break; + case E_REPORT: + sHelpId = HID_APP_REPORT_TREE; + aFolderImage = ImageProvider::getFolderImage( DatabaseObject::REPORT ); + break; + case E_QUERY: + sHelpId = HID_APP_QUERY_TREE; + aFolderImage = ImageProvider::getFolderImage( DatabaseObject::QUERY ); + break; + default: + OSL_FAIL("Illegal call!"); + } + OUString sImageId = getElementIcons(_eType); + + if ( !m_pLists[_eType] ) + { + m_pLists[_eType] = createSimpleTree( sHelpId, aFolderImage ); + } + + if ( m_pLists[_eType] ) + { + if ( !m_pLists[_eType]->GetEntryCount() && _xContainer.is() ) + { + fillNames( _xContainer, _eType, sImageId, nullptr ); + + m_pLists[_eType]->SelectAll(false); + } + setDetailPage(m_pLists[_eType]); + } +} + +void OAppDetailPageHelper::setDetailPage(vcl::Window* _pWindow) +{ + OSL_ENSURE(_pWindow,"OAppDetailPageHelper::setDetailPage: Window is NULL!"); + vcl::Window* pCurrent = getCurrentView(); + if ( pCurrent ) + pCurrent->Hide(); + + showPreview(nullptr); + bool bHasFocus = false; + m_aFL->Show(); + { + bHasFocus = pCurrent != nullptr && pCurrent->HasChildPathFocus(); + _pWindow->Show(); + } + m_aTBPreview->Show(); + m_aBorder->Show(); + switchPreview(m_ePreviewMode,true); + + if ( bHasFocus ) + _pWindow->GrabFocus(); + Resize(); +} + +namespace +{ + namespace DatabaseObjectContainer = ::com::sun::star::sdb::application::DatabaseObjectContainer; + + sal_Int32 lcl_getFolderIndicatorForType( const ElementType _eType ) + { + const sal_Int32 nFolderIndicator = + ( _eType == E_FORM ) ? DatabaseObjectContainer::FORMS_FOLDER + : ( _eType == E_REPORT ) ? DatabaseObjectContainer::REPORTS_FOLDER : -1; + return nFolderIndicator; + } +} + +void OAppDetailPageHelper::fillNames( const Reference< XNameAccess >& _xContainer, const ElementType _eType, + const OUString& rImageId, SvTreeListEntry* _pParent ) +{ + OSL_ENSURE(_xContainer.is(),"Data source is NULL! -> GPF"); + OSL_ENSURE( ( _eType >= E_TABLE ) && ( _eType < E_ELEMENT_TYPE_COUNT ), "OAppDetailPageHelper::fillNames: invalid type!" ); + + DBTreeListBox* pList = m_pLists[ _eType ].get(); + OSL_ENSURE( pList, "OAppDetailPageHelper::fillNames: you really should create the list before calling this!" ); + if ( !pList ) + return; + + if ( !(_xContainer.is() && _xContainer->hasElements()) ) + return; + + const sal_Int32 nFolderIndicator = lcl_getFolderIndicatorForType( _eType ); + + Sequence< OUString> aSeq = _xContainer->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + { + SvTreeListEntry* pEntry = nullptr; + Reference<XNameAccess> xSubElements(_xContainer->getByName(*pIter),UNO_QUERY); + if ( xSubElements.is() ) + { + pEntry = pList->InsertEntry( *pIter, _pParent, false, TREELIST_APPEND, reinterpret_cast< void* >( nFolderIndicator ) ); + getBorderWin().getView()->getAppController().containerFound( Reference< XContainer >( xSubElements, UNO_QUERY ) ); + fillNames( xSubElements, _eType, rImageId, pEntry ); + } + else + { + pEntry = pList->InsertEntry( *pIter, _pParent ); + + Image aImage(StockImage::Yes, rImageId); + pList->SetExpandedEntryBmp(pEntry, aImage); + pList->SetCollapsedEntryBmp(pEntry, aImage); + } + } +} + +DBTreeListBox* OAppDetailPageHelper::createSimpleTree( const OString& _sHelpId, const Image& _rImage) +{ + VclPtrInstance<DBTreeListBox> pTreeView(this, + WB_HASLINES | WB_SORT | WB_HASBUTTONS | WB_HSCROLL |WB_HASBUTTONSATROOT | WB_TABSTOP); + pTreeView->SetHelpId( _sHelpId ); + return createTree( pTreeView, _rImage ); +} + +DBTreeListBox* OAppDetailPageHelper::createTree( DBTreeListBox* _pTreeView, const Image& _rImage ) +{ + weld::WaitObject aWaitCursor(GetFrameWeld()); + + _pTreeView->SetStyle(_pTreeView->GetStyle() | WB_HASLINES | WB_SORT | WB_HASBUTTONS | WB_HSCROLL |WB_HASBUTTONSATROOT | WB_TABSTOP); + _pTreeView->GetModel()->SetSortMode(SortAscending); + _pTreeView->EnableCheckButton( nullptr ); // do not show any buttons + _pTreeView->SetSelectionMode(SelectionMode::Multiple); + + _pTreeView->SetDefaultCollapsedEntryBmp( _rImage ); + _pTreeView->SetDefaultExpandedEntryBmp( _rImage ); + + _pTreeView->SetDoubleClickHdl(LINK(this, OAppDetailPageHelper, OnEntryDoubleClick)); + _pTreeView->SetEnterKeyHdl(LINK(this, OAppDetailPageHelper, OnEntryEnterKey)); + _pTreeView->SetSelChangeHdl(LINK(this, OAppDetailPageHelper, OnEntrySelChange)); + + _pTreeView->setCopyHandler(LINK(this, OAppDetailPageHelper, OnCopyEntry)); + _pTreeView->setPasteHandler(LINK(this, OAppDetailPageHelper, OnPasteEntry)); + _pTreeView->setDeleteHandler(LINK(this, OAppDetailPageHelper, OnDeleteEntry)); + + _pTreeView->setControlActionListener( &getBorderWin().getView()->getAppController() ); + _pTreeView->setContextMenuProvider( &getBorderWin().getView()->getAppController() ); + + return _pTreeView; +} + +void OAppDetailPageHelper::clearPages() +{ + showPreview(nullptr); + for (VclPtr<DBTreeListBox> & rpBox : m_pLists) + { + if ( rpBox ) + rpBox->Clear(); + } +} + +bool OAppDetailPageHelper::isFilled() const +{ + size_t i = 0; + for (; i < E_ELEMENT_TYPE_COUNT && !m_pLists[i]; ++i) + ; + return i != E_ELEMENT_TYPE_COUNT; +} + +void OAppDetailPageHelper::elementReplaced(ElementType _eType + ,const OUString& _rOldName + ,const OUString& _rNewName ) +{ + DBTreeListBox* pTreeView = getCurrentView(); + if ( !pTreeView ) + return; + + SvTreeListEntry* pEntry = nullptr; + switch( _eType ) + { + case E_TABLE: + static_cast<OTableTreeListBox*>(pTreeView)->removedTable( _rOldName ); + static_cast<OTableTreeListBox*>(pTreeView)->addedTable( _rNewName ); + return; + + case E_QUERY: + pEntry = lcl_findEntry_impl(*pTreeView,_rOldName,pTreeView->First()); + break; + case E_FORM: + case E_REPORT: + pEntry = lcl_findEntry(*pTreeView,_rOldName,pTreeView->First()); + break; + default: + OSL_FAIL("Invalid element type"); + } + OSL_ENSURE(pEntry,"Do you know that the name isn't existence!"); + if ( pEntry ) + { + pTreeView->SetEntryText(pEntry,_rNewName); + } +} + +SvTreeListEntry* OAppDetailPageHelper::elementAdded(ElementType _eType,const OUString& _rName, const Any& _rObject ) +{ + SvTreeListEntry* pRet = nullptr; + DBTreeListBox* pTreeView = m_pLists[_eType].get(); + if( _eType == E_TABLE && pTreeView ) + { + pRet = static_cast<OTableTreeListBox*>(pTreeView)->addedTable( _rName ); + } + else if ( pTreeView ) + { + + SvTreeListEntry* pEntry = nullptr; + Reference<XChild> xChild(_rObject,UNO_QUERY); + if ( xChild.is() && E_QUERY != _eType ) + { + Reference<XContent> xContent(xChild->getParent(),UNO_QUERY); + if ( xContent.is() ) + { + OUString sName = xContent->getIdentifier()->getContentIdentifier(); + pEntry = lcl_findEntry(*pTreeView,sName,pTreeView->First()); + } + } + + OUString sImageId = getElementIcons(_eType); + Reference<XNameAccess> xContainer(_rObject,UNO_QUERY); + if ( xContainer.is() ) + { + const sal_Int32 nFolderIndicator = lcl_getFolderIndicatorForType( _eType ); + pRet = pTreeView->InsertEntry( _rName, pEntry, false, TREELIST_APPEND, reinterpret_cast< void* >( nFolderIndicator ) ); + fillNames( xContainer, _eType, sImageId, pRet ); + } + else + { + pRet = pTreeView->InsertEntry( _rName, pEntry ); + + Image aImage(StockImage::Yes, sImageId); + pTreeView->SetExpandedEntryBmp( pRet, aImage ); + pTreeView->SetCollapsedEntryBmp( pRet, aImage ); + } + } + return pRet; +} + +void OAppDetailPageHelper::elementRemoved( ElementType _eType,const OUString& _rName ) +{ + DBTreeListBox* pTreeView = getCurrentView(); + if ( !pTreeView ) + return; + + switch( _eType ) + { + case E_TABLE: + // we don't need to clear the table here, it is already done by the dispose listener + static_cast< OTableTreeListBox* >( pTreeView )->removedTable( _rName ); + break; + case E_QUERY: + if (auto pEntry = lcl_findEntry_impl(*pTreeView, _rName, pTreeView->First())) + pTreeView->GetModel()->Remove(pEntry); + break; + case E_FORM: + case E_REPORT: + if (auto pEntry = lcl_findEntry(*pTreeView, _rName, pTreeView->First())) + pTreeView->GetModel()->Remove(pEntry); + break; + default: + OSL_FAIL("Invalid element type"); + } + if ( !pTreeView->GetEntryCount() ) + showPreview(nullptr); +} + +IMPL_LINK(OAppDetailPageHelper, OnEntryEnterKey, DBTreeListBox*, _pTree, void ) +{ + OnEntryDoubleClick(_pTree); +} +IMPL_LINK(OAppDetailPageHelper, OnEntryDoubleClick, SvTreeListBox*, _pTree, bool) +{ + OSL_ENSURE( _pTree, "OAppDetailPageHelper, OnEntryDoubleClick: invalid callback!" ); + bool bHandled = ( _pTree != nullptr ) && getBorderWin().getView()->getAppController().onEntryDoubleClick( *_pTree ); + return bHandled; +} + +IMPL_LINK_NOARG(OAppDetailPageHelper, OnEntrySelChange, LinkParamNone*, void) +{ + getBorderWin().getView()->getAppController().onSelectionChanged(); +} + +IMPL_LINK_NOARG( OAppDetailPageHelper, OnCopyEntry, LinkParamNone*, void ) +{ + getBorderWin().getView()->getAppController().onCopyEntry(); +} + +IMPL_LINK_NOARG( OAppDetailPageHelper, OnPasteEntry, LinkParamNone*, void ) +{ + getBorderWin().getView()->getAppController().onPasteEntry(); +} + +IMPL_LINK_NOARG( OAppDetailPageHelper, OnDeleteEntry, LinkParamNone*, void ) +{ + getBorderWin().getView()->getAppController().onDeleteEntry(); +} + +void OAppDetailPageHelper::Resize() +{ + // parent window dimension + Size aOutputSize( GetOutputSize() ); + long nOutputWidth = aOutputSize.Width(); + long nOutputHeight = aOutputSize.Height(); + + vcl::Window* pWindow = getCurrentView(); + if ( !pWindow ) + return; + + Size aFLSize = LogicToPixel(Size(2, 6), MapMode(MapUnit::MapAppFont)); + sal_Int32 n6PPT = aFLSize.Height(); + long nHalfOutputWidth = static_cast<long>(nOutputWidth * 0.5); + + pWindow->SetPosSizePixel( Point(0, 0), Size(nHalfOutputWidth - n6PPT, nOutputHeight) ); + + m_aFL->SetPosSizePixel( Point(nHalfOutputWidth , 0 ), Size(aFLSize.Width(), nOutputHeight ) ); + + Size aTBSize = m_aTBPreview->CalcWindowSizePixel(); + m_aTBPreview->SetPosSizePixel(Point(nOutputWidth - aTBSize.getWidth(), 0 ), + aTBSize ); + + m_aBorder->SetPosSizePixel(Point(nHalfOutputWidth + aFLSize.Width() + n6PPT, aTBSize.getHeight() + n6PPT ), + Size(nHalfOutputWidth - aFLSize.Width() - n6PPT, nOutputHeight - 2*n6PPT - aTBSize.getHeight()) ); + m_aPreview->SetPosSizePixel(Point(0,0),m_aBorder->GetSizePixel() ); + m_aDocumentInfo->SetPosSizePixel(Point(0,0),m_aBorder->GetSizePixel() ); + m_pTablePreview->SetPosSizePixel(Point(0,0),m_aBorder->GetSizePixel() ); +} + + +bool OAppDetailPageHelper::isPreviewEnabled() const +{ + return m_ePreviewMode != E_PREVIEWNONE; +} + +namespace +{ + OUString stripTrailingDots(const OUString& rStr) + { + return comphelper::string::stripEnd(rStr, '.'); + } +} + +void OAppDetailPageHelper::switchPreview(PreviewMode _eMode,bool _bForce) +{ + if ( !(m_ePreviewMode != _eMode || _bForce) ) + return; + + m_ePreviewMode = _eMode; + + getBorderWin().getView()->getAppController().previewChanged(static_cast<sal_Int32>(m_ePreviewMode)); + + OUString aCommand; + switch ( m_ePreviewMode ) + { + case E_PREVIEWNONE: + aCommand = ".uno:DBDisablePreview"; + break; + case E_DOCUMENT: + aCommand = ".uno:DBShowDocPreview"; + break; + case E_DOCUMENTINFO: + if ( getBorderWin().getView()->getAppController().isCommandEnabled(SID_DB_APP_VIEW_DOCINFO_PREVIEW) ) + aCommand = ".uno:DBShowDocInfoPreview"; + else + { + m_ePreviewMode = E_PREVIEWNONE; + aCommand = ".uno:DBDisablePreview"; + } + break; + } + + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, "com.sun.star.sdb.OfficeDatabaseDocument"); + OUString aCommandLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + m_aTBPreview->SetItemText(SID_DB_APP_DISABLE_PREVIEW, stripTrailingDots(aCommandLabel)); + Resize(); + + // simulate a selectionChanged event at the controller, to force the preview to be updated + if ( isPreviewEnabled() ) + { + if ( getCurrentView() && getCurrentView()->FirstSelected() ) + { + getBorderWin().getView()->getAppController().onSelectionChanged(); + } + } + else + { + m_pTablePreview->Hide(); + m_aPreview->Hide(); + m_aDocumentInfo->Hide(); + } +} + +void OAppDetailPageHelper::showPreview(const Reference< XContent >& _xContent) +{ + if ( !isPreviewEnabled() ) + return; + + m_pTablePreview->Hide(); + + weld::WaitObject aWaitCursor(GetFrameWeld()); + try + { + Reference<XCommandProcessor> xContent(_xContent,UNO_QUERY); + if ( xContent.is() ) + { + css::ucb::Command aCommand; + if ( m_ePreviewMode == E_DOCUMENT ) + aCommand.Name = "preview"; + else + aCommand.Name = "getDocumentInfo"; + + Any aPreview = xContent->execute(aCommand,xContent->createCommandIdentifier(),Reference< XCommandEnvironment >()); + if ( m_ePreviewMode == E_DOCUMENT ) + { + m_aDocumentInfo->Hide(); + m_aPreview->Show(); + + Graphic aGraphic; + Sequence < sal_Int8 > aBmpSequence; + if ( aPreview >>= aBmpSequence ) + { + SvMemoryStream aData( aBmpSequence.getArray(), + aBmpSequence.getLength(), + StreamMode::READ ); + + GraphicConverter::Import(aData,aGraphic); + } + m_aPreview->setGraphic( aGraphic ); + m_aPreview->Invalidate(); + } + else + { + m_aPreview->Hide(); + m_aDocumentInfo->clear(); + m_aDocumentInfo->Show(); + Reference<document::XDocumentProperties> xProp( + aPreview, UNO_QUERY); + if ( xProp.is() ) + m_aDocumentInfo->fill(xProp); + } + } + else + { + m_aPreview->Hide(); + m_aDocumentInfo->Hide(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } +} + +void OAppDetailPageHelper::showPreview( const OUString& _sDataSourceName, + const OUString& _sName, + bool _bTable) +{ + if ( !isPreviewEnabled() ) + return; + + weld::WaitObject aWaitCursor(GetFrameWeld()); + m_aPreview->Hide(); + m_aDocumentInfo->Hide(); + m_pTablePreview->Show(); + if ( !m_xFrame.is() ) + { + try + { + m_xFrame = Frame::create( getBorderWin().getView()->getORB() ); + m_xFrame->initialize( m_xWindow ); + + // no layout manager (and thus no toolbars) in the preview + // Must be called after initialize ... but before any other call to this frame. + // Otherwise frame throws "life time exceptions" as e.g. NON_INITIALIZED + m_xFrame->setLayoutManager( Reference< XLayoutManager >() ); + + Reference<XFramesSupplier> xSup(getBorderWin().getView()->getAppController().getXController()->getFrame(),UNO_QUERY); + if ( xSup.is() ) + { + Reference<XFrames> xFrames = xSup->getFrames(); + xFrames->append( Reference<XFrame>(m_xFrame,UNO_QUERY_THROW)); + } + } + catch(const Exception&) + { + } + } + + Reference< XDatabaseDocumentUI > xApplication( getBorderWin().getView()->getAppController().getXController(), UNO_QUERY ); + std::unique_ptr< DatabaseObjectView > pDispatcher( new ResultSetBrowser( + getBorderWin().getView()->getORB(), + xApplication, nullptr, _bTable + ) ); + pDispatcher->setTargetFrame( Reference<XFrame>(m_xFrame,UNO_QUERY_THROW) ); + + ::comphelper::NamedValueCollection aArgs; + aArgs.put( "Preview", true ); + aArgs.put( "ReadOnly", true ); + aArgs.put( "AsTemplate", false ); + aArgs.put( OUString(PROPERTY_SHOWMENU), false ); + + Reference< XController > xPreview( pDispatcher->openExisting( makeAny( _sDataSourceName ), _sName, aArgs ), UNO_QUERY ); + bool bClearPreview = !xPreview.is(); + + // clear the preview when the query or table could not be loaded + if ( !bClearPreview ) + { + Reference< XTabController > xTabController( xPreview, UNO_QUERY ); + bClearPreview = !xTabController.is(); + if ( !bClearPreview ) + { + Reference< XLoadable > xLoadable( xTabController->getModel(), UNO_QUERY ); + bClearPreview = !( xLoadable.is() && xLoadable->isLoaded() ); + } + } + if ( bClearPreview ) + showPreview(nullptr); +} + +IMPL_LINK_NOARG(OAppDetailPageHelper, OnDropdownClickHdl, ToolBox*, void) +{ + m_aTBPreview->EndSelection(); + + // tell the toolbox that the item is pressed down + m_aTBPreview->SetItemDown( SID_DB_APP_DISABLE_PREVIEW, true ); + + // simulate a mouse move (so the "down" state is really painted) + Point aPoint = m_aTBPreview->GetItemRect( SID_DB_APP_DISABLE_PREVIEW ).TopLeft(); + MouseEvent aMove( aPoint, 0, MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::SYNTHETIC ); + m_aTBPreview->MouseMove( aMove ); + + m_aTBPreview->PaintImmediately(); + + // execute the menu + css::uno::Reference<css::uno::XComponentContext> xContext(getBorderWin().getView()->getORB()); + css::uno::Reference<css::frame::XUIControllerFactory> xPopupMenuFactory(css::frame::thePopupMenuControllerFactory::get(xContext)); + if (!xPopupMenuFactory.is()) + return; + + css::uno::Sequence<css::uno::Any> aArgs { + css::uno::makeAny(comphelper::makePropertyValue("InToolbar", true)), + css::uno::makeAny(comphelper::makePropertyValue("ModuleIdentifier", OUString("com.sun.star.sdb.OfficeDatabaseDocument"))), + css::uno::makeAny(comphelper::makePropertyValue("Frame", getBorderWin().getView()->getAppController().getFrame())) }; + + css::uno::Reference<css::frame::XPopupMenuController> xPopupController( + xPopupMenuFactory->createInstanceWithArgumentsAndContext(".uno:DBPreview", aArgs, xContext), css::uno::UNO_QUERY); + + if (!xPopupController.is()) + return; + + rtl::Reference xPopupMenu(new VCLXPopupMenu); + xPopupController->setPopupMenu(xPopupMenu.get()); + VclPtr<PopupMenu> aMenu(static_cast<PopupMenu*>(xPopupMenu->GetMenu())); + + sal_uInt16 nSelectedAction = aMenu->Execute(m_aTBPreview.get(), m_aTBPreview->GetItemRect( SID_DB_APP_DISABLE_PREVIEW )); + // "cleanup" the toolbox state + MouseEvent aLeave( aPoint, 0, MouseEventModifiers::LEAVEWINDOW | MouseEventModifiers::SYNTHETIC ); + m_aTBPreview->MouseMove( aLeave ); + m_aTBPreview->SetItemDown( SID_DB_APP_DISABLE_PREVIEW, false); + if ( nSelectedAction ) + { + m_aTBPreview->SetItemText(SID_DB_APP_DISABLE_PREVIEW, stripTrailingDots(aMenu->GetItemText(nSelectedAction))); + Resize(); + } + + css::uno::Reference<css::lang::XComponent> xComponent(xPopupController, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void OAppDetailPageHelper::KeyInput( const KeyEvent& rKEvt ) +{ + SvTreeListBox* pCurrentView = getCurrentView(); + OSL_PRECOND( pCurrentView, "OAppDetailPageHelper::KeyInput: how this?" ); + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + + if ( ( KEY_RETURN == nCode ) && pCurrentView ) + { + getBorderWin().getView()->getAppController().onEntryDoubleClick( *pCurrentView ); + } + else + Window::KeyInput(rKEvt); +} + +void OAppDetailPageHelper::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + + { + ImplInitSettings(); + if ( m_pLists[ E_TABLE ] ) + { + OTableTreeListBox* pTableTree = dynamic_cast< OTableTreeListBox* >( m_pLists[ E_TABLE ].get() ); + OSL_ENSURE( pTableTree != nullptr, "OAppDetailPageHelper::DataChanged: a tree list for tables which is no TableTreeList?" ); + if ( pTableTree ) + pTableTree->notifyHiContrastChanged(); + } + } +} + +void OAppDetailPageHelper::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + m_aTBPreview->SetPointFont(*m_aTBPreview, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + m_aBorder->SetTextColor( rStyleSettings.GetFieldTextColor() ); + m_aBorder->SetTextFillColor(); + m_aTBPreview->SetTextColor( rStyleSettings.GetFieldTextColor() ); + m_aTBPreview->SetTextFillColor(); + SetBackground( rStyleSettings.GetFieldColor() ); + m_aBorder->SetBackground( rStyleSettings.GetFieldColor() ); + m_aFL->SetBackground( rStyleSettings.GetFieldColor() ); + m_aDocumentInfo->SetBackground( rStyleSettings.GetFieldColor() ); + m_aTBPreview->SetBackground( rStyleSettings.GetFieldColor() ); + m_pTablePreview->SetBackground( rStyleSettings.GetFieldColor() ); +} + +OPreviewWindow::OPreviewWindow(vcl::Window* _pParent) +: Window(_pParent) +{ + ImplInitSettings(); +} + +bool OPreviewWindow::ImplGetGraphicCenterRect( const Graphic& rGraphic, tools::Rectangle& rResultRect ) const +{ + const Size aWinSize( GetOutputSizePixel() ); + Size aNewSize( LogicToPixel( rGraphic.GetPrefSize(), rGraphic.GetPrefMapMode() ) ); + bool bRet = false; + + if( aNewSize.Width() && aNewSize.Height() ) + { + // scale to fit window + const double fGrfWH = static_cast<double>(aNewSize.Width()) / aNewSize.Height(); + const double fWinWH = static_cast<double>(aWinSize.Width()) / aWinSize.Height(); + + if ( fGrfWH < fWinWH ) + { + aNewSize.setWidth( static_cast<long>( aWinSize.Height() * fGrfWH ) ); + aNewSize.setHeight( aWinSize.Height() ); + } + else + { + aNewSize.setWidth( aWinSize.Width() ); + aNewSize.setHeight( static_cast<long>( aWinSize.Width() / fGrfWH) ); + } + + const Point aNewPos( ( aWinSize.Width() - aNewSize.Width() ) >> 1, + ( aWinSize.Height() - aNewSize.Height() ) >> 1 ); + + rResultRect = tools::Rectangle( aNewPos, aNewSize ); + bRet = true; + } + + return bRet; +} + +void OPreviewWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + Window::Paint(rRenderContext, rRect); + + if (ImplGetGraphicCenterRect(m_aGraphicObj.GetGraphic(), m_aPreviewRect)) + { + const Point aPos(m_aPreviewRect.TopLeft()); + const Size aSize(m_aPreviewRect.GetSize()); + + if (m_aGraphicObj.IsAnimated()) + m_aGraphicObj.StartAnimation(&rRenderContext, aPos, aSize); + else + m_aGraphicObj.Draw(&rRenderContext, aPos, aSize); + } +} + +void OPreviewWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OPreviewWindow::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppDetailPageHelper.hxx b/dbaccess/source/ui/app/AppDetailPageHelper.hxx new file mode 100644 index 000000000..1dd89d84a --- /dev/null +++ b/dbaccess/source/ui/app/AppDetailPageHelper.hxx @@ -0,0 +1,350 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPDETAILPAGEHELPER_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPDETAILPAGEHELPER_HXX + +#include <vector> + +#include <rtl/ustring.hxx> +#include <com/sun/star/sdb/application/NamedDatabaseObject.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/ucb/XContent.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <AppElementType.hxx> +#include <vcl/treelistbox.hxx> +#include <svtools/DocumentInfoPreview.hxx> +#include <vcl/fixed.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/graph.hxx> +#include <vcl/GraphicObject.hxx> + +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::frame { class XFrame2; } +namespace com::sun::star::io { class XPersist; } + +#define ELEMENT_COUNT size_t(E_ELEMENT_TYPE_COUNT) + +namespace dbaui +{ + class OAppBorderWindow; + class DBTreeListBox; + + class OPreviewWindow : public vcl::Window + { + GraphicObject m_aGraphicObj; + tools::Rectangle m_aPreviewRect; + + /** gets the graphic center rect + @param rGraphic + the graphic + @param rResultRect + the resulting rectangle + + @return + <TRUE/> when successful + */ + bool ImplGetGraphicCenterRect( const Graphic& rGraphic, tools::Rectangle& rResultRect ) const; + void ImplInitSettings(); + protected: + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + public: + explicit OPreviewWindow(vcl::Window* _pParent); + + // Window overrides + virtual void Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect) override; + + void setGraphic(const Graphic& _rGraphic ) { m_aGraphicObj.SetGraphic(_rGraphic); } + }; + // A helper class for the controls in the detail page. + // Combines general functionality. + class OAppDetailPageHelper : public vcl::Window + { + VclPtr<DBTreeListBox> m_pLists[ELEMENT_COUNT]; + OAppBorderWindow& m_rBorderWin; + VclPtr<FixedLine> m_aFL; + VclPtr<ToolBox> m_aTBPreview; + VclPtr<Window> m_aBorder; + VclPtr<OPreviewWindow> m_aPreview; + VclPtr< ::svtools::ODocumentInfoPreview> + m_aDocumentInfo; + VclPtr<vcl::Window> m_pTablePreview; + PreviewMode m_ePreviewMode; + css::uno::Reference < css::frame::XFrame2 > + m_xFrame; + css::uno::Reference< css::awt::XWindow > + m_xWindow; + + /// returns the index of the visible control + int getVisibleControlIndex() const; + + /** sorts the entries in the tree list box. + @param _nPos + Which list should be sorted. + @param _eSortMode + How should be sorted. + */ + void sort(int _nPos,SvSortMode _eSortMode ); + + /** retrieves the resource ids of the images representing elements of the given type + */ + static OUString getElementIcons(ElementType _eType); + + /** fills the names in the listbox + @param _xContainer + This can either be the queries, forms or report names. + @param _eType + the type of elements which are being filled + @param _nImageId + the resource id of the image to use for non-container entries + @param _pParent + The parent of the entries to be inserted. + */ + void fillNames( const css::uno::Reference< css::container::XNameAccess >& _xContainer, + const ElementType _eType, + const OUString& rImageId, + SvTreeListEntry* _pParent ); + + /** sets the detail page + @param _pWindow + The control which should be visible. + */ + void setDetailPage(vcl::Window* _pWindow); + + /** sets all HandleCallbacks + @param _pTreeView + The newly created DBTreeListBox + @param _rImage + the resource id of the default icon + @return + The new tree. + */ + DBTreeListBox* createTree( DBTreeListBox* _pTreeView, const Image& _rImage ); + + /** creates the tree and sets all HandleCallbacks + @param _nHelpId + The help id of the control + @param _nCollapsedBitmap + The image to use in high contrast mode. + @return + The new tree. + */ + DBTreeListBox* createSimpleTree( const OString& _sHelpId, const Image& _rImage); + + DECL_LINK( OnEntryDoubleClick, SvTreeListBox*, bool ); + DECL_LINK( OnEntryEnterKey, DBTreeListBox*, void ); + DECL_LINK( OnEntrySelChange, LinkParamNone*, void ); + + DECL_LINK( OnCopyEntry, LinkParamNone*, void ); + DECL_LINK( OnPasteEntry, LinkParamNone*, void ); + DECL_LINK( OnDeleteEntry, LinkParamNone*, void ); + + // click a TB slot + DECL_LINK(OnDropdownClickHdl, ToolBox*, void); + + OAppBorderWindow& getBorderWin() const { return m_rBorderWin; } + void ImplInitSettings(); + + public: + OAppDetailPageHelper(vcl::Window* _pParent,OAppBorderWindow& _rBorderWin,PreviewMode _ePreviewMode); + virtual ~OAppDetailPageHelper() override; + virtual void dispose() override; + + // Window overrides + virtual void Resize() override; + virtual void KeyInput( const KeyEvent& rKEvt ) override; + + /** creates the tables page + @param _xConnection + The connection to get the table names + */ + void createTablesPage(const css::uno::Reference< css::sdbc::XConnection>& _xConnection); + + /** creates the page for the specific type. + @param _eType + The type which should be created. E_TABLE isn't allowed. + @param _xContainer + The container of the elements to be inserted. + */ + void createPage(ElementType _eType,const css::uno::Reference< css::container::XNameAccess >& _xContainer); + + /** returns the current visible tree list box + */ + DBTreeListBox* getCurrentView() const + { + ElementType eType = getElementType(); + return (eType != E_NONE ) ? m_pLists[static_cast<sal_Int32>(eType)].get() : nullptr; + } + + /// select all entries in the visible control + void selectAll(); + + /// returns <TRUE/> if it sorts ascending + bool isSortUp() const; + + /// sorts all entries ascending + void sortDown(); + + /// sorts all entries descending + void sortUp(); + + /** returns the element names which are selected + @param _rNames + The list will be filled. + */ + void getSelectionElementNames( std::vector< OUString>& _rNames ) const; + + /** describes the current selection for the given control + */ + void describeCurrentSelectionForControl( + const Control& _rControl, + css::uno::Sequence< css::sdb::application::NamedDatabaseObject >& _out_rSelectedObjects + ); + + /** describes the current selection for the given ElementType + */ + void describeCurrentSelectionForType( + const ElementType _eType, + css::uno::Sequence< css::sdb::application::NamedDatabaseObject >& _out_rSelectedObjects + ); + + /** select all names on the currently selected container. Non existence names where ignored. + * + * \param _aNames the element names + */ + void selectElements(const css::uno::Sequence< OUString>& _aNames); + + /** return the qualified name. + @param _pEntry + The entry of a table, or query, form, report to get the qualified name. + If the entry is <NULL/>, the first selected is chosen. + @return + the qualified name + */ + OUString getQualifiedName( SvTreeListEntry* _pEntry ) const; + + /// return the element of currently select entry + ElementType getElementType() const; + + /// returns the count of selected entries + sal_Int32 getSelectionCount(); + + /// returns the count of entries + sal_Int32 getElementCount() const; + + /** returns if an entry is a leaf + @param _pEntry + The entry to check + @return + <TRUE/> if the entry is a leaf, otherwise <FALSE/> + */ + static bool isLeaf(SvTreeListEntry const * _pEntry); + + /** returns if one of the selected entries is a leaf + @return + <TRUE/> if the entry is a leaf, otherwise <FALSE/> + */ + bool isALeafSelected() const; + + SvTreeListEntry* getEntry( const Point& _aPosPixel ) const; + + /// clears the detail pages + void clearPages(); + + /// returns <TRUE/> when a detail page was filled + bool isFilled() const; + + /** adds a new object to the detail page. + @param _eType + The type where the entry should be appended. + @param _rName + The name of the object to be inserted + @param _rObject + The object to add. + @param _rxConn + If we insert a table, the connection must be set. + */ + SvTreeListEntry* elementAdded(ElementType eType + ,const OUString& _rName + ,const css::uno::Any& _rObject ); + + /** replaces an objects name with a new one + @param _eType + The type where the entry should be appended. + @param _rOldName + The old name of the object to be replaced + @param _rNewName + The new name of the object to be replaced + @param _rxConn + If we insert a table, the connection must be set. + */ + void elementReplaced(ElementType eType + ,const OUString& _rOldName + ,const OUString& _rNewName ); + + /** removes an element from the detail page. + @param _eType + The type where the entry should be appended. + @param _rName + The name of the element to be removed. + @param _rxConn + If we remove a table, the connection must be set. + */ + void elementRemoved(ElementType _eType + ,const OUString& _rName ); + + /// returns the preview mode + PreviewMode getPreviewMode() const { return m_ePreviewMode;} + + /// <TRUE/> if the preview is enabled + bool isPreviewEnabled() const; + + /** switches to the given preview mode + @param _eMode + the mode to set for the preview + @param _bForce + Force the preview to be reset + */ + void switchPreview(PreviewMode _eMode,bool _bForce = false); + + /** shows the Preview of the content when it is enabled. + @param _xContent + The content which must support the "preview" command. + */ + void showPreview(const css::uno::Reference< css::ucb::XContent >& _xContent); + + /** shows the Preview of a table or query + @param _sDataSourceName + the name of the data source + @param _sName + the name of table or query + @param _bTable + <TRUE/> if it is a table, otherwise <FALSE/> + @return void + */ + void showPreview( const OUString& _sDataSourceName, + const OUString& _sName, + bool _bTable); + + protected: + void DataChanged( const DataChangedEvent& rDCEvt ) override; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPDETAILPAGEHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppDetailView.cxx b/dbaccess/source/ui/app/AppDetailView.cxx new file mode 100644 index 000000000..39da2cd49 --- /dev/null +++ b/dbaccess/source/ui/app/AppDetailView.cxx @@ -0,0 +1,877 @@ +/* -*- 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 "AppDetailView.hxx" +#include <osl/diagnose.h> +#include <helpids.h> +#include <strings.hrc> +#include "AppView.hxx" +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XImageManager.hpp> +#include <com/sun/star/ui/ImageType.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <com/sun/star/util/URL.hpp> +#include <core_resource.hxx> +#include <vcl/event.hxx> +#include <vcl/image.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/settings.hxx> +#include "AppDetailPageHelper.hxx" +#include <dbaccess/IController.hxx> +#include <vcl/treelistentry.hxx> +#include <vcl/viewdataentry.hxx> +#include <algorithm> +#include <dbtreelistbox.hxx> +#include <imageprovider.hxx> +#include "AppController.hxx" + +using namespace ::dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using ::com::sun::star::util::URL; +using ::com::sun::star::sdb::application::NamedDatabaseObject; + +#define SPACEBETWEENENTRIES 4 + +TaskEntry::TaskEntry( const char* _pAsciiUNOCommand, const char* _pHelpID, const char* pTitleResourceID, bool _bHideWhenDisabled ) + :sUNOCommand( OUString::createFromAscii( _pAsciiUNOCommand ) ) + ,pHelpID( _pHelpID ) + ,sTitle( DBA_RES(pTitleResourceID) ) + ,bHideWhenDisabled( _bHideWhenDisabled ) +{ +} + +OCreationList::OCreationList( OTasksWindow& _rParent ) + :SvTreeListBox( &_rParent, WB_TABSTOP | WB_HASBUTTONSATROOT | WB_HASBUTTONS ) + ,m_rTaskWindow( _rParent ) + ,m_pMouseDownEntry( nullptr ) + ,m_pLastActiveEntry( nullptr ) +{ + SetSpaceBetweenEntries(SPACEBETWEENENTRIES); + SetSelectionMode( SelectionMode::NONE ); + SetNoAutoCurEntry( true ); + SetNodeDefaultImages( ); + EnableEntryMnemonics(); +} + +void OCreationList::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& _rRect ) +{ + SetBackground(); + + if (m_pMouseDownEntry) + m_aOriginalFont = rRenderContext.GetFont(); + + m_aOriginalBackgroundColor = rRenderContext.GetBackground().GetColor(); + SvTreeListBox::Paint(rRenderContext, _rRect); + rRenderContext.SetBackground(m_aOriginalBackgroundColor); + + if (m_pMouseDownEntry) + rRenderContext.SetFont(m_aOriginalFont); +} + +void OCreationList::PreparePaint(vcl::RenderContext& rRenderContext, SvTreeListEntry& rEntry) +{ + Wallpaper aEntryBackground(m_aOriginalBackgroundColor); + + if (&rEntry == GetCurEntry()) + { + // draw a selection background + bool bIsMouseDownEntry = ( &rEntry == m_pMouseDownEntry ); + vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, GetBoundingRect(&rEntry), + bIsMouseDownEntry ? 1 : 2, false, true, false ); + + if (bIsMouseDownEntry) + { + vcl::Font aFont(rRenderContext.GetFont()); + aFont.SetColor(rRenderContext.GetSettings().GetStyleSettings().GetHighlightTextColor()); + rRenderContext.SetFont(aFont); + } + + // and temporary set a transparent background, for all the other + // paint operations the SvTreeListBox is going to do + aEntryBackground = Wallpaper(); + } + + rRenderContext.SetBackground(aEntryBackground); + rEntry.SetBackColor(aEntryBackground.GetColor()); +} + +void OCreationList::SelectSearchEntry( const void* _pEntry ) +{ + SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pEntry ) ); + OSL_ENSURE( pEntry, "OCreationList::SelectSearchEntry: invalid entry!" ); + + if ( pEntry ) + setCurrentEntryInvalidate( pEntry ); + + if ( !HasChildPathFocus() ) + GrabFocus(); +} + +void OCreationList::ExecuteSearchEntry( const void* _pEntry ) const +{ + SvTreeListEntry* pEntry = const_cast< SvTreeListEntry* >( static_cast< const SvTreeListEntry* >( _pEntry ) ); + OSL_ENSURE( pEntry, "OCreationList::ExecuteSearchEntry: invalid entry!" ); + OSL_ENSURE( pEntry == GetCurEntry(), "OCreationList::ExecuteSearchEntry: SelectSearchEntry should have been called before!" ); + + if ( pEntry ) + onSelected( pEntry ); +} + +tools::Rectangle OCreationList::GetFocusRect(const SvTreeListEntry* _pEntry, long _nLine) +{ + tools::Rectangle aRect = SvTreeListBox::GetFocusRect( _pEntry, _nLine ); + aRect.SetLeft( 0 ); + + // try to let the focus rect start before the bitmap item - this looks better + const SvLBoxItem* pBitmapItem = _pEntry->GetFirstItem(SvLBoxItemType::ContextBmp); + SvLBoxTab* pTab = pBitmapItem ? GetTab( _pEntry, pBitmapItem ) : nullptr; + SvViewDataItem* pItemData = pBitmapItem ? GetViewDataItem( _pEntry, pBitmapItem ) : nullptr; + OSL_ENSURE( pTab && pItemData, "OCreationList::GetFocusRect: could not find the first bitmap item!" ); + if ( pTab && pItemData ) + aRect.SetLeft( pTab->GetPos() - pItemData->mnWidth / 2 ); + + // inflate the rectangle a little bit - looks better, too + aRect.SetLeft( std::max< long >( 0, aRect.Left() - 2 ) ); + aRect.SetRight( std::min< long >( GetOutputSizePixel().Width() - 1, aRect.Right() + 2 ) ); + + return aRect; +} + +void OCreationList::StartDrag( sal_Int8 /*_nAction*/, const Point& /*_rPosPixel*/ ) +{ + // don't give this to the base class, it does a ReleaseMouse as very first action + // Though I think this is a bug (it should ReleaseMouse only if it is going to do + // something with the drag-event), I hesitate to fix it in the current state, + // since I don't overlook the consequences, and we're close to 2.0...) +} + +void OCreationList::ModelHasCleared() +{ + SvTreeListBox::ModelHasCleared(); + m_pLastActiveEntry = nullptr; + m_pMouseDownEntry = nullptr; +} + +void OCreationList::GetFocus() +{ + SvTreeListBox::GetFocus(); + if ( !GetCurEntry() ) + setCurrentEntryInvalidate( m_pLastActiveEntry ? m_pLastActiveEntry : GetFirstEntryInView() ); +} + +void OCreationList::LoseFocus() +{ + SvTreeListBox::LoseFocus(); + m_pLastActiveEntry = GetCurEntry(); + setCurrentEntryInvalidate( nullptr ); +} + +void OCreationList::MouseButtonDown( const MouseEvent& rMEvt ) +{ + SvTreeListBox::MouseButtonDown( rMEvt ); + + OSL_ENSURE( !m_pMouseDownEntry, "OCreationList::MouseButtonDown: I missed some mouse event!" ); + m_pMouseDownEntry = GetCurEntry(); + if ( m_pMouseDownEntry ) + { + InvalidateEntry( m_pMouseDownEntry ); + CaptureMouse(); + } +} + +void OCreationList::MouseMove( const MouseEvent& rMEvt ) +{ + if ( rMEvt.IsLeaveWindow() ) + { + setCurrentEntryInvalidate( nullptr ); + } + else if ( !rMEvt.IsSynthetic() ) + { + SvTreeListEntry* pEntry = GetEntry( rMEvt.GetPosPixel() ); + + if ( m_pMouseDownEntry ) + { + // we're currently in a "mouse down" phase + OSL_ENSURE( IsMouseCaptured(), "OCreationList::MouseMove: inconsistence (1)!" ); + if ( pEntry == m_pMouseDownEntry ) + { + setCurrentEntryInvalidate( m_pMouseDownEntry ); + } + else + { + OSL_ENSURE( ( GetCurEntry() == m_pMouseDownEntry ) || !GetCurEntry(), + "OCreationList::MouseMove: inconsistence (2)!" ); + setCurrentEntryInvalidate( nullptr ); + } + } + else + { + // the user is simply hovering with the mouse + if ( setCurrentEntryInvalidate( pEntry ) ) + { + if ( !m_pMouseDownEntry ) + updateHelpText(); + } + } + } + + SvTreeListBox::MouseMove(rMEvt); +} + +void OCreationList::MouseButtonUp( const MouseEvent& rMEvt ) +{ + SvTreeListEntry* pEntry = GetEntry( rMEvt.GetPosPixel() ); + bool bExecute = false; + // Was the mouse released over the active entry? + // (i.e. the entry which was under the mouse when the button went down) + if ( pEntry && ( m_pMouseDownEntry == pEntry ) ) + { + if ( !rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && rMEvt.IsLeft() && rMEvt.GetClicks() == 1 ) + bExecute = true; + } + + if ( m_pMouseDownEntry ) + { + OSL_ENSURE( IsMouseCaptured(), "OCreationList::MouseButtonUp: hmmm... no mouse captured, but an active entry?" ); + ReleaseMouse(); + + InvalidateEntry( m_pMouseDownEntry ); + m_pMouseDownEntry = nullptr; + } + + SvTreeListBox::MouseButtonUp( rMEvt ); + + if ( bExecute ) + onSelected( pEntry ); +} + +bool OCreationList::setCurrentEntryInvalidate( SvTreeListEntry* _pEntry ) +{ + if ( GetCurEntry() != _pEntry ) + { + if ( GetCurEntry() ) + InvalidateEntry( GetCurEntry() ); + SetCurEntry( _pEntry ); + if ( GetCurEntry() ) + { + InvalidateEntry( GetCurEntry() ); + CallEventListeners( VclEventId::ListboxTreeSelect, GetCurEntry() ); + } + updateHelpText(); + return true; + } + return false; +} + +void OCreationList::updateHelpText() +{ + const char* pHelpTextId = nullptr; + if ( GetCurEntry() ) + pHelpTextId = static_cast< TaskEntry* >( GetCurEntry()->GetUserData() )->pHelpID; + m_rTaskWindow.setHelpText(pHelpTextId); +} + +void OCreationList::onSelected( SvTreeListEntry const * _pEntry ) const +{ + OSL_ENSURE( _pEntry, "OCreationList::onSelected: invalid entry!" ); + URL aCommand; + aCommand.Complete = static_cast< TaskEntry* >( _pEntry->GetUserData() )->sUNOCommand; + m_rTaskWindow.getDetailView()->getBorderWin().getView()->getAppController().executeChecked( aCommand, Sequence< PropertyValue >() ); +} + +void OCreationList::KeyInput( const KeyEvent& rKEvt ) +{ + const vcl::KeyCode& rCode = rKEvt.GetKeyCode(); + if ( !rCode.IsMod1() && !rCode.IsMod2() && !rCode.IsShift() ) + { + if ( rCode.GetCode() == KEY_RETURN ) + { + SvTreeListEntry* pEntry = GetCurEntry() ? GetCurEntry() : FirstSelected(); + if ( pEntry ) + onSelected( pEntry ); + return; + } + } + SvTreeListEntry* pOldCurrent = GetCurEntry(); + SvTreeListBox::KeyInput(rKEvt); + SvTreeListEntry* pNewCurrent = GetCurEntry(); + + if ( pOldCurrent != pNewCurrent ) + { + if ( pOldCurrent ) + InvalidateEntry( pOldCurrent ); + if ( pNewCurrent ) + { + InvalidateEntry( pNewCurrent ); + CallEventListeners( VclEventId::ListboxSelect, pNewCurrent ); + } + updateHelpText(); + } +} + +OTasksWindow::OTasksWindow(vcl::Window* _pParent,OApplicationDetailView* _pDetailView) + : Window(_pParent,WB_DIALOGCONTROL ) + ,m_aCreation(VclPtr<OCreationList>::Create(*this)) + ,m_aDescription(VclPtr<FixedText>::Create(this)) + ,m_aHelpText(VclPtr<FixedText>::Create(this,WB_WORDBREAK)) + ,m_aFL(VclPtr<FixedLine>::Create(this,WB_VERT)) + ,m_pDetailView(_pDetailView) +{ + m_aCreation->SetHelpId(HID_APP_CREATION_LIST); + m_aCreation->SetSelectHdl(LINK(this, OTasksWindow, OnEntrySelectHdl)); + m_aHelpText->SetHelpId(HID_APP_HELP_TEXT); + m_aDescription->SetHelpId(HID_APP_DESCRIPTION_TEXT); + m_aDescription->SetText(DBA_RES(STR_DESCRIPTION)); + + Image aFolderImage = ImageProvider::getFolderImage( css::sdb::application::DatabaseObject::FORM ); + m_aCreation->SetDefaultCollapsedEntryBmp( aFolderImage ); + m_aCreation->SetDefaultExpandedEntryBmp( aFolderImage ); + + ImplInitSettings(); +} + +OTasksWindow::~OTasksWindow() +{ + disposeOnce(); +} + +void OTasksWindow::dispose() +{ + Clear(); + m_aCreation.disposeAndClear(); + m_aDescription.disposeAndClear(); + m_aHelpText.disposeAndClear(); + m_aFL.disposeAndClear(); + m_pDetailView.clear(); + vcl::Window::dispose(); +} + +void OTasksWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OTasksWindow::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + m_aHelpText->SetTextColor( rStyleSettings.GetFieldTextColor() ); + m_aHelpText->SetTextFillColor(); + m_aDescription->SetTextColor( rStyleSettings.GetFieldTextColor() ); + m_aDescription->SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); + m_aHelpText->SetBackground( rStyleSettings.GetFieldColor() ); + m_aDescription->SetBackground( rStyleSettings.GetFieldColor() ); + m_aFL->SetBackground( rStyleSettings.GetFieldColor() ); + + aFont = m_aDescription->GetControlFont(); + aFont.SetWeight(WEIGHT_BOLD); + m_aDescription->SetControlFont(aFont); +} + +void OTasksWindow::setHelpText(const char* pId) +{ + if (pId) + { + OUString sText = DBA_RES(pId); + m_aHelpText->SetText(sText); + } + else + { + m_aHelpText->SetText(OUString()); +} + +} + +IMPL_LINK_NOARG(OTasksWindow, OnEntrySelectHdl, SvTreeListBox*, void) +{ + SvTreeListEntry* pEntry = m_aCreation->GetHdlEntry(); + if ( pEntry ) + m_aHelpText->SetText(DBA_RES(static_cast<TaskEntry*>(pEntry->GetUserData())->pHelpID)); +} + +void OTasksWindow::Resize() +{ + // parent window dimension + Size aOutputSize( GetOutputSize() ); + long nOutputWidth = aOutputSize.Width(); + long nOutputHeight = aOutputSize.Height(); + + Size aFLSize = LogicToPixel(Size(2, 6), MapMode(MapUnit::MapAppFont)); + sal_Int32 n6PPT = aFLSize.Height(); + long nHalfOutputWidth = static_cast<long>(nOutputWidth * 0.5); + + m_aCreation->SetPosSizePixel( Point(0, 0), Size(nHalfOutputWidth - n6PPT, nOutputHeight) ); + // i77897 make the m_aHelpText a little bit smaller. (-5) + sal_Int32 nNewWidth = nOutputWidth - nHalfOutputWidth - aFLSize.Width() - 5; + m_aDescription->SetPosSizePixel( Point(nHalfOutputWidth + n6PPT, 0), Size(nNewWidth, nOutputHeight) ); + Size aDesc = m_aDescription->CalcMinimumSize(); + m_aHelpText->SetPosSizePixel( Point(nHalfOutputWidth + n6PPT, aDesc.Height() ), Size(nNewWidth, nOutputHeight - aDesc.Height() - n6PPT) ); + + m_aFL->SetPosSizePixel( Point(nHalfOutputWidth , 0), Size(aFLSize.Width(), nOutputHeight ) ); +} + +void OTasksWindow::fillTaskEntryList( const TaskEntryList& _rList ) +{ + Clear(); + + try + { + Reference< XModuleUIConfigurationManagerSupplier > xModuleCfgMgrSupplier = + theModuleUIConfigurationManagerSupplier::get( getDetailView()->getBorderWin().getView()->getORB() ); + Reference< XUIConfigurationManager > xUIConfigMgr = xModuleCfgMgrSupplier->getUIConfigurationManager( + "com.sun.star.sdb.OfficeDatabaseDocument" + ); + Reference< XImageManager > xImageMgr( xUIConfigMgr->getImageManager(), UNO_QUERY ); + + // copy the commands so we can use them with the config managers + Sequence< OUString > aCommands( _rList.size() ); + OUString* pCommands = aCommands.getArray(); + for (auto const& copyTask : _rList) + { + *pCommands = copyTask.sUNOCommand; + ++pCommands; + } + + Sequence< Reference< XGraphic> > aImages = xImageMgr->getImages( + ImageType::SIZE_DEFAULT | ImageType::COLOR_NORMAL , + aCommands + ); + + const Reference< XGraphic >* pImages( aImages.getConstArray() ); + + for (auto const& task : _rList) + { + SvTreeListEntry* pEntry = m_aCreation->InsertEntry(task.sTitle); + pEntry->SetUserData( new TaskEntry(task) ); + + Image aImage( *pImages ); + m_aCreation->SetExpandedEntryBmp( pEntry, aImage ); + m_aCreation->SetCollapsedEntryBmp( pEntry, aImage ); + ++pImages; + } + } + catch(Exception&) + { + } + + m_aCreation->Show(); + m_aCreation->SelectAll(false); + m_aHelpText->Show(); + m_aDescription->Show(); + m_aFL->Show(); + m_aCreation->updateHelpText(); + Enable(!_rList.empty()); +} + +void OTasksWindow::Clear() +{ + m_aCreation->resetLastActive(); + SvTreeListEntry* pEntry = m_aCreation->First(); + while ( pEntry ) + { + delete static_cast< TaskEntry* >( pEntry->GetUserData() ); + pEntry = m_aCreation->Next(pEntry); + } + m_aCreation->Clear(); +} + + +OApplicationDetailView::OApplicationDetailView(OAppBorderWindow& _rParent,PreviewMode _ePreviewMode) : OSplitterView(&_rParent ) + ,m_aHorzSplitter(VclPtr<Splitter>::Create(this)) + ,m_aTasks(VclPtr<dbaui::OTitleWindow>::Create(this, STR_TASKS, WB_BORDER | WB_DIALOGCONTROL)) + ,m_aContainer(VclPtr<dbaui::OTitleWindow>::Create(this, nullptr, WB_BORDER | WB_DIALOGCONTROL)) + ,m_rBorderWin(_rParent) +{ + ImplInitSettings(); + + m_pControlHelper = VclPtr<OAppDetailPageHelper>::Create(m_aContainer.get(),m_rBorderWin,_ePreviewMode); + m_pControlHelper->Show(); + m_aContainer->setChildWindow(m_pControlHelper); + + VclPtrInstance<OTasksWindow> pTasks(m_aTasks.get(),this); + pTasks->Show(); + pTasks->Disable(m_rBorderWin.getView()->getCommandController().isDataSourceReadOnly()); + m_aTasks->setChildWindow(pTasks); + m_aTasks->Show(); + + m_aContainer->Show(); + + const long nFrameWidth = LogicToPixel(Size(3, 0), MapMode(MapUnit::MapAppFont)).Width(); + m_aHorzSplitter->SetPosSizePixel( Point(0,50), Size(0,nFrameWidth) ); + // now set the components at the base class + set(m_aContainer.get(),m_aTasks.get()); + + m_aHorzSplitter->Show(); + setSplitter(m_aHorzSplitter.get()); +} + +OApplicationDetailView::~OApplicationDetailView() +{ + disposeOnce(); +} + +void OApplicationDetailView::dispose() +{ + set(nullptr); + setSplitter(nullptr); + m_aHorzSplitter.disposeAndClear(); + m_aTasks.disposeAndClear(); + m_aContainer.disposeAndClear(); + m_pControlHelper.clear(); + OSplitterView::dispose(); +} + +void OApplicationDetailView::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); + + m_aHorzSplitter->SetBackground( rStyleSettings.GetDialogColor() ); + m_aHorzSplitter->SetFillColor( rStyleSettings.GetDialogColor() ); + m_aHorzSplitter->SetTextFillColor(rStyleSettings.GetDialogColor() ); +} + +void OApplicationDetailView::DataChanged( const DataChangedEvent& rDCEvt ) +{ + OSplitterView::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OApplicationDetailView::setTaskExternalMnemonics( MnemonicGenerator const & _rMnemonics ) +{ + m_aExternalMnemonics = _rMnemonics; +} + +bool OApplicationDetailView::interceptKeyInput( const KeyEvent& _rEvent ) +{ + const vcl::KeyCode& rKeyCode = _rEvent.GetKeyCode(); + if ( rKeyCode.GetModifier() == KEY_MOD2 ) + return getTasksWindow().HandleKeyInput( _rEvent ); + + // not handled + return false; +} + +void OApplicationDetailView::createTablesPage(const Reference< XConnection >& _xConnection ) +{ + impl_createPage( E_TABLE, _xConnection, nullptr ); +} + +void OApplicationDetailView::createPage( ElementType _eType,const Reference< XNameAccess >& _xContainer ) +{ + impl_createPage( _eType, nullptr, _xContainer ); +} + +void OApplicationDetailView::impl_createPage( ElementType _eType, const Reference< XConnection >& _rxConnection, + const Reference< XNameAccess >& _rxNonTableElements ) +{ + // get the data for the pane + const TaskPaneData& rData = impl_getTaskPaneData( _eType ); + getTasksWindow().fillTaskEntryList( rData.aTasks ); + + // enable the pane as a whole, depending on the availability of the first command + OSL_ENSURE( !rData.aTasks.empty(), "OApplicationDetailView::impl_createPage: no tasks at all!?" ); + bool bEnabled = !rData.aTasks.empty() + && getBorderWin().getView()->getCommandController().isCommandEnabled( rData.aTasks[0].sUNOCommand ); + getTasksWindow().Enable( bEnabled ); + m_aContainer->setTitle(rData.pTitleId); + + // let our helper create the object list + if ( _eType == E_TABLE ) + m_pControlHelper->createTablesPage( _rxConnection ); + else + m_pControlHelper->createPage( _eType, _rxNonTableElements ); + + // resize for proper window arrangements + Resize(); +} + +const TaskPaneData& OApplicationDetailView::impl_getTaskPaneData( ElementType _eType ) +{ + if ( m_aTaskPaneData.empty() ) + m_aTaskPaneData.resize( ELEMENT_COUNT ); + OSL_ENSURE( ( _eType >= 0 ) && ( _eType < E_ELEMENT_TYPE_COUNT ), "OApplicationDetailView::impl_getTaskPaneData: illegal element type!" ); + TaskPaneData& rData = m_aTaskPaneData[ _eType ]; + + //oj: do not check, otherwise extensions will only be visible after a reload. + impl_fillTaskPaneData( _eType, rData ); + + return rData; +} + +void OApplicationDetailView::impl_fillTaskPaneData( ElementType _eType, TaskPaneData& _rData ) const +{ + TaskEntryList& rList( _rData.aTasks ); + rList.clear(); rList.reserve( 4 ); + + switch ( _eType ) + { + case E_TABLE: + rList.emplace_back( ".uno:DBNewTable", RID_STR_TABLES_HELP_TEXT_DESIGN, RID_STR_NEW_TABLE ); + rList.emplace_back( ".uno:DBNewTableAutoPilot", RID_STR_TABLES_HELP_TEXT_WIZARD, RID_STR_NEW_TABLE_AUTO ); + rList.emplace_back( ".uno:DBNewView", RID_STR_VIEWS_HELP_TEXT_DESIGN, RID_STR_NEW_VIEW, true ); + _rData.pTitleId = RID_STR_TABLES_CONTAINER; + break; + + case E_FORM: + rList.emplace_back( ".uno:DBNewForm", RID_STR_FORMS_HELP_TEXT, RID_STR_NEW_FORM ); + rList.emplace_back( ".uno:DBNewFormAutoPilot", RID_STR_FORMS_HELP_TEXT_WIZARD, RID_STR_NEW_FORM_AUTO ); + _rData.pTitleId = RID_STR_FORMS_CONTAINER; + break; + + case E_REPORT: + rList.emplace_back( ".uno:DBNewReport", RID_STR_REPORT_HELP_TEXT, RID_STR_NEW_REPORT, true ); + rList.emplace_back( ".uno:DBNewReportAutoPilot", RID_STR_REPORTS_HELP_TEXT_WIZARD, RID_STR_NEW_REPORT_AUTO ); + _rData.pTitleId = RID_STR_REPORTS_CONTAINER; + break; + + case E_QUERY: + rList.emplace_back( ".uno:DBNewQuery", RID_STR_QUERIES_HELP_TEXT, RID_STR_NEW_QUERY ); + rList.emplace_back( ".uno:DBNewQueryAutoPilot", RID_STR_QUERIES_HELP_TEXT_WIZARD, RID_STR_NEW_QUERY_AUTO ); + rList.emplace_back( ".uno:DBNewQuerySql", RID_STR_QUERIES_HELP_TEXT_SQL, RID_STR_NEW_QUERY_SQL ); + _rData.pTitleId = RID_STR_QUERIES_CONTAINER; + break; + + default: + OSL_FAIL( "OApplicationDetailView::impl_fillTaskPaneData: illegal element type!" ); + } + + MnemonicGenerator aAllMnemonics( m_aExternalMnemonics ); + + // remove the entries which are not enabled currently + for ( TaskEntryList::iterator pTask = rList.begin(); + pTask != rList.end(); + ) + { + if ( pTask->bHideWhenDisabled + && !getBorderWin().getView()->getCommandController().isCommandEnabled( pTask->sUNOCommand ) + ) + pTask = rList.erase( pTask ); + else + { + aAllMnemonics.RegisterMnemonic( pTask->sTitle ); + ++pTask; + } + } + + // for the remaining entries, assign mnemonics + for (auto const& task : rList) + { + aAllMnemonics.CreateMnemonic(task.sTitle); + // don't do this for now, until our task window really supports mnemonics + } +} + +OUString OApplicationDetailView::getQualifiedName( SvTreeListEntry* _pEntry ) const +{ + return m_pControlHelper->getQualifiedName( _pEntry ); +} + +bool OApplicationDetailView::isLeaf(SvTreeListEntry const * _pEntry) +{ + return OAppDetailPageHelper::isLeaf(_pEntry); +} + +bool OApplicationDetailView::isALeafSelected() const +{ + return m_pControlHelper->isALeafSelected(); +} + +void OApplicationDetailView::selectAll() +{ + m_pControlHelper->selectAll(); +} + +void OApplicationDetailView::sortDown() +{ + m_pControlHelper->sortDown(); +} + +void OApplicationDetailView::sortUp() +{ + m_pControlHelper->sortUp(); +} + +bool OApplicationDetailView::isFilled() const +{ + return m_pControlHelper->isFilled(); +} + +ElementType OApplicationDetailView::getElementType() const +{ + return m_pControlHelper->getElementType(); +} + +void OApplicationDetailView::clearPages(bool _bTaskAlso) +{ + if ( _bTaskAlso ) + getTasksWindow().Clear(); + m_pControlHelper->clearPages(); +} + +sal_Int32 OApplicationDetailView::getSelectionCount() +{ + return m_pControlHelper->getSelectionCount(); +} + +sal_Int32 OApplicationDetailView::getElementCount() const +{ + return m_pControlHelper->getElementCount(); +} + +void OApplicationDetailView::getSelectionElementNames( std::vector< OUString>& _rNames ) const +{ + m_pControlHelper->getSelectionElementNames( _rNames ); +} + +void OApplicationDetailView::describeCurrentSelectionForControl( const Control& _rControl, Sequence< NamedDatabaseObject >& _out_rSelectedObjects ) +{ + m_pControlHelper->describeCurrentSelectionForControl( _rControl, _out_rSelectedObjects ); +} + +void OApplicationDetailView::describeCurrentSelectionForType( const ElementType _eType, Sequence< NamedDatabaseObject >& _out_rSelectedObjects ) +{ + m_pControlHelper->describeCurrentSelectionForType( _eType, _out_rSelectedObjects ); +} + +void OApplicationDetailView::selectElements(const Sequence< OUString>& _aNames) +{ + m_pControlHelper->selectElements( _aNames ); +} + +SvTreeListEntry* OApplicationDetailView::getEntry( const Point& _aPoint ) const +{ + return m_pControlHelper->getEntry(_aPoint); +} + +bool OApplicationDetailView::isCutAllowed() +{ + return false; +} + +bool OApplicationDetailView::isCopyAllowed() +{ + return true; +} + +bool OApplicationDetailView::isPasteAllowed() { return true; } + +void OApplicationDetailView::copy() { } + +void OApplicationDetailView::cut() { } + +void OApplicationDetailView::paste() { } + +SvTreeListEntry* OApplicationDetailView::elementAdded(ElementType _eType,const OUString& _rName, const Any& _rObject ) +{ + return m_pControlHelper->elementAdded(_eType,_rName, _rObject ); +} + +void OApplicationDetailView::elementRemoved(ElementType _eType,const OUString& _rName ) +{ + m_pControlHelper->elementRemoved(_eType,_rName ); +} + +void OApplicationDetailView::elementReplaced(ElementType _eType + ,const OUString& _rOldName + ,const OUString& _rNewName ) +{ + m_pControlHelper->elementReplaced( _eType, _rOldName, _rNewName ); +} + +PreviewMode OApplicationDetailView::getPreviewMode() const +{ + return m_pControlHelper->getPreviewMode(); +} + +bool OApplicationDetailView::isPreviewEnabled() const +{ + return m_pControlHelper->isPreviewEnabled(); +} + +void OApplicationDetailView::switchPreview(PreviewMode _eMode) +{ + m_pControlHelper->switchPreview(_eMode); +} + +void OApplicationDetailView::showPreview(const Reference< XContent >& _xContent) +{ + m_pControlHelper->showPreview(_xContent); +} + +void OApplicationDetailView::showPreview( const OUString& _sDataSourceName, + const OUString& _sName, + bool _bTable) +{ + m_pControlHelper->showPreview(_sDataSourceName,_sName,_bTable); +} + +bool OApplicationDetailView::isSortUp() const +{ + return m_pControlHelper->isSortUp(); +} + +vcl::Window* OApplicationDetailView::getTreeWindow() const +{ + return m_pControlHelper->getCurrentView(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppDetailView.hxx b/dbaccess/source/ui/app/AppDetailView.hxx new file mode 100644 index 000000000..38bc41ab1 --- /dev/null +++ b/dbaccess/source/ui/app/AppDetailView.hxx @@ -0,0 +1,368 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPDETAILVIEW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPDETAILVIEW_HXX + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/ucb/XContent.hpp> +#include <com/sun/star/sdb/application/NamedDatabaseObject.hpp> +#include <vcl/split.hxx> +#include <vcl/fixed.hxx> +#include <vcl/mnemonic.hxx> +#include <IClipBoardTest.hxx> +#include "AppTitleWindow.hxx" +#include <AppElementType.hxx> +#include <vcl/treelistbox.hxx> +#include <VertSplitView.hxx> + +#include <vector> + +class SvTreeListEntry; + +namespace dbaui +{ + class OAppBorderWindow; + class OApplicationDetailView; + class OAppDetailPageHelper; + class OTasksWindow; + + class OCreationList : public SvTreeListBox + { + OTasksWindow& m_rTaskWindow; + + // members related to drawing the currently hovered/selected entry + SvTreeListEntry* m_pMouseDownEntry; + SvTreeListEntry* m_pLastActiveEntry; + Color m_aOriginalBackgroundColor; + vcl::Font m_aOriginalFont; + + public: + explicit OCreationList( OTasksWindow& _rParent ); + // Window overrides + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; + virtual void KeyInput( const KeyEvent& rKEvt ) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void StartDrag( sal_Int8 _nAction, const Point& _rPosPixel ) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + + void resetLastActive() { m_pLastActiveEntry = nullptr;} + + void updateHelpText(); + + protected: + virtual void PreparePaint(vcl::RenderContext& rRenderContext, SvTreeListEntry& rEntry) override; + virtual tools::Rectangle GetFocusRect(const SvTreeListEntry* _pEntry, long _nLine) override; + virtual void ModelHasCleared() override; + + // IMnemonicEntryList + virtual void SelectSearchEntry( const void* _pEntry ) override; + virtual void ExecuteSearchEntry( const void* _pEntry ) const override; + + private: + void onSelected( SvTreeListEntry const * _pEntry ) const; + /** sets a new current entry, and invalidates the old and the new one, if necessary + @return <TRUE/> if and only if the "current entry" changed + */ + bool setCurrentEntryInvalidate( SvTreeListEntry* _pEntry ); + }; + + struct TaskEntry + { + OUString sUNOCommand; + const char* pHelpID; + OUString sTitle; + bool bHideWhenDisabled; + // TODO: we should be consistent in the task pane and the menus/toolbars: + // If an entry is disabled in the latter, it should also be disabled in the former. + // If an entry is *hidden* in the former, it should also be hidden in the latter. + + TaskEntry( const char* _pAsciiUNOCommand, const char* pHelpID, const char* pTitleResourceID, bool _bHideWhenDisabled = false ); + }; + typedef std::vector< TaskEntry > TaskEntryList; + + struct TaskPaneData + { + /// the tasks available in the pane + TaskEntryList aTasks; + /// the resource ID for the title of the pane + const char* pTitleId; + }; + + class OTasksWindow : public vcl::Window + { + VclPtr<OCreationList> m_aCreation; + VclPtr<FixedText> m_aDescription; + VclPtr<FixedText> m_aHelpText; + VclPtr<FixedLine> m_aFL; + VclPtr<OApplicationDetailView> m_pDetailView; + + DECL_LINK( OnEntrySelectHdl, SvTreeListBox*, void ); + void ImplInitSettings(); + protected: + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + public: + OTasksWindow(vcl::Window* _pParent,OApplicationDetailView* _pDetailView); + virtual ~OTasksWindow() override; + virtual void dispose() override; + + // Window overrides + virtual void Resize() override; + + OApplicationDetailView* getDetailView() const { return m_pDetailView; } + + /// fills the Creation listbox with the necessary strings and images + void fillTaskEntryList( const TaskEntryList& _rList ); + + bool HandleKeyInput( const KeyEvent& _rKEvt ) + { + return m_aCreation->HandleKeyInput( _rKEvt ); + } + + void Clear(); + void setHelpText(const char* pId); + }; + class OApplicationDetailView : public OSplitterView + , public IClipboardTest + { + VclPtr<Splitter> m_aHorzSplitter; + VclPtr<OTitleWindow> m_aTasks; + VclPtr<OTitleWindow> m_aContainer; + OAppBorderWindow& m_rBorderWin; // my parent + VclPtr<OAppDetailPageHelper> m_pControlHelper; + std::vector< TaskPaneData > m_aTaskPaneData; + MnemonicGenerator m_aExternalMnemonics; + + void ImplInitSettings(); + + protected: + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + + public: + OApplicationDetailView(OAppBorderWindow& _rParent,PreviewMode _ePreviewMode); + virtual ~OApplicationDetailView() override; + // Window overrides + virtual void dispose() override; + + /** creates the tables page + @param _xConnection + The connection to get the table names + */ + void createTablesPage(const css::uno::Reference< css::sdbc::XConnection>& _xConnection); + + /** creates the page for the specific type. + @param _eType + The type which should be created. E_TABLE isn't allowed. + @param _xContainer + The container of the elements to be inserted. + */ + void createPage(ElementType _eType,const css::uno::Reference< css::container::XNameAccess >& _xContainer); + + void setTaskExternalMnemonics( MnemonicGenerator const & _rMnemonics ); + + /** called to give the window the chance to intercept key events, while it has not + the focus + + @return <TRUE/> if and only if the event has been handled, and should not + not be further processed + */ + bool interceptKeyInput( const KeyEvent& _rEvent ); + + OAppBorderWindow& getBorderWin() const { return m_rBorderWin; } + OTasksWindow& getTasksWindow() const { return *static_cast< OTasksWindow* >( m_aTasks->getChildWindow() ); } + + bool isCutAllowed() override ; + bool isCopyAllowed() override ; + bool isPasteAllowed() override; + void copy() override; + void cut() override; + void paste() override; + + /** return the qualified name. + @param _pEntry + The entry of a table, or query, form, report to get the qualified name. + If the entry is <NULL/>, the first selected is chosen. + @return + the qualified name + */ + OUString getQualifiedName( SvTreeListEntry* _pEntry ) const; + + /** returns if an entry is a leaf + @param _pEntry + The entry to check + @return + <TRUE/> if the entry is a leaf, otherwise <FALSE/> + */ + static bool isLeaf(SvTreeListEntry const * _pEntry); + + /** returns if one of the selected entries is a leaf + @return + <TRUE/> if the entry is a leaf, otherwise <FALSE/> + */ + bool isALeafSelected() const; + + /** select all entries in the detail page + */ + void selectAll(); + + /// returns <TRUE/> if it sorts ascending + bool isSortUp() const; + + /// sort the entries in the detail page down + void sortDown(); + + /// sort the entries in the detail page up + void sortUp(); + + /// returns <TRUE/> when a detail page was filled + bool isFilled() const; + + /// return the element of currently select entry + ElementType getElementType() const; + + /** clears the detail pages. + @param _bTaskAlso + If <TRUE/> the task window will also be cleared. + */ + void clearPages(bool _bTaskAlso = true); + + /// returns the count of entries + sal_Int32 getElementCount() const; + + /// returns the count of selected entries + sal_Int32 getSelectionCount(); + + /** returns the element names which are selected + @param _rNames + The list will be filled. + */ + void getSelectionElementNames(std::vector< OUString>& _rNames ) const; + + /** describes the current selection for the given control + */ + void describeCurrentSelectionForControl( + const Control& _rControl, + css::uno::Sequence< css::sdb::application::NamedDatabaseObject >& _out_rSelectedObjects + ); + + /** describes the current selection for the given ElementType + */ + void describeCurrentSelectionForType( + const ElementType _eType, + css::uno::Sequence< css::sdb::application::NamedDatabaseObject >& _out_rSelectedObjects + ); + + /** select all names on the currently selected container. Non existence names where ignored. + * + * \param _aNames the element names + */ + void selectElements(const css::uno::Sequence< OUString>& _aNames); + + /** adds a new object to the detail page. + @param _eType + The type where the entry should be appended. + @param _rName + The name of the object to be inserted + @param _rObject + The object to add. + @param _rxConn + If we insert a table, the connection must be set. + */ + SvTreeListEntry* elementAdded(ElementType eType + ,const OUString& _rName + ,const css::uno::Any& _rObject ); + + /** replaces an objects name with a new one + @param _eType + The type where the entry should be appended. + @param _rOldName + The old name of the object to be replaced + @param _rNewName + The new name of the object to be replaced + @param _rxConn + If we insert a table, the connection must be set. + @param _xObject + The object which was replaced + */ + void elementReplaced(ElementType eType + ,const OUString& _rOldName + ,const OUString& _rNewName ); + + /** removes an element from the detail page. + @param _eType + The type where the entry should be appended. + @param _rName + The name of the element to be removed. + @param _rxConn + If we remove a table, the connection must be set. + */ + void elementRemoved(ElementType _eType + ,const OUString& _rName ); + + /// returns the preview mode + PreviewMode getPreviewMode() const; + + /// <TRUE/> if the preview is enabled + bool isPreviewEnabled() const; + + /** switches to the given preview mode + @param _eMode + the mode to set for the preview + */ + void switchPreview(PreviewMode _eMode); + + /** shows the Preview of the content when it is enabled. + @param _xContent + The content which must support the "preview" command. + */ + void showPreview(const css::uno::Reference< css::ucb::XContent >& _xContent); + + /** shows the Preview of a table or query + @param _sDataSourceName + the name of the data source + @param _sName + the name of table or query + @param _bTable + <TRUE/> if it is a table, otherwise <FALSE/> + @return void + */ + void showPreview( const OUString& _sDataSourceName, + const OUString& _sName, + bool _bTable); + + SvTreeListEntry* getEntry( const Point& _aPoint ) const; + + vcl::Window* getTreeWindow() const; + private: + void impl_createPage( + ElementType _eType, + const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, + const css::uno::Reference< css::container::XNameAccess >& _rxNonTableElements + ); + + const TaskPaneData& impl_getTaskPaneData( ElementType _eType ); + void impl_fillTaskPaneData( ElementType _eType, TaskPaneData& _rData ) const; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPDETAILVIEW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppIconControl.cxx b/dbaccess/source/ui/app/AppIconControl.cxx new file mode 100644 index 000000000..514580014 --- /dev/null +++ b/dbaccess/source/ui/app/AppIconControl.cxx @@ -0,0 +1,106 @@ +/* -*- 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 "AppIconControl.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <vcl/image.hxx> +#include <callbacks.hxx> +#include <AppElementType.hxx> + +using namespace ::dbaui; +OApplicationIconControl::OApplicationIconControl(vcl::Window* _pParent) + : SvtIconChoiceCtrl(_pParent,WB_ICON | WB_NOCOLUMNHEADER | WB_HIGHLIGHTFRAME | /*!WB_NOSELECTION |*/ + WB_TABSTOP | WB_CLIPCHILDREN | WB_NOVSCROLL | WB_SMART_ARRANGE | WB_NOHSCROLL | WB_CENTER) + ,DropTargetHelper(this) + ,m_pActionListener(nullptr) +{ + + static const struct CategoryDescriptor + { + const char* pLabelResId; + ElementType eType; + const char* aImageResId; + } aCategories[] = { + { RID_STR_TABLES_CONTAINER, E_TABLE, BMP_TABLEFOLDER_TREE_L }, + { RID_STR_QUERIES_CONTAINER, E_QUERY, BMP_QUERYFOLDER_TREE_L }, + { RID_STR_FORMS_CONTAINER, E_FORM, BMP_FORMFOLDER_TREE_L }, + { RID_STR_REPORTS_CONTAINER, E_REPORT, BMP_REPORTFOLDER_TREE_L } + }; + for (const CategoryDescriptor& aCategorie : aCategories) + { + SvxIconChoiceCtrlEntry* pEntry = InsertEntry( + DBA_RES(aCategorie.pLabelResId) , + Image(StockImage::Yes, OUString::createFromAscii(aCategorie.aImageResId))); + if ( pEntry ) + pEntry->SetUserData( new ElementType( aCategorie.eType ) ); + } + + SetChoiceWithCursor(); + SetSelectionMode(SelectionMode::Single); +} + +OApplicationIconControl::~OApplicationIconControl() +{ + disposeOnce(); +} + +void OApplicationIconControl::dispose() +{ + sal_Int32 nCount = GetEntryCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvxIconChoiceCtrlEntry* pEntry = GetEntry( i ); + if ( pEntry ) + { + delete static_cast<ElementType*>(pEntry->GetUserData()); + pEntry->SetUserData(nullptr); + } + } + DropTargetHelper::dispose(); + SvtIconChoiceCtrl::dispose(); +} + +sal_Int8 OApplicationIconControl::AcceptDrop( const AcceptDropEvent& _rEvt ) +{ + sal_Int8 nDropOption = DND_ACTION_NONE; + if ( m_pActionListener ) + { + + SvxIconChoiceCtrlEntry* pEntry = GetEntry(_rEvt.maPosPixel); + if ( pEntry ) + { + SetCursor(pEntry); + nDropOption = m_pActionListener->queryDrop( _rEvt, GetDataFlavorExVector() ); + } + } + + return nDropOption; +} + +sal_Int8 OApplicationIconControl::ExecuteDrop( const ExecuteDropEvent& _rEvt ) +{ + if ( m_pActionListener ) + return m_pActionListener->executeDrop( _rEvt ); + + return DND_ACTION_NONE; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppIconControl.hxx b/dbaccess/source/ui/app/AppIconControl.hxx new file mode 100644 index 000000000..38408af60 --- /dev/null +++ b/dbaccess/source/ui/app/AppIconControl.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPICONCONTROL_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPICONCONTROL_HXX + +#include <vcl/ivctrl.hxx> +#include <vcl/transfer.hxx> + +namespace dbaui +{ + class IControlActionListener; + class OApplicationIconControl :public SvtIconChoiceCtrl + ,public DropTargetHelper + { + IControlActionListener* m_pActionListener; + + public: + explicit OApplicationIconControl(vcl::Window* _pParent); + virtual ~OApplicationIconControl() override; + virtual void dispose() override; + + void setControlActionListener( IControlActionListener* _pListener ) { m_pActionListener = _pListener; } + + protected: + // DropTargetHelper overridables + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& _rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& _rEvt ) override; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPICONCONTROL_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppSwapWindow.cxx b/dbaccess/source/ui/app/AppSwapWindow.cxx new file mode 100644 index 000000000..e291bdb8a --- /dev/null +++ b/dbaccess/source/ui/app/AppSwapWindow.cxx @@ -0,0 +1,180 @@ +/* -*- 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 "AppSwapWindow.hxx" +#include <helpids.h> +#include "AppView.hxx" +#include <vcl/event.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/settings.hxx> +#include "AppController.hxx" + +using namespace ::dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; + +OApplicationSwapWindow::OApplicationSwapWindow( vcl::Window* _pParent, OAppBorderWindow& _rBorderWindow ) + :Window(_pParent,WB_DIALOGCONTROL ) + ,m_aIconControl(VclPtr<OApplicationIconControl>::Create(this)) + ,m_eLastType(E_NONE) + ,m_rBorderWin( _rBorderWindow ) +{ + ImplInitSettings(); + + m_aIconControl->SetClickHdl(LINK(this, OApplicationSwapWindow, OnContainerSelectHdl)); + m_aIconControl->setControlActionListener( &m_rBorderWin.getView()->getAppController() ); + m_aIconControl->SetHelpId(HID_APP_SWAP_ICONCONTROL); + m_aIconControl->Show(); +} + +OApplicationSwapWindow::~OApplicationSwapWindow() +{ + disposeOnce(); +} + +void OApplicationSwapWindow::dispose() +{ + m_aIconControl.disposeAndClear(); + vcl::Window::dispose(); +} + +void OApplicationSwapWindow::Resize() +{ + Size aFLSize = LogicToPixel(Size(8, 0), MapMode(MapUnit::MapAppFont)); + long nX = 0; + if ( m_aIconControl->GetEntryCount() != 0 ) + nX = m_aIconControl->GetBoundingBox( m_aIconControl->GetEntry(0) ).GetWidth() + aFLSize.Width(); + + Size aOutputSize = GetOutputSize(); + + m_aIconControl->SetPosSizePixel( Point(static_cast<long>((aOutputSize.Width() - nX)*0.5), 0) ,Size(nX,aOutputSize.Height())); + m_aIconControl->ArrangeIcons(); +} + +void OApplicationSwapWindow::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); +} + +void OApplicationSwapWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OApplicationSwapWindow::clearSelection() +{ + m_aIconControl->SetNoSelection(); + SvxIconChoiceCtrlEntry* pEntry = m_aIconControl->GetSelectedEntry(); + if ( pEntry ) + m_aIconControl->InvalidateEntry(pEntry); + m_aIconControl->GetClickHdl().Call(m_aIconControl.get()); +} + +void OApplicationSwapWindow::createIconAutoMnemonics( MnemonicGenerator& _rMnemonics ) +{ + m_aIconControl->CreateAutoMnemonics( _rMnemonics ); +} + +bool OApplicationSwapWindow::interceptKeyInput( const KeyEvent& _rEvent ) +{ + const vcl::KeyCode& rKeyCode = _rEvent.GetKeyCode(); + if ( rKeyCode.GetModifier() == KEY_MOD2 ) + return m_aIconControl->DoKeyInput( _rEvent ); + + // not handled + return false; +} + +ElementType OApplicationSwapWindow::getElementType() const +{ + SvxIconChoiceCtrlEntry* pEntry = m_aIconControl->GetSelectedEntry(); + return pEntry ? *static_cast<ElementType*>(pEntry->GetUserData()) : E_NONE; +} + +bool OApplicationSwapWindow::onContainerSelected( ElementType _eType ) +{ + if ( m_eLastType == _eType ) + return true; + + if ( m_rBorderWin.getView()->getAppController().onContainerSelect( _eType ) ) + { + if ( _eType != E_NONE ) + m_eLastType = _eType; + return true; + } + + PostUserEvent( LINK( this, OApplicationSwapWindow, ChangeToLastSelected ), nullptr, true ); + return false; +} + +IMPL_LINK(OApplicationSwapWindow, OnContainerSelectHdl, SvtIconChoiceCtrl*, _pControl, void) +{ + SvxIconChoiceCtrlEntry* pEntry = _pControl->GetSelectedEntry(); + ElementType eType = E_NONE; + if ( pEntry ) + { + eType = *static_cast<ElementType*>(pEntry->GetUserData()); + onContainerSelected( eType ); // i87582 + } +} + +IMPL_LINK_NOARG(OApplicationSwapWindow, ChangeToLastSelected, void*, void) +{ + selectContainer(m_eLastType); +} + +void OApplicationSwapWindow::selectContainer(ElementType _eType) +{ + sal_Int32 nCount = m_aIconControl->GetEntryCount(); + SvxIconChoiceCtrlEntry* pEntry = nullptr; + for (sal_Int32 i=0; i < nCount; ++i) + { + pEntry = m_aIconControl->GetEntry(i); + if ( pEntry && *static_cast<ElementType*>(pEntry->GetUserData()) == _eType ) + break; + pEntry = nullptr; + } + + if ( pEntry ) + m_aIconControl->SetCursor(pEntry); // this call also initiates a onContainerSelected call + else + onContainerSelected( _eType ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppSwapWindow.hxx b/dbaccess/source/ui/app/AppSwapWindow.hxx new file mode 100644 index 000000000..62f58c152 --- /dev/null +++ b/dbaccess/source/ui/app/AppSwapWindow.hxx @@ -0,0 +1,92 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPSWAPWINDOW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPSWAPWINDOW_HXX + +#include <IClipBoardTest.hxx> +#include <vcl/vclptr.hxx> +#include "AppIconControl.hxx" +#include <AppElementType.hxx> + +namespace dbaui +{ + class OAppBorderWindow; + class OApplicationSwapWindow : public vcl::Window, + public IClipboardTest + { + VclPtr<OApplicationIconControl> m_aIconControl; + ElementType m_eLastType; + OAppBorderWindow& m_rBorderWin; + + void ImplInitSettings(); + + DECL_LINK( OnContainerSelectHdl, SvtIconChoiceCtrl*, void ); + DECL_LINK( ChangeToLastSelected, void*, void ); + protected: + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + public: + OApplicationSwapWindow( vcl::Window* _pParent, OAppBorderWindow& _rBorderWindow ); + virtual ~OApplicationSwapWindow() override; + // Window overrides + virtual void dispose() override; + virtual void Resize() override; + + bool isCutAllowed() override { return false; } + bool isCopyAllowed() override { return false; } + bool isPasteAllowed() override { return false; } + void copy() override { } + void cut() override { } + void paste() override { } + + sal_Int32 GetEntryCount() const { return m_aIconControl->GetEntryCount(); } + SvxIconChoiceCtrlEntry* GetEntry( sal_uLong nPos ) const { return m_aIconControl->GetEntry(nPos); } + tools::Rectangle GetBoundingBox( SvxIconChoiceCtrlEntry* pEntry ) const { return m_aIconControl->GetBoundingBox(pEntry); } + + /** automatically creates mnemonics for the icon/texts in our left hand side panel + */ + void createIconAutoMnemonics( MnemonicGenerator& _rMnemonics ); + + /** called to give the window the chance to intercept key events, while it has not + the focus + + @return <TRUE/> if and only if the event has been handled, and should not + not be further processed + */ + bool interceptKeyInput( const KeyEvent& _rEvent ); + + /// return the element of currently select entry + ElementType getElementType() const; + + /** clears the selection in the icon choice control and calls the handler + */ + void clearSelection(); + + /** changes the container which should be displayed. The select handler will also be called. + @param _eType + Which container to show. + */ + void selectContainer(ElementType _eType); + + private: + bool onContainerSelected( ElementType _eType ); + }; +} // namespace dbaui +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPSWAPWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppTitleWindow.cxx b/dbaccess/source/ui/app/AppTitleWindow.cxx new file mode 100644 index 000000000..d6d121ba4 --- /dev/null +++ b/dbaccess/source/ui/app/AppTitleWindow.cxx @@ -0,0 +1,182 @@ +/* -*- 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 "AppTitleWindow.hxx" +#include <core_resource.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/event.hxx> + +namespace dbaui +{ + +OTitleWindow::OTitleWindow(vcl::Window* _pParent, const char* pTitleId, WinBits _nBits, bool _bShift) +: Window(_pParent,_nBits | WB_DIALOGCONTROL) +, m_aSpace1(VclPtr<FixedText>::Create(this)) +, m_aSpace2(VclPtr<FixedText>::Create(this)) +, m_aTitle(VclPtr<FixedText>::Create(this)) +, m_pChild(nullptr) +, m_bShift(_bShift) +{ + setTitle(pTitleId); + SetBorderStyle(WindowBorderStyle::MONO); + ImplInitSettings(); + + const StyleSettings& rStyle = Application::GetSettings().GetStyleSettings(); + vcl::Window* pWindows[] = { m_aSpace1.get(), m_aSpace2.get(), m_aTitle.get() }; + for (vcl::Window* pWindow : pWindows) + { + vcl::Font aFont = pWindow->GetControlFont(); + aFont.SetWeight(WEIGHT_BOLD); + pWindow->SetControlFont(aFont); + pWindow->SetControlForeground(rStyle.GetLightColor()); + pWindow->SetControlBackground(rStyle.GetShadowColor()); + pWindow->Show(); + } +} + +OTitleWindow::~OTitleWindow() +{ + disposeOnce(); +} + +void OTitleWindow::dispose() +{ + if ( m_pChild ) + { + m_pChild->Hide(); + } + m_pChild.disposeAndClear(); + m_aSpace1.disposeAndClear(); + m_aSpace2.disposeAndClear(); + m_aTitle.disposeAndClear(); + vcl::Window::dispose(); +} + +void OTitleWindow::setChildWindow(vcl::Window* _pChild) +{ + m_pChild = _pChild; +} + +#define SPACE_BORDER 1 +void OTitleWindow::Resize() +{ + // parent window dimension + Size aOutputSize( GetOutputSize() ); + long nOutputWidth = aOutputSize.Width(); + long nOutputHeight = aOutputSize.Height(); + + Size aTextSize = LogicToPixel(Size(6, 3), MapMode(MapUnit::MapAppFont)); + sal_Int32 nXOffset = aTextSize.Width(); + sal_Int32 nYOffset = aTextSize.Height(); + sal_Int32 nHeight = GetTextHeight() + 2*nYOffset; + + m_aSpace1->SetPosSizePixel( Point(SPACE_BORDER, SPACE_BORDER ), + Size(nXOffset , nHeight - SPACE_BORDER) ); + m_aSpace2->SetPosSizePixel( Point(nXOffset + SPACE_BORDER, SPACE_BORDER ), + Size(nOutputWidth - nXOffset - 2*SPACE_BORDER, nYOffset) ); + m_aTitle->SetPosSizePixel( Point(nXOffset + SPACE_BORDER, nYOffset + SPACE_BORDER), + Size(nOutputWidth - nXOffset - 2*SPACE_BORDER, nHeight - nYOffset - SPACE_BORDER) ); + if ( m_pChild ) + { + m_pChild->SetPosSizePixel( Point(m_bShift ? (nXOffset+SPACE_BORDER) : sal_Int32(SPACE_BORDER), nHeight + nXOffset + SPACE_BORDER), + Size(nOutputWidth - ( m_bShift ? (2*nXOffset - 2*SPACE_BORDER) : sal_Int32(SPACE_BORDER) ), nOutputHeight - nHeight - 2*nXOffset - 2*SPACE_BORDER) ); + } +} + +void OTitleWindow::setTitle(const char* pTitleId) +{ + if (pTitleId) + { + m_aTitle->SetText(DBA_RES(pTitleId)); + } +} + +void OTitleWindow::GetFocus() +{ + Window::GetFocus(); + if ( m_pChild ) + m_pChild->GrabFocus(); +} + +long OTitleWindow::GetWidthPixel() const +{ + Size aTextSize = LogicToPixel(Size(12, 0), MapMode(MapUnit::MapAppFont)); + sal_Int32 nWidth = GetTextWidth(m_aTitle->GetText()) + 2*aTextSize.Width(); + + return nWidth; +} + +void OTitleWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OTitleWindow::ImplInitSettings() +{ + // FIXME RenderContext + AllSettings aAllSettings = GetSettings(); + StyleSettings aStyle = aAllSettings.GetStyleSettings(); + aStyle.SetMonoColor(aStyle.GetActiveBorderColor());//GetMenuBorderColor()); + aAllSettings.SetStyleSettings(aStyle); + SetSettings(aAllSettings); + + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); +} + +void OTitleWindow::ApplySettings(vcl::RenderContext& rRenderContext) +{ + // FIXME RenderContext + AllSettings aAllSettings = rRenderContext.GetSettings(); + StyleSettings aStyle = aAllSettings.GetStyleSettings(); + aStyle.SetMonoColor(aStyle.GetActiveBorderColor());//GetMenuBorderColor()); + aAllSettings.SetStyleSettings(aStyle); + rRenderContext.SetSettings(aAllSettings); + + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor(rStyleSettings.GetWindowTextColor()); + SetPointFont(*this, aFont); + + rRenderContext.SetTextColor(rStyleSettings.GetFieldTextColor()); + rRenderContext.SetTextFillColor(); + + rRenderContext.SetBackground(rStyleSettings.GetFieldColor()); +} + +} // namespace dbaui +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppTitleWindow.hxx b/dbaccess/source/ui/app/AppTitleWindow.hxx new file mode 100644 index 000000000..c6a8d417d --- /dev/null +++ b/dbaccess/source/ui/app/AppTitleWindow.hxx @@ -0,0 +1,76 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPTITLEWINDOW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPTITLEWINDOW_HXX + +#include <vcl/fixed.hxx> + +namespace dbaui +{ + class OTitleWindow : public vcl::Window + { + VclPtr<FixedText> m_aSpace1; + VclPtr<FixedText> m_aSpace2; + VclPtr<FixedText> m_aTitle; + VclPtr<vcl::Window> m_pChild; + bool m_bShift; + void ImplInitSettings(); + protected: + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + public: + OTitleWindow(vcl::Window* _pParent, const char* pTitleId, WinBits _nBits, bool _bShift = true); + virtual ~OTitleWindow() override; + virtual void dispose() override; + + // Window overrides + virtual void Resize() override; + virtual void GetFocus() override; + + virtual void ApplySettings(vcl::RenderContext& rRenderContext) override; + + /** sets the child window which should be displayed below the title. It will be destroyed at the end. + @param _pChild + The child window. + */ + void setChildWindow(vcl::Window* _pChild); + + /** gets the child window. + + @return + The child window. + */ + vcl::Window* getChildWindow() const { return m_pChild; } + + /** sets the title text out of the resource + @param pTitleId + The resource id of the title text. + */ + void setTitle(const char* pTitleId); + + /** Gets the min Width in Pixel which is needed to display the whole + + @return + the min width + */ + long GetWidthPixel() const; + }; +} // namespace dbaui +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPTITLEWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppView.cxx b/dbaccess/source/ui/app/AppView.cxx new file mode 100644 index 000000000..fb61be909 --- /dev/null +++ b/dbaccess/source/ui/app/AppView.cxx @@ -0,0 +1,524 @@ +/* -*- 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 "AppView.hxx" +#include <strings.hrc> +#include <tools/diagnose_ex.h> +#include <vcl/event.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include "AppDetailView.hxx" +#include "AppSwapWindow.hxx" +#include <vcl/settings.hxx> +#include "AppTitleWindow.hxx" +#include "AppController.hxx" + +using namespace ::dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::datatransfer::clipboard; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using ::com::sun::star::sdb::application::NamedDatabaseObject; + +OAppBorderWindow::OAppBorderWindow(OApplicationView* _pParent,PreviewMode _ePreviewMode) : Window(_pParent,WB_DIALOGCONTROL) + ,m_pPanel(nullptr) + ,m_pDetailView(nullptr) + ,m_pView(_pParent) +{ + + SetBorderStyle(WindowBorderStyle::MONO); + + m_pPanel = VclPtr<OTitleWindow>::Create(this,STR_DATABASE,WB_BORDER | WB_DIALOGCONTROL, false); + m_pPanel->SetBorderStyle(WindowBorderStyle::MONO); + VclPtrInstance<OApplicationSwapWindow> pSwap( m_pPanel, *this ); + pSwap->Show(); + + m_pPanel->setChildWindow(pSwap); + m_pPanel->Show(); + + m_pDetailView = VclPtr<OApplicationDetailView>::Create(*this,_ePreviewMode); + m_pDetailView->Show(); + + ImplInitSettings(); +} + +OAppBorderWindow::~OAppBorderWindow() +{ + disposeOnce(); +} + +void OAppBorderWindow::dispose() +{ + // destroy children + if ( m_pPanel ) + m_pPanel->Hide(); + m_pPanel.disposeAndClear(); + if ( m_pDetailView ) + m_pDetailView->Hide(); + m_pDetailView.disposeAndClear(); + m_pView.clear(); + vcl::Window::dispose(); +} + +void OAppBorderWindow::GetFocus() +{ + if ( m_pPanel ) + m_pPanel->GrabFocus(); +} + +void OAppBorderWindow::Resize() +{ + // parent window dimension + Size aOutputSize( GetOutputSize() ); + long nOutputWidth = aOutputSize.Width(); + long nOutputHeight = aOutputSize.Height(); + long nX = 0; + + Size aFLSize = LogicToPixel(Size(3, 8), MapMode(MapUnit::MapAppFont)); + if ( m_pPanel ) + { + OApplicationSwapWindow* pSwap = getPanel(); + if ( pSwap && pSwap->GetEntryCount() != 0 ) + nX = pSwap->GetBoundingBox( pSwap->GetEntry(0) ).GetWidth() + aFLSize.Height(); + nX = std::max(m_pPanel->GetWidthPixel() ,nX); + m_pPanel->SetPosSizePixel(Point(0,0),Size(nX,nOutputHeight)); + } + + if ( m_pDetailView ) + m_pDetailView->SetPosSizePixel(Point(nX + aFLSize.Width(),0),Size(nOutputWidth - nX - aFLSize.Width(),nOutputHeight)); +} + +void OAppBorderWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OAppBorderWindow::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetDialogColor() ); +} + + +OApplicationSwapWindow* OAppBorderWindow::getPanel() const +{ + return static_cast< OApplicationSwapWindow* >( m_pPanel->getChildWindow() ); +} + + +OApplicationView::OApplicationView( vcl::Window* pParent + ,const Reference< XComponentContext >& _rxOrb + ,OApplicationController& _rAppController + ,PreviewMode _ePreviewMode + ) : + ODataView( pParent, _rAppController, _rxOrb, WB_DIALOGCONTROL ) + ,m_rAppController( _rAppController ) + ,m_eChildFocus(NONE) +{ + m_pWin = VclPtr<OAppBorderWindow>::Create(this,_ePreviewMode); + m_pWin->Show(); + + ImplInitSettings(); +} + +OApplicationView::~OApplicationView() +{ + disposeOnce(); +} + +void OApplicationView::dispose() +{ + stopComponentListening(m_xObject); + m_xObject.clear(); + m_pWin->Hide(); + m_pWin.disposeAndClear(); + ODataView::dispose(); +} + +void OApplicationView::createIconAutoMnemonics( MnemonicGenerator& _rMnemonics ) +{ + if ( m_pWin && m_pWin->getPanel() ) + m_pWin->getPanel()->createIconAutoMnemonics( _rMnemonics ); +} + +void OApplicationView::setTaskExternalMnemonics( MnemonicGenerator const & _rMnemonics ) +{ + if ( m_pWin && m_pWin->getDetailView() ) + m_pWin->getDetailView()->setTaskExternalMnemonics( _rMnemonics ); +} + +void OApplicationView::DataChanged( const DataChangedEvent& rDCEvt ) +{ + ODataView::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + ImplInitSettings(); + Invalidate(); + } +} + +void OApplicationView::resizeDocumentView(tools::Rectangle& _rPlayground) +{ + if ( m_pWin && !_rPlayground.IsEmpty() ) + { + Size aFLSize = LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont)); + _rPlayground.Move( aFLSize.Width(),aFLSize.Height() ); + Size aOldSize = _rPlayground.GetSize(); + _rPlayground.SetSize( Size(aOldSize.Width() - 2*aFLSize.Width(), aOldSize.Height() - 2*aFLSize.Height()) ); + + m_pWin->SetPosSizePixel(_rPlayground.TopLeft() , _rPlayground.GetSize() ); + } + // just for completeness: there is no space left, we occupied it all ... + _rPlayground.SetPos( _rPlayground.BottomRight() ); + _rPlayground.SetSize( Size( 0, 0 ) ); +} + +bool OApplicationView::PreNotify( NotifyEvent& rNEvt ) +{ + switch(rNEvt.GetType()) + { + case MouseNotifyEvent::GETFOCUS: + if( m_pWin && getPanel() && getPanel()->HasChildPathFocus() ) + m_eChildFocus = PANELSWAP; + else if ( m_pWin && getDetailView() && getDetailView()->HasChildPathFocus() ) + m_eChildFocus = DETAIL; + else + m_eChildFocus = NONE; + break; + case MouseNotifyEvent::KEYINPUT: + { + const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent(); + // give the pane the chance to intercept mnemonic accelerators + // #i34790# + if ( getPanel() && getPanel()->interceptKeyInput( *pKeyEvent ) ) + return true; + // and ditto the detail view + // #i72799# + if ( getDetailView() && getDetailView()->interceptKeyInput( *pKeyEvent ) ) + return true; + } + break; + default: + break; + } + + return ODataView::PreNotify(rNEvt); +} + +IClipboardTest* OApplicationView::getActiveChild() const +{ + IClipboardTest* pTest = nullptr; + if ( DETAIL == m_eChildFocus ) + pTest = getDetailView(); + return pTest; +} + +bool OApplicationView::isCopyAllowed() +{ + IClipboardTest* pTest = getActiveChild(); + return pTest && pTest->isCopyAllowed(); +} + +bool OApplicationView::isCutAllowed() +{ + IClipboardTest* pTest = getActiveChild(); + return pTest && pTest->isCutAllowed(); +} + +bool OApplicationView::isPasteAllowed() +{ + IClipboardTest* pTest = getActiveChild(); + return pTest && pTest->isPasteAllowed(); +} + +void OApplicationView::copy() +{ + IClipboardTest* pTest = getActiveChild(); + if ( pTest ) + pTest->copy(); +} + +void OApplicationView::cut() +{ + IClipboardTest* pTest = getActiveChild(); + if ( pTest ) + pTest->cut(); +} + +void OApplicationView::paste() +{ + IClipboardTest* pTest = getActiveChild(); + if ( pTest ) + pTest->paste(); +} + +OUString OApplicationView::getQualifiedName( SvTreeListEntry* _pEntry ) const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->getQualifiedName( _pEntry ); +} + +bool OApplicationView::isLeaf(SvTreeListEntry const * _pEntry) const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return OApplicationDetailView::isLeaf(_pEntry); +} + +bool OApplicationView::isALeafSelected() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->isALeafSelected(); +} + +void OApplicationView::selectAll() +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->selectAll(); +} + +bool OApplicationView::isSortUp() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->isSortUp(); +} + +void OApplicationView::sortDown() +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->sortDown(); +} + +void OApplicationView::sortUp() +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->sortUp(); +} + +bool OApplicationView::isFilled() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->isFilled(); +} + +ElementType OApplicationView::getElementType() const +{ + OSL_ENSURE(m_pWin && getDetailView() && getPanel(),"Detail view is NULL! -> GPF"); + return getDetailView()->HasChildPathFocus() ? getDetailView()->getElementType() : getPanel()->getElementType(); +} + +sal_Int32 OApplicationView::getSelectionCount() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->getSelectionCount(); +} + +sal_Int32 OApplicationView::getElementCount() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->getElementCount(); +} + +void OApplicationView::getSelectionElementNames( std::vector< OUString>& _rNames ) const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->getSelectionElementNames( _rNames ); +} + +void OApplicationView::describeCurrentSelectionForControl( const Control& _rControl, Sequence< NamedDatabaseObject >& _out_rSelectedObjects ) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->describeCurrentSelectionForControl( _rControl, _out_rSelectedObjects ); +} + +void OApplicationView::describeCurrentSelectionForType( const ElementType _eType, Sequence< NamedDatabaseObject >& _out_rSelectedObjects ) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->describeCurrentSelectionForType( _eType, _out_rSelectedObjects ); +} + +void OApplicationView::selectElements(const Sequence< OUString>& _aNames) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->selectElements( _aNames ); +} + +SvTreeListEntry* OApplicationView::elementAdded(ElementType eType,const OUString& _rName, const Any& _rObject ) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->elementAdded(eType,_rName,_rObject); +} + +void OApplicationView::elementRemoved(ElementType eType,const OUString& _rName ) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->elementRemoved(eType,_rName); +} + +void OApplicationView::elementReplaced(ElementType _eType + ,const OUString& _rOldName + ,const OUString& _rNewName ) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->elementReplaced(_eType, _rOldName, _rNewName ); +} + +void OApplicationView::clearPages() +{ + OSL_ENSURE(m_pWin && getDetailView() && getPanel(),"Detail view is NULL! -> GPF"); + getPanel()->clearSelection(); + getDetailView()->clearPages(); +} + +void OApplicationView::selectContainer(ElementType _eType) +{ + OSL_ENSURE(m_pWin && getPanel(),"Detail view is NULL! -> GPF"); + weld::WaitObject aWO(GetFrameWeld()); + getPanel()->selectContainer(_eType); +} + +SvTreeListEntry* OApplicationView::getEntry( const Point& _aPosPixel ) const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->getEntry(_aPosPixel); +} + +PreviewMode OApplicationView::getPreviewMode() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->getPreviewMode(); +} + +bool OApplicationView::isPreviewEnabled() const +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + return getDetailView()->isPreviewEnabled(); +} + +void OApplicationView::switchPreview(PreviewMode _eMode) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + getDetailView()->switchPreview(_eMode); +} + +void OApplicationView::showPreview(const Reference< XContent >& _xContent) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + stopComponentListening(m_xObject); + m_xObject = nullptr; + getDetailView()->showPreview(_xContent); +} + +void OApplicationView::showPreview( const OUString& _sDataSourceName, + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const OUString& _sName, + bool _bTable) +{ + OSL_ENSURE(m_pWin && getDetailView(),"Detail view is NULL! -> GPF"); + if ( !isPreviewEnabled() ) + return; + + stopComponentListening(m_xObject); + m_xObject = nullptr; + try + { + Reference<XNameAccess> xNameAccess; + if ( _bTable ) + { + Reference<XTablesSupplier> xSup(_xConnection,UNO_QUERY); + if ( xSup.is() ) + xNameAccess = xSup->getTables(); + } + else + { + Reference<XQueriesSupplier> xSup(_xConnection,UNO_QUERY); + if ( xSup.is() ) + xNameAccess = xSup->getQueries(); + } + if ( xNameAccess.is() && xNameAccess->hasByName(_sName) ) + m_xObject.set(xNameAccess->getByName(_sName),UNO_QUERY); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + if ( m_xObject.is() ) + startComponentListening(m_xObject); + getDetailView()->showPreview(_sDataSourceName,_sName,_bTable); +} + +void OApplicationView::GetFocus() +{ + if ( m_eChildFocus == NONE && m_pWin ) + { + m_pWin->GrabFocus(); + } +} + +void OApplicationView::_disposing( const css::lang::EventObject& /*_rSource*/ ) +{ + if ( m_pWin && getDetailView() ) + showPreview(nullptr); +} + +void OApplicationView::ImplInitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + vcl::Font aFont = rStyleSettings.GetFieldFont(); + aFont.SetColor( rStyleSettings.GetWindowTextColor() ); + SetPointFont(*this, aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground( rStyleSettings.GetFieldColor() ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/AppView.hxx b/dbaccess/source/ui/app/AppView.hxx new file mode 100644 index 000000000..0d492953a --- /dev/null +++ b/dbaccess/source/ui/app/AppView.hxx @@ -0,0 +1,297 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_APPVIEW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_APPVIEW_HXX + +#include <dbaccess/dataview.hxx> +#include <com/sun/star/ucb/XContent.hpp> +#include <com/sun/star/sdb/application/NamedDatabaseObject.hpp> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <unotools/eventlisteneradapter.hxx> +#include <IClipBoardTest.hxx> +#include <AppElementType.hxx> + +namespace com::sun::star::beans { class XPropertySet; } + +class Control; +class SvTreeListEntry; +class MnemonicGenerator; + +namespace dbaui +{ + class OApplicationView; + class OApplicationDetailView; + class OApplicationSwapWindow; + class OTitleWindow; + class OApplicationController; + + class OAppBorderWindow : public vcl::Window + { + VclPtr<OTitleWindow> m_pPanel; + VclPtr<OApplicationDetailView> m_pDetailView; + VclPtr<OApplicationView> m_pView; + + void ImplInitSettings(); + protected: + // Window + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + public: + OAppBorderWindow(OApplicationView* _pParent,PreviewMode _ePreviewMode); + virtual ~OAppBorderWindow() override; + virtual void dispose() override; + + // Window overrides + virtual void GetFocus() override; + virtual void Resize() override; + + OApplicationView* getView() const { return m_pView;} + OApplicationSwapWindow* getPanel() const; + OApplicationDetailView* getDetailView() const { return m_pDetailView;} + }; + + class OApplicationView : public ODataView + ,public IClipboardTest + ,public ::utl::OEventListenerAdapter + { + enum ChildFocusState + { + PANELSWAP, + DETAIL, + NONE + }; + private: + css::uno::Reference< css::lang::XComponent > + m_xObject; + VclPtr<OAppBorderWindow> m_pWin; + OApplicationController& m_rAppController; + ChildFocusState m_eChildFocus; + + IClipboardTest* getActiveChild() const; + + void ImplInitSettings(); + protected: + + // return the Rectangle where I can paint myself + virtual void resizeDocumentView(tools::Rectangle& rRect) override; + + // OEventListenerAdapter + virtual void _disposing( const css::lang::EventObject& _rSource ) override; + + // Window + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + public: + OApplicationView( vcl::Window* pParent + ,const css::uno::Reference< css::uno::XComponentContext >& + ,OApplicationController& _rAppController + ,PreviewMode _ePreviewMode + ); + virtual ~OApplicationView() override; + virtual void dispose() override; + + /// automatically creates mnemonics for the icon/texts in our left hand side panel + void createIconAutoMnemonics( MnemonicGenerator& _rMnemonics ); + + /// automatically creates mnemonics for the texts in our task pane + void setTaskExternalMnemonics( MnemonicGenerator const & _rMnemonics ); + + // Window overrides + virtual bool PreNotify( NotifyEvent& rNEvt ) override; + virtual void GetFocus() override; + + OApplicationController& getAppController() const { return m_rAppController; } + + // IClipboardTest + virtual bool isCutAllowed() override; + virtual bool isCopyAllowed() override; + virtual bool isPasteAllowed() override; + virtual void copy() override; + virtual void cut() override; + virtual void paste() override; + + /// get the left panel + OApplicationSwapWindow* getPanel() const { return m_pWin->getPanel(); } + /// get the detail page + OApplicationDetailView* getDetailView() const { return m_pWin->getDetailView(); } + + /** return the qualified name. + @param _pEntry + The entry of a table, or query, form, report to get the qualified name. + If the entry is <NULL/>, the first selected is chosen. + @return + the qualified name + */ + OUString getQualifiedName( SvTreeListEntry* _pEntry ) const; + + /** returns if an entry is a leaf + @param _pEntry + The entry to check + @return + <TRUE/> if the entry is a leaf, otherwise <FALSE/> + */ + bool isLeaf(SvTreeListEntry const * _pEntry) const; + + /** returns if one of the selected entries is a leaf + @return + <TRUE/> if the entry is a leaf, otherwise <FALSE/> + */ + bool isALeafSelected() const; + + /** select all entries in the detail page + */ + void selectAll(); + + /// returns <TRUE/> if it sorts ascending + bool isSortUp() const; + + /// sort the entries in the detail page down + void sortDown(); + + /// sort the entries in the detail page up + void sortUp(); + + /// returns <TRUE/> when a detail page was filled + bool isFilled() const; + + /// return the element of currently select entry + ElementType getElementType() const; + + /// returns the count of entries + sal_Int32 getElementCount() const; + + /// returns the count of selected entries + sal_Int32 getSelectionCount() const; + + /** clears the detail page and the selection on the left side. + The task window will also be cleared. + */ + void clearPages(); + + /** returns the element names which are selected + @param _rNames + The list will be filled. + */ + void getSelectionElementNames( std::vector< OUString>& _rNames ) const; + + /** describes the current selection for the given control + */ + void describeCurrentSelectionForControl( + const Control& _rControl, + css::uno::Sequence< css::sdb::application::NamedDatabaseObject >& _out_rSelectedObjects + ); + + /** describes the current selection for the given ElementType + */ + void describeCurrentSelectionForType( + const ElementType _eType, + css::uno::Sequence< css::sdb::application::NamedDatabaseObject >& _out_rSelectedObjects + ); + + /** select all names on the currently selected container. Non existence names where ignored. + * + * \param _aNames the element names + */ + void selectElements(const css::uno::Sequence< OUString>& _aNames); + + /** adds a new object to the detail page. + @param _eType + The type where the entry should be appended. + @param _rName + The name of the object to be inserted + @param _rObject + The object to add. + @param _rxConn + If we insert a table, the connection must be set. + */ + SvTreeListEntry* elementAdded(ElementType _eType + ,const OUString& _rName + ,const css::uno::Any& _rObject ); + + /** replaces an objects name with a new one + @param _eType + The type where the entry should be appended. + @param _rOldName + The old name of the object to be replaced + @param _rNewName + The new name of the object to be replaced + @param _rxConn + If we insert a table, the connection must be set. + @param _xObject + The object which was replaced + */ + void elementReplaced(ElementType eType + ,const OUString& _rOldName + ,const OUString& _rNewName ); + + /** removes an element from the detail page. + @param _eType + The type where the entry should be appended. + @param _rName + The name of the element to be removed. + @param _rxConn + If we remove a table, the connection must be set. + */ + void elementRemoved(ElementType _eType + ,const OUString& _rName ); + + /** changes the container which should be displayed. The select handler will also be called. + @param _eType + Which container to show. + */ + void selectContainer(ElementType _eType); + + /// returns the preview mode + PreviewMode getPreviewMode() const; + + /// <TRUE/> if the preview is enabled + bool isPreviewEnabled() const; + + /** switches to the given preview mode + @param _eMode + the mode to set for the preview + */ + void switchPreview(PreviewMode _eMode); + + /** shows the Preview of the content when it is enabled. + @param _xContent + The content which must support the "preview" command. + */ + void showPreview(const css::uno::Reference< css::ucb::XContent >& _xContent); + + /** shows the Preview of a table or query + @param _sDataSourceName + the name of the data source + @param _xConnection + the connection which will be shared + @param _sName + the name of table or query + @param _bTable + <TRUE/> if it is a table, otherwise <FALSE/> + @return void + */ + void showPreview( const OUString& _sDataSourceName, + const css::uno::Reference< css::sdbc::XConnection>& _xConnection, + const OUString& _sName, + bool _bTable); + + SvTreeListEntry* getEntry( const Point& _aPosPixel ) const; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_APPVIEW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/subcomponentmanager.cxx b/dbaccess/source/ui/app/subcomponentmanager.cxx new file mode 100644 index 000000000..08df1dff0 --- /dev/null +++ b/dbaccess/source/ui/app/subcomponentmanager.cxx @@ -0,0 +1,555 @@ +/* -*- 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 "subcomponentmanager.hxx" +#include "AppController.hxx" +#include <strings.hxx> + +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/embed/XComponentSupplier.hpp> +#include <com/sun/star/ucb/XCommandProcessor.hpp> +#include <com/sun/star/document/XDocumentEventBroadcaster.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <tools/diagnose_ex.h> +#include <dbaccess/dataview.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> + +#include <algorithm> + +namespace dbaui +{ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::makeAny; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::frame::XFrame; + using ::com::sun::star::frame::XController; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::lang::XComponent; + using ::com::sun::star::frame::XModel2; + using ::com::sun::star::container::XEnumeration; + using ::com::sun::star::util::XCloseable; + using ::com::sun::star::awt::XTopWindow; + using ::com::sun::star::embed::XComponentSupplier; + using ::com::sun::star::ucb::XCommandProcessor; + using ::com::sun::star::ucb::Command; + using ::com::sun::star::document::XDocumentEventBroadcaster; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::beans::PropertyChangeEvent; + + // helper structs + namespace + { + struct SubComponentDescriptor + { + /// the name of the sub component, empty if it is yet unsaved + OUString sName; + /// type of the component - an ElementType value, except for relation design + sal_Int32 nComponentType; + /// the mode in which the sub component has been opened + ElementOpenMode eOpenMode; + /// the frame which the component resides in. Must not be <NULL/> + Reference< XFrame > xFrame; + /// the controller of the sub component. Must not be <NULL/> + Reference< XController > xController; + /// the model of the sub component. Might be <NULL/> + Reference< XModel > xModel; + /// the document definition which holds the component, if any; as CommandProcessor + Reference< XCommandProcessor > xComponentCommandProcessor; + /// the document definition which holds the component, if any; as PropertySet + Reference< XPropertySet > xDocumentDefinitionProperties; + + SubComponentDescriptor() + :sName() + ,nComponentType( -1 ) + ,eOpenMode( E_OPEN_NORMAL ) + ,xFrame() + ,xController() + ,xModel() + { + } + + SubComponentDescriptor( const OUString& i_rName, const sal_Int32 i_nComponentType, + const ElementOpenMode i_eOpenMode, const Reference< XComponent >& i_rComponent ) + :sName( i_rName ) + ,nComponentType( i_nComponentType ) + ,eOpenMode( i_eOpenMode ) + { + if ( !impl_constructFrom( i_rComponent ) ) + { + // i_rComponent is neither a model, nor a controller, nor a frame + // => it must be a css.sdb.DocumentDefinition + Reference< XComponentSupplier > xCompSupp( i_rComponent, UNO_QUERY_THROW ); + Reference< XComponent > xComponent( xCompSupp->getComponent(), UNO_QUERY_THROW ); + if ( !impl_constructFrom( xComponent ) ) + throw RuntimeException("Illegal component type." ); + xComponentCommandProcessor.set( i_rComponent, UNO_QUERY_THROW ); + xDocumentDefinitionProperties.set( i_rComponent, UNO_QUERY_THROW ); + } + } + + bool is() const { return xFrame.is(); } + + private: + bool impl_constructFrom( const Reference< XComponent >& _rxComponent ) + { + // is it a model? + xModel.set( _rxComponent, UNO_QUERY ); + if ( xModel.is() ) + { + xController.set( xModel->getCurrentController() ); + if ( xController.is() ) + xFrame.set( xController->getFrame(), UNO_SET_THROW ); + } + else + { + // is it a controller? + xController.set( _rxComponent, UNO_QUERY ); + if ( xController.is() ) + { + xFrame.set( xController->getFrame(), UNO_SET_THROW ); + } + else + { + // is it a frame? + xFrame.set( _rxComponent, UNO_QUERY ); + if ( !xFrame.is() ) + return false; + + // ensure we have a controller + xController.set( xFrame->getController(), UNO_SET_THROW ); + } + + // check whether there is a model (not required) + xModel.set( xController->getModel() ); + } + + return true; + } + }; + + struct SelectSubComponent + { + Reference< XComponent > operator()( const SubComponentDescriptor &_desc ) const + { + if ( _desc.xModel.is() ) + return _desc.xModel.get(); + OSL_ENSURE( _desc.xController.is(), "SelectSubComponent::operator(): illegal component!" ); + return _desc.xController.get(); + } + }; + + typedef std::vector< SubComponentDescriptor > SubComponents; + + struct SubComponentMatch + { + public: + SubComponentMatch( const OUString& i_rName, const sal_Int32 i_nComponentType, + const ElementOpenMode i_eOpenMode ) + :m_sName( i_rName ) + ,m_nComponentType( i_nComponentType ) + ,m_eOpenMode( i_eOpenMode ) + { + } + + bool operator()( const SubComponentDescriptor& i_rCompareWith ) const + { + return ( m_sName == i_rCompareWith.sName ) + && ( m_nComponentType == i_rCompareWith.nComponentType ) + && ( m_eOpenMode == i_rCompareWith.eOpenMode ); + } + private: + const OUString m_sName; + const sal_Int32 m_nComponentType; + const ElementOpenMode m_eOpenMode; + }; + } + + // SubComponentManager_Data + struct SubComponentManager_Data + { + SubComponentManager_Data( OApplicationController& _rController, const ::comphelper::SharedMutex& _rMutex ) + :m_rController( _rController ) + ,m_aMutex( _rMutex ) + { + } + + OApplicationController& m_rController; + mutable ::comphelper::SharedMutex m_aMutex; + SubComponents m_aComponents; + + ::osl::Mutex& getMutex() const { return m_aMutex; } + }; + + // SubComponentManager + SubComponentManager::SubComponentManager( OApplicationController& _rController, const ::comphelper::SharedMutex& _rMutex ) + :m_pData( new SubComponentManager_Data( _rController, _rMutex ) ) + { + } + + SubComponentManager::~SubComponentManager() + { + } + + void SubComponentManager::disposing() + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + m_pData->m_aComponents.clear(); + } + + namespace + { + bool lcl_fallbackToAnotherController( SubComponentDescriptor& _rCompDesc ) + { + Reference< XController > xFallback; + OSL_PRECOND( _rCompDesc.xModel.is(), "lcl_fallbackToAnotherController: illegal call!" ); + if ( !_rCompDesc.xModel.is() ) + return false; + + xFallback.set( _rCompDesc.xModel->getCurrentController() ); + if ( xFallback == _rCompDesc.xController ) + // don't accept the very same controller as fallback + xFallback.clear(); + + if ( !xFallback.is() ) + { + // perhaps XModel2 can be of help here + Reference< XModel2 > xModel2( _rCompDesc.xModel, UNO_QUERY ); + Reference< XEnumeration > xControllerEnum; + if ( xModel2.is() ) + xControllerEnum = xModel2->getControllers(); + while ( xControllerEnum.is() && xControllerEnum->hasMoreElements() ) + { + xFallback.set( xControllerEnum->nextElement(), UNO_QUERY ); + if ( xFallback == _rCompDesc.xController ) + xFallback.clear(); + } + } + + if ( xFallback.is() ) + { + _rCompDesc.xController = xFallback; + _rCompDesc.xFrame.set( xFallback->getFrame(), UNO_SET_THROW ); + return true; + } + + return false; + } + + bool lcl_closeComponent( const Reference< XCommandProcessor >& _rxCommandProcessor ) + { + bool bSuccess = false; + try + { + sal_Int32 nCommandIdentifier = _rxCommandProcessor->createCommandIdentifier(); + + Command aCommand; + aCommand.Name = "close"; + _rxCommandProcessor->execute( aCommand, nCommandIdentifier, nullptr ); + bSuccess = true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bSuccess; + } + + bool lcl_closeComponent( const SubComponentDescriptor& _rComponent ) + { + if ( _rComponent.xComponentCommandProcessor.is() ) + return lcl_closeComponent( _rComponent.xComponentCommandProcessor ); + + Reference< XController > xController( _rComponent.xController ); + OSL_ENSURE( xController.is(), "lcl_closeComponent: invalid controller!" ); + + // suspend the controller in the document + if ( xController.is() ) + if ( !xController->suspend( true ) ) + return false; + + bool bSuccess = false; + try + { + Reference< XCloseable > xCloseable( _rComponent.xFrame, UNO_QUERY_THROW ); + xCloseable->close( true ); + bSuccess = true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return bSuccess; + } + + void lcl_notifySubComponentEvent( const SubComponentManager_Data& _rData, const char* _pAsciiEventName, + const SubComponentDescriptor& _rComponent ) + { + try + { + Reference< XDocumentEventBroadcaster > xBroadcaster( _rData.m_rController.getModel(), UNO_QUERY_THROW ); + xBroadcaster->notifyDocumentEvent( + OUString::createFromAscii( _pAsciiEventName ), + &_rData.m_rController, + makeAny( _rComponent.xFrame ) + ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + } + + void SAL_CALL SubComponentManager::propertyChange( const PropertyChangeEvent& i_rEvent ) + { + if ( i_rEvent.PropertyName != PROPERTY_NAME ) + // by definition, it's allowed to broadcast more than what we've registered for + return; + + // find the sub component whose name changed + for (auto & component : m_pData->m_aComponents) + { + if ( component.xDocumentDefinitionProperties != i_rEvent.Source ) + continue; + + OUString sNewName; + OSL_VERIFY( i_rEvent.NewValue >>= sNewName ); + + #if OSL_DEBUG_LEVEL > 0 + OUString sOldKnownName( component.sName ); + OUString sOldName; + OSL_VERIFY( i_rEvent.OldValue >>= sOldName ); + OSL_ENSURE( sOldName == sOldKnownName, "SubComponentManager::propertyChange: inconsistency in the old names!" ); + #endif + + component.sName = sNewName; + break; + } + } + + void SAL_CALL SubComponentManager::disposing( const EventObject& _rSource ) + { + ::osl::ClearableMutexGuard aGuard( m_pData->getMutex() ); + + SubComponentDescriptor aClosedComponent; + + for ( SubComponents::iterator comp = m_pData->m_aComponents.begin(); + comp != m_pData->m_aComponents.end(); + ++comp + ) + { + bool bRemove = false; + + if ( comp->xController == _rSource.Source ) + { + if ( !comp->xModel.is() ) + { + bRemove = true; + } + else + { + // maybe this is just one view to the sub document, and only this view is closed + if ( !lcl_fallbackToAnotherController( *comp ) ) + { + bRemove = true; + } + } + } + else if ( comp->xModel == _rSource.Source ) + { + bRemove = true; + } + + if ( bRemove ) + { + aClosedComponent = *comp; + m_pData->m_aComponents.erase( comp ); + break; + } + } + + if ( aClosedComponent.is() ) + { + aGuard.clear(); + lcl_notifySubComponentEvent( *m_pData, "OnSubComponentClosed", aClosedComponent ); + } + } + + Sequence< Reference< XComponent> > SubComponentManager::getSubComponents() const + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + + Sequence< Reference< XComponent > > aComponents( m_pData->m_aComponents.size() ); + std::transform( + m_pData->m_aComponents.begin(), + m_pData->m_aComponents.end(), + aComponents.getArray(), + SelectSubComponent() + ); + return aComponents; + } + + bool SubComponentManager::closeSubComponents() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + + try + { + SubComponents aWorkingCopy( m_pData->m_aComponents ); + for (auto const& elem : aWorkingCopy) + { + lcl_closeComponent(elem); + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + return empty(); + } + + bool SubComponentManager::empty() const + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + return m_pData->m_aComponents.empty(); + } + + void SubComponentManager::onSubComponentOpened( const OUString& _rName, const sal_Int32 _nComponentType, + const ElementOpenMode _eOpenMode, const Reference< XComponent >& _rxComponent ) + { + ::osl::ClearableMutexGuard aGuard( m_pData->getMutex() ); + +#if OSL_DEBUG_LEVEL > 0 + if ( !_rName.isEmpty() ) + { + // check there does not already exist such a component + auto subComponentNotExists = std::none_of( + m_pData->m_aComponents.begin(), + m_pData->m_aComponents.end(), + SubComponentMatch( _rName, _nComponentType, _eOpenMode ) + ); + OSL_ENSURE( subComponentNotExists, "already existent!" ); + } +#endif + SubComponentDescriptor aElement( _rName, _nComponentType, _eOpenMode, _rxComponent ); + ENSURE_OR_THROW( aElement.xModel.is() || aElement.xController.is(), "illegal component" ); + + m_pData->m_aComponents.push_back( aElement ); + + // add as listener + if ( aElement.xController.is() ) + aElement.xController->addEventListener( this ); + if ( aElement.xModel.is() ) + aElement.xModel->addEventListener( this ); + if ( aElement.xDocumentDefinitionProperties.is() ) + aElement.xDocumentDefinitionProperties->addPropertyChangeListener( PROPERTY_NAME, this ); + + // notify this to interested parties + aGuard.clear(); + lcl_notifySubComponentEvent( *m_pData, "OnSubComponentOpened", aElement ); + } + + bool SubComponentManager::activateSubFrame( const OUString& _rName, const sal_Int32 _nComponentType, + const ElementOpenMode _eOpenMode, Reference< XComponent >& o_rComponent ) const + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + + SubComponents::const_iterator pos = std::find_if( + m_pData->m_aComponents.begin(), + m_pData->m_aComponents.end(), + SubComponentMatch( _rName, _nComponentType, _eOpenMode ) + ); + if ( pos == m_pData->m_aComponents.end() ) + // no component with this name/type/open mode + return false; + + const Reference< XFrame > xFrame( pos->xFrame, UNO_SET_THROW ); + const Reference< XTopWindow > xTopWindow( xFrame->getContainerWindow(), UNO_QUERY_THROW ); + xTopWindow->toFront(); + + if ( pos->xModel.is() ) + o_rComponent = pos->xModel.get(); + else if ( pos->xController.is() ) + o_rComponent = pos->xController.get(); + else + o_rComponent = pos->xFrame.get(); + + return true; + } + + bool SubComponentManager::closeSubFrames( const OUString& i_rName, const sal_Int32 _nComponentType ) + { + ::osl::MutexGuard aGuard( m_pData->getMutex() ); + ENSURE_OR_RETURN_FALSE( !i_rName.isEmpty(), "SubComponentManager::closeSubFrames: illegal name!" ); + + SubComponents aWorkingCopy( m_pData->m_aComponents ); + for (auto const& elem : aWorkingCopy) + { + if ( ( elem.sName != i_rName ) || ( elem.nComponentType != _nComponentType ) ) + continue; + + if ( !lcl_closeComponent(elem) ) + return false; + } + + return true; + } + + bool SubComponentManager::lookupSubComponent( const Reference< XComponent >& i_rComponent, + OUString& o_rName, sal_Int32& o_rComponentType ) + { + for (auto const& component : m_pData->m_aComponents) + { + if ( ( component.xModel.is() + && ( component.xModel == i_rComponent ) + ) + || ( component.xController.is() + && ( component.xController == i_rComponent ) + ) + || ( component.xFrame.is() + && ( component.xFrame == i_rComponent ) + ) + ) + { + o_rName = component.sName; + o_rComponentType = component.nComponentType; + return true; + } + } + return false; + } + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/subcomponentmanager.hxx b/dbaccess/source/ui/app/subcomponentmanager.hxx new file mode 100644 index 000000000..a2f56badc --- /dev/null +++ b/dbaccess/source/ui/app/subcomponentmanager.hxx @@ -0,0 +1,123 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_DBACCESS_SOURCE_UI_APP_SUBCOMPONENTMANAGER_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_APP_SUBCOMPONENTMANAGER_HXX + +#include <AppElementType.hxx> + +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +#include <comphelper/sharedmutex.hxx> +#include <cppuhelper/implbase.hxx> + +#include <memory> + +namespace dbaui +{ + + struct SubComponentManager_Data; + class OApplicationController; + + // SubComponentManager + typedef ::cppu::WeakImplHelper< css::beans::XPropertyChangeListener + > SubComponentManager_Base; + class SubComponentManager : public SubComponentManager_Base + { + public: + SubComponentManager( OApplicationController& _rController, const ::comphelper::SharedMutex& _rMutex ); + virtual ~SubComponentManager() override; + + void disposing(); + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& evt ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + // XDatabaseDocumentUI helpers + css::uno::Sequence< css::uno::Reference< css::lang::XComponent> > + getSubComponents() const; + bool closeSubComponents(); + + // container access + void onSubComponentOpened( + const OUString& _rName, + const sal_Int32 _nComponentType, + const ElementOpenMode _eOpenMode, + const css::uno::Reference< css::lang::XComponent >& + _rxComponent + ); + bool empty() const; + + /** activates (i.e. brings to top) the frame in which the given component is loaded, if any + + @return + <TRUE/> if any only of such a frame was found, i.e. the component had already been loaded + previously + */ + bool activateSubFrame( + const OUString& _rName, + const sal_Int32 _nComponentType, + const ElementOpenMode _eOpenMode, + css::uno::Reference< css::lang::XComponent >& o_rComponent + ) const; + + /** closes all frames of the given component + + If a view for the component (given by name and type) has been loaded into one or more + frames (with potentially different OpenModes), then those frames are gracefully closed. + + @return + <TRUE/> if and only if closing those frames was successful, or frames for the given sub component + exist. + */ + bool closeSubFrames( + const OUString& _rName, + const sal_Int32 _nComponentType + ); + + /** searches for the given sub component + + @param i_rComponent + the sub component to look up + @param o_rName + contains, upon successful return, the name of the sub component + @param o_nComponentType + contains, upon successful return, the type of the sub component + @return + <TRUE/> if and only if the component was found + */ + bool lookupSubComponent( + const css::uno::Reference< css::lang::XComponent >& i_rComponent, + OUString& o_rName, + sal_Int32& o_rComponentType + ); + + private: + std::unique_ptr< SubComponentManager_Data > m_pData; + }; + +} // namespace dbaui + +#endif // INCLUDED_DBACCESS_SOURCE_UI_APP_SUBCOMPONENTMANAGER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/app/window_layout.txt b/dbaccess/source/ui/app/window_layout.txt new file mode 100644 index 000000000..abb15f00b --- /dev/null +++ b/dbaccess/source/ui/app/window_layout.txt @@ -0,0 +1,49 @@ +# +# 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 . +# + +(still unfinished) + +OApplicationView +| ++-OAppBorderWindow + | + +-OTitleWindow "Database" + | | + | +-OApplicationSwapWindow + | | + | +-OApplicationIconControl + | + +-OApplicationDetailView + | + +-OTitleWindow "Tasks" + | | + | +-OTasksWindow + | | + | +-OCreationList + | + +-OTitleWindow "Forms" "Tables" "Queries" "Reports" + | + +-OAppDetailPageHelper + | + +-SvTreeListBox* + | + +-FixedLine + | + +-Window (Border) + | + +-OPreviewWindow |