diff options
Diffstat (limited to 'basctl/source')
90 files changed, 32581 insertions, 0 deletions
diff --git a/basctl/source/accessibility/accessibledialogcontrolshape.cxx b/basctl/source/accessibility/accessibledialogcontrolshape.cxx new file mode 100644 index 0000000000..62a260948f --- /dev/null +++ b/basctl/source/accessibility/accessibledialogcontrolshape.cxx @@ -0,0 +1,534 @@ +/* -*- 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 <accessibledialogcontrolshape.hxx> +#include <baside3.hxx> +#include <dlgeddef.hxx> +#include <dlgedview.hxx> +#include <dlgedobj.hxx> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <toolkit/helper/convert.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/accessiblecontexthelper.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <i18nlangtag/languagetag.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::accessibility; +using namespace ::comphelper; + + + + +AccessibleDialogControlShape::AccessibleDialogControlShape (DialogWindow* pDialogWindow, DlgEdObj* pDlgEdObj) + :m_pDialogWindow( pDialogWindow ) + ,m_pDlgEdObj( pDlgEdObj ) +{ + if ( m_pDlgEdObj ) + m_xControlModel.set( m_pDlgEdObj->GetUnoControlModel(), UNO_QUERY ); + + if ( m_xControlModel.is() ) + m_xControlModel->addPropertyChangeListener( OUString(), static_cast< beans::XPropertyChangeListener* >( this ) ); + + m_bFocused = IsFocused(); + m_bSelected = IsSelected(); + m_aBounds = GetBounds(); +} + + +AccessibleDialogControlShape::~AccessibleDialogControlShape() +{ + if ( m_xControlModel.is() ) + m_xControlModel->removePropertyChangeListener( OUString(), static_cast< beans::XPropertyChangeListener* >( this ) ); +} + + +bool AccessibleDialogControlShape::IsFocused() const +{ + bool bFocused = false; + if ( m_pDialogWindow ) + { + SdrView& rView = m_pDialogWindow->GetView(); + if (rView.IsObjMarked(m_pDlgEdObj) && rView.GetMarkedObjectList().GetMarkCount() == 1) + bFocused = true; + } + + return bFocused; +} + + +bool AccessibleDialogControlShape::IsSelected() const +{ + if ( m_pDialogWindow ) + return m_pDialogWindow->GetView().IsObjMarked(m_pDlgEdObj); + return false; +} + + +void AccessibleDialogControlShape::SetFocused( bool bFocused ) +{ + if ( m_bFocused != bFocused ) + { + Any aOldValue, aNewValue; + if ( m_bFocused ) + aOldValue <<= AccessibleStateType::FOCUSED; + else + aNewValue <<= AccessibleStateType::FOCUSED; + m_bFocused = bFocused; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } +} + + +void AccessibleDialogControlShape::SetSelected( bool bSelected ) +{ + if ( m_bSelected != bSelected ) + { + Any aOldValue, aNewValue; + if ( m_bSelected ) + aOldValue <<= AccessibleStateType::SELECTED; + else + aNewValue <<= AccessibleStateType::SELECTED; + m_bSelected = bSelected; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } +} + + +awt::Rectangle AccessibleDialogControlShape::GetBounds() const +{ + awt::Rectangle aBounds( 0, 0, 0, 0 ); + if ( m_pDlgEdObj ) + { + // get the bounding box of the shape in logic units + tools::Rectangle aRect = m_pDlgEdObj->GetSnapRect(); + + if ( m_pDialogWindow ) + { + // transform coordinates relative to the parent + MapMode aMap = m_pDialogWindow->GetMapMode(); + Point aOrg = aMap.GetOrigin(); + aRect.Move( aOrg.X(), aOrg.Y() ); + + // convert logic units to pixel + aRect = m_pDialogWindow->LogicToPixel( aRect, MapMode(MapUnit::Map100thMM) ); + + // clip the shape's bounding box with the bounding box of its parent + tools::Rectangle aParentRect( Point( 0, 0 ), m_pDialogWindow->GetSizePixel() ); + aRect = aRect.GetIntersection( aParentRect ); + aBounds = AWTRectangle( aRect ); + } + } + + return aBounds; +} + + +void AccessibleDialogControlShape::SetBounds( const awt::Rectangle& aBounds ) +{ + if ( m_aBounds.X != aBounds.X || m_aBounds.Y != aBounds.Y || m_aBounds.Width != aBounds.Width || m_aBounds.Height != aBounds.Height ) + { + m_aBounds = aBounds; + NotifyAccessibleEvent( AccessibleEventId::BOUNDRECT_CHANGED, Any(), Any() ); + } +} + + +vcl::Window* AccessibleDialogControlShape::GetWindow() const +{ + vcl::Window* pWindow = nullptr; + if ( m_pDlgEdObj ) + { + Reference< awt::XControl > xControl = m_pDlgEdObj->GetControl(); + if ( xControl.is() ) + pWindow = VCLUnoHelper::GetWindow( xControl->getPeer() ); + } + + return pWindow; +} + + +OUString AccessibleDialogControlShape::GetModelStringProperty( OUString const & pPropertyName ) +{ + OUString sReturn; + + try + { + if ( m_xControlModel.is() ) + { + Reference< XPropertySetInfo > xInfo = m_xControlModel->getPropertySetInfo(); + if ( xInfo.is() && xInfo->hasPropertyByName( pPropertyName ) ) + m_xControlModel->getPropertyValue( pPropertyName ) >>= sReturn; + } + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "basctl", "AccessibleDialogControlShape::GetModelStringProperty" ); + } + + return sReturn; +} + + +void AccessibleDialogControlShape::FillAccessibleStateSet( sal_Int64& rStateSet ) +{ + rStateSet |= AccessibleStateType::ENABLED; + + rStateSet |= AccessibleStateType::VISIBLE; + + rStateSet |= AccessibleStateType::SHOWING; + + rStateSet |= AccessibleStateType::FOCUSABLE; + + if ( IsFocused() ) + rStateSet |= AccessibleStateType::FOCUSED; + + rStateSet |= AccessibleStateType::SELECTABLE; + + if ( IsSelected() ) + rStateSet |= AccessibleStateType::SELECTED; + + rStateSet |= AccessibleStateType::RESIZABLE; +} + +// OCommonAccessibleComponent +awt::Rectangle AccessibleDialogControlShape::implGetBounds() +{ + return GetBounds(); +} + +// XComponent +void AccessibleDialogControlShape::disposing() +{ + OAccessibleExtendedComponentHelper::disposing(); + + m_pDialogWindow = nullptr; + m_pDlgEdObj = nullptr; + + if ( m_xControlModel.is() ) + m_xControlModel->removePropertyChangeListener( OUString(), static_cast< beans::XPropertyChangeListener* >( this ) ); + m_xControlModel.clear(); +} + + +// XEventListener + + +void AccessibleDialogControlShape::disposing( const lang::EventObject& ) +{ + if ( m_xControlModel.is() ) + m_xControlModel->removePropertyChangeListener( OUString(), static_cast< beans::XPropertyChangeListener* >( this ) ); + m_xControlModel.clear(); +} + + +// XPropertyChangeListener + + +void AccessibleDialogControlShape::propertyChange( const beans::PropertyChangeEvent& rEvent ) +{ + if ( rEvent.PropertyName == DLGED_PROP_NAME ) + { + NotifyAccessibleEvent( AccessibleEventId::NAME_CHANGED, rEvent.OldValue, rEvent.NewValue ); + } + else if ( rEvent.PropertyName == DLGED_PROP_POSITIONX || + rEvent.PropertyName == DLGED_PROP_POSITIONY || + rEvent.PropertyName == DLGED_PROP_WIDTH || + rEvent.PropertyName == DLGED_PROP_HEIGHT ) + { + SetBounds( GetBounds() ); + } + else if ( rEvent.PropertyName == DLGED_PROP_BACKGROUNDCOLOR || + rEvent.PropertyName == DLGED_PROP_TEXTCOLOR || + rEvent.PropertyName == DLGED_PROP_TEXTLINECOLOR ) + { + NotifyAccessibleEvent( AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any() ); + } +} + +// XServiceInfo +OUString AccessibleDialogControlShape::getImplementationName() +{ + return "com.sun.star.comp.basctl.AccessibleShape"; +} + +sal_Bool AccessibleDialogControlShape::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > AccessibleDialogControlShape::getSupportedServiceNames() +{ + return { "com.sun.star.drawing.AccessibleShape" }; +} + +// XAccessible +Reference< XAccessibleContext > AccessibleDialogControlShape::getAccessibleContext( ) +{ + return this; +} + +// XAccessibleContext +sal_Int64 AccessibleDialogControlShape::getAccessibleChildCount() +{ + return 0; +} + + +Reference< XAccessible > AccessibleDialogControlShape::getAccessibleChild( sal_Int64 i ) +{ + OExternalLockGuard aGuard( this ); + + if ( i < 0 || i >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + return Reference< XAccessible >(); +} + + +Reference< XAccessible > AccessibleDialogControlShape::getAccessibleParent( ) +{ + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xParent; + if ( m_pDialogWindow ) + xParent = m_pDialogWindow->GetAccessible(); + + return xParent; +} + + +sal_Int64 AccessibleDialogControlShape::getAccessibleIndexInParent( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nIndexInParent = -1; + Reference< XAccessible > xParent( getAccessibleParent() ); + if ( xParent.is() ) + { + Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() ); + if ( xParentContext.is() ) + { + for ( sal_Int64 i = 0, nCount = xParentContext->getAccessibleChildCount(); i < nCount; ++i ) + { + Reference< XAccessible > xChild( xParentContext->getAccessibleChild( i ) ); + if ( xChild.is() ) + { + Reference< XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if ( xChildContext == static_cast<XAccessibleContext*>(this) ) + { + nIndexInParent = i; + break; + } + } + } + } + } + + return nIndexInParent; +} + + +sal_Int16 AccessibleDialogControlShape::getAccessibleRole( ) +{ + OExternalLockGuard aGuard( this ); + + return AccessibleRole::SHAPE; +} + + +OUString AccessibleDialogControlShape::getAccessibleDescription( ) +{ + OExternalLockGuard aGuard( this ); + + return GetModelStringProperty( "HelpText" ); +} + + +OUString AccessibleDialogControlShape::getAccessibleName( ) +{ + OExternalLockGuard aGuard( this ); + + return GetModelStringProperty( "Name" ); +} + + +Reference< XAccessibleRelationSet > AccessibleDialogControlShape::getAccessibleRelationSet( ) +{ + OExternalLockGuard aGuard( this ); + + return new utl::AccessibleRelationSetHelper; +} + + +sal_Int64 AccessibleDialogControlShape::getAccessibleStateSet( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nStateSet = 0; + + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + FillAccessibleStateSet( nStateSet ); + } + else + { + nStateSet |= AccessibleStateType::DEFUNC; + } + + return nStateSet; +} + + +Locale AccessibleDialogControlShape::getLocale( ) +{ + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); +} + + +// XAccessibleComponent + + +Reference< XAccessible > AccessibleDialogControlShape::getAccessibleAtPoint( const awt::Point& ) +{ + OExternalLockGuard aGuard( this ); + + return Reference< XAccessible >(); +} + + +void AccessibleDialogControlShape::grabFocus( ) +{ + // no focus for shapes +} + + +sal_Int32 AccessibleDialogControlShape::getForeground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + vcl::Window* pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsControlForeground() ) + nColor = pWindow->GetControlForeground(); + else + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + nColor = aFont.GetColor(); + } + } + + return sal_Int32(nColor); +} + + +sal_Int32 AccessibleDialogControlShape::getBackground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + vcl::Window* pWindow = GetWindow(); + if ( pWindow ) + { + if ( pWindow->IsControlBackground() ) + nColor = pWindow->GetControlBackground(); + else + nColor = pWindow->GetBackground().GetColor(); + } + + return sal_Int32(nColor); +} + + +// XAccessibleExtendedComponent + + +Reference< awt::XFont > AccessibleDialogControlShape::getFont( ) +{ + OExternalLockGuard aGuard( this ); + + Reference< awt::XFont > xFont; + vcl::Window* pWindow = GetWindow(); + if ( pWindow ) + { + Reference< awt::XDevice > xDev( pWindow->GetComponentInterface(), UNO_QUERY ); + if ( xDev.is() ) + { + vcl::Font aFont; + if ( pWindow->IsControlFont() ) + aFont = pWindow->GetControlFont(); + else + aFont = pWindow->GetFont(); + rtl::Reference<VCLXFont> pVCLXFont = new VCLXFont; + pVCLXFont->Init( *xDev, aFont ); + xFont = pVCLXFont; + } + } + + return xFont; +} + + +OUString AccessibleDialogControlShape::getTitledBorderText( ) +{ + OExternalLockGuard aGuard( this ); + + return OUString(); +} + + +OUString AccessibleDialogControlShape::getToolTipText( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sText; + vcl::Window* pWindow = GetWindow(); + if ( pWindow ) + sText = pWindow->GetQuickHelpText(); + + return sText; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/accessibility/accessibledialogwindow.cxx b/basctl/source/accessibility/accessibledialogwindow.cxx new file mode 100644 index 0000000000..d5c702616d --- /dev/null +++ b/basctl/source/accessibility/accessibledialogwindow.cxx @@ -0,0 +1,915 @@ +/* -*- 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 <accessibledialogwindow.hxx> +#include <accessibledialogcontrolshape.hxx> +#include <baside3.hxx> +#include <dlged.hxx> +#include <dlgedmod.hxx> +#include <dlgedpage.hxx> +#include <dlgedview.hxx> +#include <dlgedobj.hxx> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessiblecontexthelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <tools/debug.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <i18nlangtag/languagetag.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; +using namespace ::comphelper; + +AccessibleDialogWindow::ChildDescriptor::ChildDescriptor( DlgEdObj* _pDlgEdObj ) + :pDlgEdObj( _pDlgEdObj ) +{ +} + +bool AccessibleDialogWindow::ChildDescriptor::operator==( const ChildDescriptor& rDesc ) +{ + bool bRet = false; + if ( pDlgEdObj == rDesc.pDlgEdObj ) + bRet = true; + + return bRet; +} + + +bool AccessibleDialogWindow::ChildDescriptor::operator<( const ChildDescriptor& rDesc ) const +{ + bool bRet = false; + if ( pDlgEdObj && rDesc.pDlgEdObj && pDlgEdObj->GetOrdNum() < rDesc.pDlgEdObj->GetOrdNum() ) + bRet = true; + + return bRet; +} + + + + +AccessibleDialogWindow::AccessibleDialogWindow (basctl::DialogWindow* pDialogWindow) + : m_pDialogWindow(pDialogWindow) + , m_pDlgEdModel(nullptr) +{ + if ( !m_pDialogWindow ) + return; + + SdrPage& rPage = m_pDialogWindow->GetPage(); + + for (const rtl::Reference<SdrObject>& pObj : rPage) + { + if (DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj.get())) + { + ChildDescriptor aDesc( pDlgEdObj ); + if ( IsChildVisible( aDesc ) ) + m_aAccessibleChildren.push_back( aDesc ); + } + } + + m_pDialogWindow->AddEventListener( LINK( this, AccessibleDialogWindow, WindowEventListener ) ); + + StartListening(m_pDialogWindow->GetEditor()); + + m_pDlgEdModel = &m_pDialogWindow->GetModel(); + StartListening(*m_pDlgEdModel); +} + + +AccessibleDialogWindow::~AccessibleDialogWindow() +{ + if ( m_pDialogWindow ) + m_pDialogWindow->RemoveEventListener( LINK( this, AccessibleDialogWindow, WindowEventListener ) ); + + if ( m_pDlgEdModel ) + EndListening( *m_pDlgEdModel ); +} + + +void AccessibleDialogWindow::UpdateFocused() +{ + for (const ChildDescriptor & i : m_aAccessibleChildren) + { + if ( i.mxAccessible ) + i.mxAccessible->SetFocused( i.mxAccessible->IsFocused() ); + } +} + + +void AccessibleDialogWindow::UpdateSelected() +{ + NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + + for (const ChildDescriptor & i : m_aAccessibleChildren) + { + if ( i.mxAccessible ) + i.mxAccessible->SetSelected( i.mxAccessible->IsSelected() ); + } +} + + +void AccessibleDialogWindow::UpdateBounds() +{ + for (const ChildDescriptor & i : m_aAccessibleChildren) + { + if ( i.mxAccessible ) + i.mxAccessible->SetBounds( i.mxAccessible->GetBounds() ); + } +} + + +bool AccessibleDialogWindow::IsChildVisible( const ChildDescriptor& rDesc ) +{ + bool bVisible = false; + + if ( m_pDialogWindow ) + { + // first check, if the shape is in a visible layer + SdrLayerAdmin& rLayerAdmin = m_pDialogWindow->GetModel().GetLayerAdmin(); + DlgEdObj* pDlgEdObj = rDesc.pDlgEdObj; + if ( pDlgEdObj ) + { + SdrLayerID nLayerId = pDlgEdObj->GetLayer(); + const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId ); + if ( pSdrLayer ) + { + const OUString& aLayerName = pSdrLayer->GetName(); + SdrView& rView = m_pDialogWindow->GetView(); + if (rView.IsLayerVisible(aLayerName)) + { + // get the bounding box of the shape in logic units + tools::Rectangle aRect = pDlgEdObj->GetSnapRect(); + + // transform coordinates relative to the parent + MapMode aMap = m_pDialogWindow->GetMapMode(); + Point aOrg = aMap.GetOrigin(); + aRect.Move( aOrg.X(), aOrg.Y() ); + + // convert logic units to pixel + aRect = m_pDialogWindow->LogicToPixel( aRect, MapMode(MapUnit::Map100thMM) ); + + // check, if the shape's bounding box intersects with the bounding box of its parent + tools::Rectangle aParentRect( Point( 0, 0 ), m_pDialogWindow->GetSizePixel() ); + if ( aParentRect.Overlaps( aRect ) ) + bVisible = true; + } + } + } + } + + return bVisible; +} + + +void AccessibleDialogWindow::InsertChild( const ChildDescriptor& rDesc ) +{ + // check, if object is already in child list + AccessibleChildren::iterator aIter = std::find( m_aAccessibleChildren.begin(), m_aAccessibleChildren.end(), rDesc ); + + // if not found, insert in child list + if ( aIter != m_aAccessibleChildren.end() ) + return; + + // insert entry in child list + m_aAccessibleChildren.push_back( rDesc ); + + // get the accessible of the inserted child + Reference< XAccessible > xChild( getAccessibleChild( m_aAccessibleChildren.size() - 1 ) ); + + // sort child list + SortChildren(); + + // send accessible child event + if ( xChild.is() ) + { + Any aOldValue, aNewValue; + aNewValue <<= xChild; + NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue ); + } +} + + +void AccessibleDialogWindow::RemoveChild( const ChildDescriptor& rDesc ) +{ + // find object in child list + AccessibleChildren::iterator aIter = std::find( m_aAccessibleChildren.begin(), m_aAccessibleChildren.end(), rDesc ); + + // if found, remove from child list + if ( aIter == m_aAccessibleChildren.end() ) + return; + + // get the accessible of the removed child + rtl::Reference< AccessibleDialogControlShape > xChild( aIter->mxAccessible ); + + // remove entry from child list + m_aAccessibleChildren.erase( aIter ); + + // send accessible child event + if ( xChild.is() ) + { + Any aOldValue, aNewValue; + aOldValue <<= uno::Reference<XAccessible>(xChild); + NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue ); + + if ( xChild ) + xChild->dispose(); + } +} + + +void AccessibleDialogWindow::UpdateChild( const ChildDescriptor& rDesc ) +{ + if ( IsChildVisible( rDesc ) ) + { + // if the object is not in the child list, insert child + InsertChild( rDesc ); + } + else + { + // if the object is in the child list, remove child + RemoveChild( rDesc ); + } +} + + +void AccessibleDialogWindow::UpdateChildren() +{ + if ( m_pDialogWindow ) + { + SdrPage& rPage = m_pDialogWindow->GetPage(); + for (const rtl::Reference<SdrObject>& pObj : rPage) + if (DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj.get())) + UpdateChild( ChildDescriptor( pDlgEdObj ) ); + } +} + + +void AccessibleDialogWindow::SortChildren() +{ + // sort child list + std::sort( m_aAccessibleChildren.begin(), m_aAccessibleChildren.end() ); +} + + +IMPL_LINK( AccessibleDialogWindow, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + DBG_ASSERT(rEvent.GetWindow(), "AccessibleDialogWindow::WindowEventListener: no window!"); + if (!rEvent.GetWindow()->IsAccessibilityEventsSuppressed() || rEvent.GetId() == VclEventId::ObjectDying) + ProcessWindowEvent(rEvent); +} + + +void AccessibleDialogWindow::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + Any aOldValue, aNewValue; + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowEnabled: + { + aNewValue <<= AccessibleStateType::ENABLED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowDisabled: + { + aOldValue <<= AccessibleStateType::ENABLED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowActivate: + { + aNewValue <<= AccessibleStateType::ACTIVE; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowDeactivate: + { + aOldValue <<= AccessibleStateType::ACTIVE; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowGetFocus: + { + aNewValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowLoseFocus: + { + aOldValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowShow: + { + aNewValue <<= AccessibleStateType::SHOWING; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowHide: + { + aOldValue <<= AccessibleStateType::SHOWING; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowResize: + { + NotifyAccessibleEvent( AccessibleEventId::BOUNDRECT_CHANGED, aOldValue, aNewValue ); + UpdateChildren(); + UpdateBounds(); + } + break; + case VclEventId::ObjectDying: + { + if ( m_pDialogWindow ) + { + m_pDialogWindow->RemoveEventListener( LINK( this, AccessibleDialogWindow, WindowEventListener ) ); + m_pDialogWindow = nullptr; + + if ( m_pDlgEdModel ) + EndListening( *m_pDlgEdModel ); + m_pDlgEdModel = nullptr; + + // dispose all children + for (const ChildDescriptor & i : m_aAccessibleChildren) + { + if ( i.mxAccessible ) + i.mxAccessible->dispose(); + } + m_aAccessibleChildren.clear(); + } + } + break; + default: + { + } + break; + } +} + + +void AccessibleDialogWindow::FillAccessibleStateSet( sal_Int64& rStateSet ) +{ + if ( !m_pDialogWindow ) + return; + + if ( m_pDialogWindow->IsEnabled() ) + rStateSet |= AccessibleStateType::ENABLED; + + rStateSet |= AccessibleStateType::FOCUSABLE; + + if ( m_pDialogWindow->HasFocus() ) + rStateSet |= AccessibleStateType::FOCUSED; + + rStateSet |= AccessibleStateType::VISIBLE; + + if ( m_pDialogWindow->IsVisible() ) + rStateSet |= AccessibleStateType::SHOWING; + + rStateSet |= AccessibleStateType::OPAQUE; + + rStateSet |= AccessibleStateType::RESIZABLE; +} + + +// OCommonAccessibleComponent + + +awt::Rectangle AccessibleDialogWindow::implGetBounds() +{ + awt::Rectangle aBounds; + if ( m_pDialogWindow ) + aBounds = AWTRectangle( tools::Rectangle( m_pDialogWindow->GetPosPixel(), m_pDialogWindow->GetSizePixel() ) ); + + return aBounds; +} + + +// SfxListener + + +void AccessibleDialogWindow::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if (SdrHint const* pSdrHint = dynamic_cast<SdrHint const*>(&rHint)) + { + switch ( pSdrHint->GetKind() ) + { + case SdrHintKind::ObjectInserted: + { + if (DlgEdObj const* pDlgEdObj = dynamic_cast<DlgEdObj const*>(pSdrHint->GetObject())) + { + ChildDescriptor aDesc(const_cast<DlgEdObj*>(pDlgEdObj)); + if ( IsChildVisible( aDesc ) ) + InsertChild( aDesc ); + } + } + break; + case SdrHintKind::ObjectRemoved: + { + if (DlgEdObj const* pDlgEdObj = dynamic_cast<DlgEdObj const*>(pSdrHint->GetObject())) + RemoveChild( ChildDescriptor(const_cast<DlgEdObj*>(pDlgEdObj)) ); + } + break; + default: ; + } + } + else if (DlgEdHint const* pDlgEdHint = dynamic_cast<DlgEdHint const*>(&rHint)) + { + switch (pDlgEdHint->GetKind()) + { + case DlgEdHint::WINDOWSCROLLED: + { + UpdateChildren(); + UpdateBounds(); + } + break; + case DlgEdHint::LAYERCHANGED: + { + if (DlgEdObj* pDlgEdObj = pDlgEdHint->GetObject()) + UpdateChild( ChildDescriptor( pDlgEdObj ) ); + } + break; + case DlgEdHint::OBJORDERCHANGED: + { + SortChildren(); + } + break; + case DlgEdHint::SELECTIONCHANGED: + { + UpdateFocused(); + UpdateSelected(); + } + break; + default: ; + } + } +} + + +// XComponent + + +void AccessibleDialogWindow::disposing() +{ + OAccessibleExtendedComponentHelper::disposing(); + + if ( !m_pDialogWindow ) + return; + + m_pDialogWindow->RemoveEventListener( LINK( this, AccessibleDialogWindow, WindowEventListener ) ); + m_pDialogWindow = nullptr; + + if ( m_pDlgEdModel ) + EndListening( *m_pDlgEdModel ); + m_pDlgEdModel = nullptr; + + // dispose all children + for (const ChildDescriptor & i : m_aAccessibleChildren) + { + if ( i.mxAccessible ) + i.mxAccessible->dispose(); + } + m_aAccessibleChildren.clear(); +} + +// XServiceInfo +OUString AccessibleDialogWindow::getImplementationName() +{ + return "com.sun.star.comp.basctl.AccessibleWindow"; +} + +sal_Bool AccessibleDialogWindow::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > AccessibleDialogWindow::getSupportedServiceNames() +{ + return { "com.sun.star.awt.AccessibleWindow" }; +} + +// XAccessible +Reference< XAccessibleContext > AccessibleDialogWindow::getAccessibleContext( ) +{ + return this; +} + +// XAccessibleContext +sal_Int64 AccessibleDialogWindow::getAccessibleChildCount() +{ + OExternalLockGuard aGuard( this ); + + return m_aAccessibleChildren.size(); +} + + +Reference< XAccessible > AccessibleDialogWindow::getAccessibleChild( sal_Int64 i ) +{ + OExternalLockGuard aGuard( this ); + + if ( i < 0 || i >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + rtl::Reference< AccessibleDialogControlShape > xChild = m_aAccessibleChildren[i].mxAccessible; + if ( !xChild.is() ) + { + if ( m_pDialogWindow ) + { + DlgEdObj* pDlgEdObj = m_aAccessibleChildren[i].pDlgEdObj; + if ( pDlgEdObj ) + { + xChild = new AccessibleDialogControlShape( m_pDialogWindow, pDlgEdObj ); + + // insert into child list + m_aAccessibleChildren[i].mxAccessible = xChild; + } + } + } + + return xChild; +} + + +Reference< XAccessible > AccessibleDialogWindow::getAccessibleParent( ) +{ + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xParent; + if ( m_pDialogWindow ) + { + vcl::Window* pParent = m_pDialogWindow->GetAccessibleParentWindow(); + if ( pParent ) + xParent = pParent->GetAccessible(); + } + + return xParent; +} + + +sal_Int64 AccessibleDialogWindow::getAccessibleIndexInParent( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nIndexInParent = -1; + if ( m_pDialogWindow ) + { + vcl::Window* pParent = m_pDialogWindow->GetAccessibleParentWindow(); + if ( pParent ) + { + for ( sal_uInt16 i = 0, nCount = pParent->GetAccessibleChildWindowCount(); i < nCount; ++i ) + { + vcl::Window* pChild = pParent->GetAccessibleChildWindow( i ); + if ( pChild == static_cast< vcl::Window* >( m_pDialogWindow ) ) + { + nIndexInParent = i; + break; + } + } + } + } + + return nIndexInParent; +} + + +sal_Int16 AccessibleDialogWindow::getAccessibleRole( ) +{ + OExternalLockGuard aGuard( this ); + + return AccessibleRole::PANEL; +} + + +OUString AccessibleDialogWindow::getAccessibleDescription( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sDescription; + if ( m_pDialogWindow ) + sDescription = m_pDialogWindow->GetAccessibleDescription(); + + return sDescription; +} + + +OUString AccessibleDialogWindow::getAccessibleName( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sName; + if ( m_pDialogWindow ) + sName = m_pDialogWindow->GetAccessibleName(); + + return sName; +} + + +Reference< XAccessibleRelationSet > AccessibleDialogWindow::getAccessibleRelationSet( ) +{ + OExternalLockGuard aGuard( this ); + + return new utl::AccessibleRelationSetHelper; +} + + +sal_Int64 AccessibleDialogWindow::getAccessibleStateSet( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nStateSet = 0; + + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + FillAccessibleStateSet( nStateSet ); + } + else + { + nStateSet |= AccessibleStateType::DEFUNC; + } + + return nStateSet; +} + + +Locale AccessibleDialogWindow::getLocale( ) +{ + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); +} + + +// XAccessibleComponent + + +Reference< XAccessible > AccessibleDialogWindow::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xChild; + for ( size_t i = 0; i < m_aAccessibleChildren.size(); ++i ) + { + Reference< XAccessible > xAcc = getAccessibleChild( i ); + if ( xAcc.is() ) + { + Reference< XAccessibleComponent > xComp( xAcc->getAccessibleContext(), UNO_QUERY ); + if ( xComp.is() ) + { + tools::Rectangle aRect = VCLRectangle( xComp->getBounds() ); + Point aPos = VCLPoint( rPoint ); + if ( aRect.Contains( aPos ) ) + { + xChild = xAcc; + break; + } + } + } + } + + return xChild; +} + + +void AccessibleDialogWindow::grabFocus( ) +{ + OExternalLockGuard aGuard( this ); + + if ( m_pDialogWindow ) + m_pDialogWindow->GrabFocus(); +} + + +sal_Int32 AccessibleDialogWindow::getForeground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + if ( m_pDialogWindow ) + { + if ( m_pDialogWindow->IsControlForeground() ) + nColor = m_pDialogWindow->GetControlForeground(); + else + { + vcl::Font aFont; + if ( m_pDialogWindow->IsControlFont() ) + aFont = m_pDialogWindow->GetControlFont(); + else + aFont = m_pDialogWindow->GetFont(); + nColor = aFont.GetColor(); + } + } + + return sal_Int32(nColor); +} + + +sal_Int32 AccessibleDialogWindow::getBackground( ) +{ + OExternalLockGuard aGuard( this ); + + Color nColor; + if ( m_pDialogWindow ) + { + if ( m_pDialogWindow->IsControlBackground() ) + nColor = m_pDialogWindow->GetControlBackground(); + else + nColor = m_pDialogWindow->GetBackground().GetColor(); + } + + return sal_Int32(nColor); +} + + +// XAccessibleExtendedComponent + + +Reference< awt::XFont > AccessibleDialogWindow::getFont( ) +{ + OExternalLockGuard aGuard( this ); + + Reference< awt::XFont > xFont; + if ( m_pDialogWindow ) + { + Reference< awt::XDevice > xDev( m_pDialogWindow->GetComponentInterface(), UNO_QUERY ); + if ( xDev.is() ) + { + vcl::Font aFont; + if ( m_pDialogWindow->IsControlFont() ) + aFont = m_pDialogWindow->GetControlFont(); + else + aFont = m_pDialogWindow->GetFont(); + rtl::Reference<VCLXFont> pVCLXFont = new VCLXFont; + pVCLXFont->Init( *xDev, aFont ); + xFont = pVCLXFont; + } + } + + return xFont; +} + + +OUString AccessibleDialogWindow::getTitledBorderText( ) +{ + OExternalLockGuard aGuard( this ); + + return OUString(); +} + + +OUString AccessibleDialogWindow::getToolTipText( ) +{ + OExternalLockGuard aGuard( this ); + + OUString sText; + if ( m_pDialogWindow ) + sText = m_pDialogWindow->GetQuickHelpText(); + + return sText; +} + + +// XAccessibleSelection + + +void AccessibleDialogWindow::selectAccessibleChild( sal_Int64 nChildIndex ) +{ + OExternalLockGuard aGuard( this ); + + if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + if ( m_pDialogWindow ) + { + if (DlgEdObj* pDlgEdObj = m_aAccessibleChildren[nChildIndex].pDlgEdObj) + { + SdrView& rView = m_pDialogWindow->GetView(); + if (SdrPageView* pPgView = rView.GetSdrPageView()) + rView.MarkObj(pDlgEdObj, pPgView); + } + } +} + + +sal_Bool AccessibleDialogWindow::isAccessibleChildSelected( sal_Int64 nChildIndex ) +{ + OExternalLockGuard aGuard( this ); + + if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + if (m_pDialogWindow) + if (DlgEdObj* pDlgEdObj = m_aAccessibleChildren[nChildIndex].pDlgEdObj) + return m_pDialogWindow->GetView().IsObjMarked(pDlgEdObj); + return false; +} + + +void AccessibleDialogWindow::clearAccessibleSelection() +{ + OExternalLockGuard aGuard( this ); + + if ( m_pDialogWindow ) + m_pDialogWindow->GetView().UnmarkAll(); +} + + +void AccessibleDialogWindow::selectAllAccessibleChildren( ) +{ + OExternalLockGuard aGuard( this ); + + if ( m_pDialogWindow ) + m_pDialogWindow->GetView().MarkAll(); +} + + +sal_Int64 AccessibleDialogWindow::getSelectedAccessibleChildCount( ) +{ + OExternalLockGuard aGuard( this ); + + sal_Int64 nRet = 0; + + for ( sal_Int64 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + if ( isAccessibleChildSelected( i ) ) + ++nRet; + } + + return nRet; +} + + +Reference< XAccessible > AccessibleDialogWindow::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) +{ + OExternalLockGuard aGuard( this ); + + if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild; + + for ( sal_Int64 i = 0, j = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + if ( isAccessibleChildSelected( i ) && ( j++ == nSelectedChildIndex ) ) + { + xChild = getAccessibleChild( i ); + break; + } + } + + return xChild; +} + + +void AccessibleDialogWindow::deselectAccessibleChild( sal_Int64 nChildIndex ) +{ + OExternalLockGuard aGuard( this ); + + if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + if ( m_pDialogWindow ) + { + if (DlgEdObj* pDlgEdObj = m_aAccessibleChildren[nChildIndex].pDlgEdObj) + { + SdrView& rView = m_pDialogWindow->GetView(); + SdrPageView* pPgView = rView.GetSdrPageView(); + if (pPgView) + rView.MarkObj( pDlgEdObj, pPgView, true ); + } + } +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/IDEComboBox.cxx b/basctl/source/basicide/IDEComboBox.cxx new file mode 100644 index 0000000000..d25e143d62 --- /dev/null +++ b/basctl/source/basicide/IDEComboBox.cxx @@ -0,0 +1,530 @@ +/* -*- 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 <strings.hrc> +#include <basidesh.hxx> +#include <basobj.hxx> +#include <IDEComboBox.hxx> +#include <iderdll.hxx> +#include <iderid.hxx> +#include <localizationmgr.hxx> +#include <managelang.hxx> + +#include <sfx2/dispatch.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/sfxsids.hrc> +#include <svtools/langtab.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/event.hxx> +#include <svl/itemset.hxx> + +namespace basctl +{ +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; + +/*! Macro for implementation two methods for LibBoxControl Class + * + * @code + * SfxToolBoxControl* LibBoxControl::CreateImpl(sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx) + * { + * return new LibBoxControl(nSlotId, nId, rTbx); + * } + * + * void LibBoxControl::RegisterControl(sal_uInt16 nSlotId, SfxModule* pMod) + * { + * SfxToolBoxControl::RegisterToolBoxControl( + * pMod, SfxTbxCtrlFactory(* LibBoxControl::CreateImpl, typeid(nItemClass), nSlotId)); + * } + * @endcode + * @see Macro SFX_DECL_TOOLBOX_CONTROL + */ +SFX_IMPL_TOOLBOX_CONTROL(LibBoxControl, SfxStringItem); + +LibBoxControl::LibBoxControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx) + : SfxToolBoxControl(nSlotId, nId, rTbx) +{ +} + +void LibBoxControl::StateChangedAtToolBoxControl(sal_uInt16, SfxItemState eState, + const SfxPoolItem* pState) +{ + LibBox* pBox = static_cast<LibBox*>(GetToolBox().GetItemWindow(GetId())); + + DBG_ASSERT(pBox, "Box not found"); + if (!pBox) + return; + + if (eState != SfxItemState::DEFAULT) + pBox->set_sensitive(false); + else + { + pBox->set_sensitive(true); + pBox->Update(dynamic_cast<SfxStringItem const*>(pState)); + } +} + +VclPtr<InterimItemWindow> LibBoxControl::CreateItemWindow(vcl::Window* pParent) +{ + return VclPtr<LibBox>::Create(pParent); +} + +DocListenerBox::DocListenerBox(vcl::Window* pParent) + : InterimItemWindow(pParent, "modules/BasicIDE/ui/combobox.ui", "ComboBox") + , m_xWidget(m_xBuilder->weld_combo_box("combobox")) + , maNotifier(*this) +{ + InitControlBase(m_xWidget.get()); + + m_xWidget->connect_changed(LINK(this, DocListenerBox, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, DocListenerBox, KeyInputHdl)); +} + +void DocListenerBox::set_sensitive(bool bSensitive) +{ + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); +} + +IMPL_LINK(DocListenerBox, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return HandleKeyInput(rKEvt); +} + +bool DocListenerBox::HandleKeyInput(const KeyEvent& rKEvt) { return ChildKeyInput(rKEvt); } + +IMPL_LINK_NOARG(DocListenerBox, SelectHdl, weld::ComboBox&, void) { Select(); } + +DocListenerBox::~DocListenerBox() { disposeOnce(); } + +void DocListenerBox::dispose() +{ + maNotifier.dispose(); + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +/// Only calls FillBox(). Parameter is not used. +void DocListenerBox::onDocumentCreated(const ScriptDocument& /*_rDoc*/) { FillBox(); } + +/// Only calls FillBox(). Parameter is not used. +void DocListenerBox::onDocumentOpened(const ScriptDocument& /*_rDoc*/) { FillBox(); } + +/// Only calls FillBox(). Parameter is not used. +void DocListenerBox::onDocumentSaveAsDone(const ScriptDocument& /*_rDoc*/) { FillBox(); } + +/// Only calls FillBox(). Parameter is not used. +void DocListenerBox::onDocumentClosed(const ScriptDocument& /*_rDoc*/) { FillBox(); } + +/// Not interested in. Do nothing. +void DocListenerBox::onDocumentSave(const ScriptDocument& /*_rDoc*/) {} + +/// Not interested in. Do nothing. +void DocListenerBox::onDocumentSaveDone(const ScriptDocument& /*_rDoc*/) {} + +/// Not interested in. Do nothing. +void DocListenerBox::onDocumentSaveAs(const ScriptDocument& /*_rDoc*/) {} + +/// Not interested in. Do nothing. +void DocListenerBox::onDocumentTitleChanged(const ScriptDocument& /*_rDoc*/) {} + +/// Not interested in. Do nothing. +void DocListenerBox::onDocumentModeChanged(const ScriptDocument& /*_rDoc*/) {} + +LibBox::LibBox(vcl::Window* pParent) + : DocListenerBox(pParent) +{ + FillBox(); + mbIgnoreSelect = true; // do not yet transfer select of 0 + mbFillBox = true; + m_xWidget->set_active(0); + maCurrentText = m_xWidget->get_text(0); + mbIgnoreSelect = false; + + m_xWidget->connect_focus_in(LINK(this, LibBox, FocusInHdl)); + m_xWidget->connect_focus_out(LINK(this, LibBox, FocusOutHdl)); + + SetSizePixel(m_xWidget->get_preferred_size()); +} + +LibBox::~LibBox() { disposeOnce(); } + +void LibBox::dispose() +{ + ClearBox(); + DocListenerBox::dispose(); +} + +void LibBox::Update(const SfxStringItem* pItem) +{ + // if ( !pItem || !pItem->GetValue().Len() ) + FillBox(); + + if (pItem) + { + maCurrentText = pItem->GetValue(); + if (maCurrentText.isEmpty()) + maCurrentText = IDEResId(RID_STR_ALL); + } + + if (m_xWidget->get_active_text() != maCurrentText) + m_xWidget->set_active_text(maCurrentText); +} + +void LibBox::ReleaseFocus() +{ + SfxViewShell* pCurSh = SfxViewShell::Current(); + DBG_ASSERT(pCurSh, "Current ViewShell not found!"); + + if (!pCurSh) + return; + + vcl::Window* pShellWin = pCurSh->GetWindow(); + if (pShellWin) + { + pShellWin->GrabFocus(); + return; + } + + weld::Window* pWin = Application::GetDefDialogParent(); + if (!pWin) + return; + pWin->grab_focus(); +} + +void LibBox::FillBox() +{ + m_xWidget->freeze(); + mbIgnoreSelect = true; + + maCurrentText = m_xWidget->get_active_text(); + + ClearBox(); + + // create list box entries + LibEntry* pEntry = new LibEntry(ScriptDocument::getApplicationScriptDocument(), + LIBRARY_LOCATION_UNKNOWN, OUString()); + OUString sId(weld::toId(pEntry)); + m_xWidget->append(sId, IDEResId(RID_STR_ALL)); + + InsertEntries(ScriptDocument::getApplicationScriptDocument(), LIBRARY_LOCATION_USER); + InsertEntries(ScriptDocument::getApplicationScriptDocument(), LIBRARY_LOCATION_SHARE); + + ScriptDocuments aDocuments( + ScriptDocument::getAllScriptDocuments(ScriptDocument::DocumentsSorted)); + for (auto const& doc : aDocuments) + { + InsertEntries(doc, LIBRARY_LOCATION_DOCUMENT); + } + + m_xWidget->thaw(); + + int nIndex = m_xWidget->find_text(maCurrentText); + if (nIndex != -1) + m_xWidget->set_active(nIndex); + else + m_xWidget->set_active(0); + maCurrentText = m_xWidget->get_active_text(); + mbIgnoreSelect = false; +} + +void LibBox::InsertEntries(const ScriptDocument& rDocument, LibraryLocation eLocation) +{ + // get a sorted list of library names + Sequence<OUString> aLibNames = rDocument.getLibraryNames(); + sal_Int32 nLibCount = aLibNames.getLength(); + const OUString* pLibNames = aLibNames.getConstArray(); + + for (sal_Int32 i = 0; i < nLibCount; ++i) + { + OUString aLibName = pLibNames[i]; + if (eLocation == rDocument.getLibraryLocation(aLibName)) + { + OUString aName(rDocument.getTitle(eLocation)); + OUString aEntryText(CreateMgrAndLibStr(aName, aLibName)); + LibEntry* pEntry = new LibEntry(rDocument, eLocation, aLibName); + m_xWidget->append(weld::toId(pEntry), aEntryText); + } + } +} + +bool LibBox::HandleKeyInput(const KeyEvent& rKEvt) +{ + bool bDone = false; + + sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch (nKeyCode) + { + case KEY_RETURN: + { + NotifyIDE(); + bDone = true; + } + break; + case KEY_ESCAPE: + { + m_xWidget->set_active_text(maCurrentText); + ReleaseFocus(); + bDone = true; + } + break; + } + + return bDone || DocListenerBox::HandleKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(LibBox, FocusInHdl, weld::Widget&, void) +{ + if (mbFillBox) + { + FillBox(); + mbFillBox = false; + } +} + +IMPL_LINK_NOARG(LibBox, FocusOutHdl, weld::Widget&, void) +{ + // comboboxes can be comprised of multiple widgets, ensure all have lost focus + if (m_xWidget && !m_xWidget->has_focus()) + mbFillBox = true; +} + +void LibBox::Select() +{ + if (m_xWidget->changed_by_direct_pick()) + { + if (!mbIgnoreSelect) + NotifyIDE(); + else + m_xWidget->set_active_text(maCurrentText); // (Select after Escape) + } +} + +void LibBox::NotifyIDE() +{ + LibEntry* pEntry = weld::fromId<LibEntry*>(m_xWidget->get_active_id()); + if (pEntry) + { + const ScriptDocument& aDocument(pEntry->GetDocument()); + SfxUnoAnyItem aDocumentItem(SID_BASICIDE_ARG_DOCUMENT_MODEL, + uno::Any(aDocument.getDocumentOrNull())); + const OUString& aLibName = pEntry->GetLibName(); + SfxStringItem aLibNameItem(SID_BASICIDE_ARG_LIBNAME, aLibName); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->ExecuteList(SID_BASICIDE_LIBSELECTED, SfxCallMode::SYNCHRON, + { &aDocumentItem, &aLibNameItem }); + } + ReleaseFocus(); +} + +void LibBox::ClearBox() +{ + sal_Int32 nCount = m_xWidget->get_count(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + LibEntry* pEntry = weld::fromId<LibEntry*>(m_xWidget->get_id(i)); + delete pEntry; + } + m_xWidget->clear(); +} + +// class LanguageBoxControl ---------------------------------------------- + +/*! Macro for implementation two methods for LanguageBoxControl Class + * + * @code + * SfxToolBoxControl* LanguageBoxControl::CreateImpl(sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx) + * { + * return new LanguageBoxControl(nSlotId, nId, rTbx); + * } + * + * void LanguageBoxControl::RegisterControl(sal_uInt16 nSlotId, SfxModule* pMod) + * { + * SfxToolBoxControl::RegisterToolBoxControl( + * pMod, SfxTbxCtrlFactory(* LanguageBoxControl::CreateImpl, typeid(nItemClass), nSlotId)); + * } + * @endcode + * @see Macro SFX_DECL_TOOLBOX_CONTROL + */ +SFX_IMPL_TOOLBOX_CONTROL(LanguageBoxControl, SfxStringItem); + +LanguageBoxControl::LanguageBoxControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx) + : SfxToolBoxControl(nSlotId, nId, rTbx) +{ +} + +void LanguageBoxControl::StateChangedAtToolBoxControl(sal_uInt16, SfxItemState eState, + const SfxPoolItem* pItem) +{ + if (LanguageBox* pBox = static_cast<LanguageBox*>(GetToolBox().GetItemWindow(GetId()))) + { + if (eState != SfxItemState::DEFAULT) + pBox->set_sensitive(false); + else + { + pBox->set_sensitive(true); + pBox->Update(dynamic_cast<SfxStringItem const*>(pItem)); + } + } +} + +VclPtr<InterimItemWindow> LanguageBoxControl::CreateItemWindow(vcl::Window* pParent) +{ + return VclPtr<LanguageBox>::Create(pParent); +} + +// class basctl::LanguageBox ----------------------------------------------- +LanguageBox::LanguageBox(vcl::Window* pParent) + : DocListenerBox(pParent) + , msNotLocalizedStr(IDEResId(RID_STR_TRANSLATION_NOTLOCALIZED)) + , msDefaultLanguageStr(IDEResId(RID_STR_TRANSLATION_DEFAULT)) + , mbIgnoreSelect(false) +{ + FillBox(); + + SetSizePixel(m_xWidget->get_preferred_size()); +} + +LanguageBox::~LanguageBox() { disposeOnce(); } + +void LanguageBox::dispose() +{ + ClearBox(); + DocListenerBox::dispose(); +} + +void LanguageBox::FillBox() +{ + m_xWidget->freeze(); + mbIgnoreSelect = true; + msCurrentText = m_xWidget->get_active_text(); + ClearBox(); + + sal_Int32 nSelPos = -1; + + std::shared_ptr<LocalizationMgr> pCurMgr(GetShell()->GetCurLocalizationMgr()); + if (pCurMgr->isLibraryLocalized()) + { + set_sensitive(true); + Locale aDefaultLocale = pCurMgr->getStringResourceManager()->getDefaultLocale(); + Locale aCurrentLocale = pCurMgr->getStringResourceManager()->getCurrentLocale(); + Sequence<Locale> aLocaleSeq = pCurMgr->getStringResourceManager()->getLocales(); + const Locale* pLocale = aLocaleSeq.getConstArray(); + sal_Int32 i, nCount = aLocaleSeq.getLength(); + for (i = 0; i < nCount; ++i) + { + bool bIsDefault = localesAreEqual(aDefaultLocale, pLocale[i]); + bool bIsCurrent = localesAreEqual(aCurrentLocale, pLocale[i]); + LanguageType eLangType = LanguageTag::convertToLanguageType(pLocale[i]); + OUString sLanguage = SvtLanguageTable::GetLanguageString(eLangType); + if (bIsDefault) + { + sLanguage += " " + msDefaultLanguageStr; + } + LanguageEntry* pEntry = new LanguageEntry(pLocale[i], bIsDefault); + OUString sId(weld::toId(pEntry)); + m_xWidget->append(sId, sLanguage); + + if (bIsCurrent) + nSelPos = i; + } + + if (nSelPos != -1) + msCurrentText = m_xWidget->get_text(nSelPos); + } + else + { + m_xWidget->append_text(msNotLocalizedStr); + nSelPos = 0; + set_sensitive(false); + } + + m_xWidget->thaw(); + m_xWidget->set_active(nSelPos); + mbIgnoreSelect = false; +} + +void LanguageBox::ClearBox() +{ + sal_Int32 nCount = m_xWidget->get_count(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + LanguageEntry* pEntry = weld::fromId<LanguageEntry*>(m_xWidget->get_id(i)); + delete pEntry; + } + m_xWidget->clear(); +} + +void LanguageBox::SetLanguage() +{ + LanguageEntry* pEntry = weld::fromId<LanguageEntry*>(m_xWidget->get_active_id()); + if (pEntry) + GetShell()->GetCurLocalizationMgr()->handleSetCurrentLocale(pEntry->m_aLocale); +} + +void LanguageBox::Select() +{ + if (!mbIgnoreSelect) + SetLanguage(); + else + m_xWidget->set_active_text(msCurrentText); // Select after Escape +} + +bool LanguageBox::HandleKeyInput(const KeyEvent& rKEvt) +{ + bool bDone = false; + + sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch (nKeyCode) + { + case KEY_RETURN: + { + SetLanguage(); + bDone = true; + } + break; + case KEY_ESCAPE: + { + m_xWidget->set_active_text(msCurrentText); + bDone = true; + } + break; + } + + return bDone || DocListenerBox::HandleKeyInput(rKEvt); +} + +void LanguageBox::Update(const SfxStringItem* pItem) +{ + FillBox(); + + if (pItem && !pItem->GetValue().isEmpty()) + { + msCurrentText = pItem->GetValue(); + if (m_xWidget->get_active_text() != msCurrentText) + m_xWidget->set_active_text(msCurrentText); + } +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/ObjectCatalog.cxx b/basctl/source/basicide/ObjectCatalog.cxx new file mode 100644 index 0000000000..13069ed466 --- /dev/null +++ b/basctl/source/basicide/ObjectCatalog.cxx @@ -0,0 +1,85 @@ +/* -*- 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 <strings.hrc> +#include <iderid.hxx> + +#include <ObjectCatalog.hxx> +#include <helpids.h> + +#include <vcl/taskpanelist.hxx> + +namespace basctl +{ +ObjectCatalog::ObjectCatalog(vcl::Window* pParent) + : DockingWindow(pParent, "modules/BasicIDE/ui/dockingorganizer.ui", "DockingOrganizer") +{ + m_xTitle = m_xBuilder->weld_label("title"); + m_xTree.reset(new SbTreeListBox(m_xBuilder->weld_tree_view("libraries"), GetFrameWeld())); + + SetHelpId("basctl:FloatingWindow:RID_BASICIDE_OBJCAT"); + SetText(IDEResId(RID_BASICIDE_OBJCAT)); + + // title + m_xTitle->set_label(IDEResId(RID_BASICIDE_OBJCAT)); + + // tree list + weld::TreeView& rWidget = m_xTree->get_widget(); + + rWidget.set_help_id(HID_BASICIDE_OBJECTCAT); + m_xTree->ScanAllEntries(); + rWidget.grab_focus(); + + // make object catalog keyboard accessible + GetParent()->GetSystemWindow()->GetTaskPaneList()->AddWindow(this); +} + +ObjectCatalog::~ObjectCatalog() { disposeOnce(); } + +void ObjectCatalog::dispose() +{ + GetParent()->GetSystemWindow()->GetTaskPaneList()->RemoveWindow(this); + m_xTitle.reset(); + m_xTree.reset(); + DockingWindow::dispose(); +} + +// ToggleFloatingMode() -- called by DockingWindow when IsFloatingMode() changes +void ObjectCatalog::ToggleFloatingMode() +{ + // base class version + DockingWindow::ToggleFloatingMode(); + + bool const bFloating = IsFloatingMode(); + // tdf#152154: m_xTitle will be null during disposing + if (m_xTitle) + m_xTitle->set_visible(!bFloating); +} + +void ObjectCatalog::SetCurrentEntry(BaseWindow* pCurWin) +{ + EntryDescriptor aDescriptor; + if (pCurWin) + aDescriptor = pCurWin->CreateEntryDescriptor(); + m_xTree->SetCurrentEntry(aDescriptor); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basdoc.cxx b/basctl/source/basicide/basdoc.cxx new file mode 100644 index 0000000000..ffd22b9063 --- /dev/null +++ b/basctl/source/basicide/basdoc.cxx @@ -0,0 +1,90 @@ +/* -*- 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 <sal/config.h> + +#include <sfx2/app.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/objface.hxx> + +#include "unomodel.hxx" + +#include "basdoc.hxx" +#define ShellClass_basctl_DocShell +#include <basslots.hxx> +#include <sfx2/sfxmodelfactory.hxx> +#include <svl/itemset.hxx> +#include <svx/svxids.hrc> +#include <tools/globname.hxx> +#include <tools/debug.hxx> + +namespace basctl +{ + + +SFX_IMPL_OBJECTFACTORY( DocShell, SvGlobalName(), "sbasic" ) + +SFX_IMPL_SUPERCLASS_INTERFACE(basctl_DocShell, SfxObjectShell) + +void basctl_DocShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterStatusBar(StatusBarId::BasicIdeStatusBar); +} + +DocShell::DocShell() + :SfxObjectShell( SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | SfxModelFlags::DISABLE_DOCUMENT_RECOVERY ) +{ + SetPool( &SfxGetpApp()->GetPool() ); + SetBaseModel( new SIDEModel(this) ); +} + +DocShell::~DocShell() +{ + pPrinter.disposeAndClear(); +} + +SfxPrinter* DocShell::GetPrinter( bool bCreate ) +{ + if ( !pPrinter && bCreate ) + pPrinter.disposeAndReset(VclPtr<SfxPrinter>::Create(std::make_unique<SfxItemSetFixed + <SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN>>(GetPool()) + )); + + return pPrinter.get(); +} + +void DocShell::SetPrinter( SfxPrinter* pPr ) +{ + if (pPr != pPrinter.get()) + { + pPrinter.disposeAndReset(pPr); + } +} + +void DocShell::FillClass( SvGlobalName*, SotClipboardFormatId*, OUString*, sal_Int32, bool bTemplate) const +{ + DBG_ASSERT( !bTemplate, "No template for Basic" ); +} + +void DocShell::Draw( OutputDevice *, const JobSetup &, sal_uInt16, bool ) +{} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basdoc.hxx b/basctl/source/basicide/basdoc.hxx new file mode 100644 index 0000000000..bb847a0a9b --- /dev/null +++ b/basctl/source/basicide/basdoc.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/docfac.hxx> +#include <svx/ifaceids.hxx> +#include <vcl/vclptr.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/objsh.hxx> + +namespace basctl +{ + +class DocShell: public SfxObjectShell +{ + ScopedVclPtr<SfxPrinter> pPrinter; + +protected: + virtual void Draw( OutputDevice *, const JobSetup & rSetup, + sal_uInt16 nAspect, bool bOutputForScreen ) override; + virtual void FillClass( SvGlobalName * pClassName, + SotClipboardFormatId * pFormat, + OUString * pFullTypeName, + sal_Int32 nVersion, + bool bTemplate = false ) const override; + +public: + + SFX_DECL_OBJECTFACTORY(); + SFX_DECL_INTERFACE( SVX_INTERFACE_BASIDE_DOCSH ) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + DocShell(); + virtual ~DocShell() override; + + SfxPrinter* GetPrinter( bool bCreate ); + void SetPrinter( SfxPrinter* pPrinter ); +}; + +} // namespace basctl + +// This typedef helps baside.sdi, +// because I don't know how to use nested names in it. +typedef basctl::DocShell basctl_DocShell; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basicmod.hxx b/basctl/source/basicide/basicmod.hxx new file mode 100644 index 0000000000..7a0175183d --- /dev/null +++ b/basctl/source/basicide/basicmod.hxx @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sfx2/module.hxx> + +namespace basctl +{ +class Module : public SfxModule +{ +public: + Module(const OString& rResName, SfxObjectFactory* pObjFact) + : SfxModule(rResName, { pObjFact }) + { + } +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basicrenderable.cxx b/basctl/source/basicide/basicrenderable.cxx new file mode 100644 index 0000000000..648f234cbd --- /dev/null +++ b/basctl/source/basicide/basicrenderable.cxx @@ -0,0 +1,221 @@ +/* -*- 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 "basicrenderable.hxx" +#include <bastypes.hxx> +#include <iderid.hxx> +#include <strings.hrc> + +#include <toolkit/awt/vclxdevice.hxx> +#include <tools/multisel.hxx> +#include <cppuhelper/compbase.hxx> +#include <comphelper/propertysequence.hxx> + +namespace basctl +{ + +using namespace com::sun::star; +using namespace com::sun::star::uno; + +Renderable::Renderable (BaseWindow* pWin) +: cppu::WeakComponentImplHelper< css::view::XRenderable >( maMutex ) +, mpWindow( pWin ) +{ + m_aUIProperties.resize( 4 ); + + // show Subgroup for print range + vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt; + aPrintRangeOpt.maGroupHint = "PrintRange" ; + aPrintRangeOpt.mbInternalOnly = true; + + m_aUIProperties[0].Value = setSubgroupControlOpt("printrange", + IDEResId( RID_STR_PRINTDLG_PAGES ), OUString(), aPrintRangeOpt); + + // create a choice for the range to print + OUString aPrintContentName( "PrintContent" ); + const Sequence<OUString> aChoices{IDEResId(RID_STR_PRINTDLG_PRINTALLPAGES), + IDEResId(RID_STR_PRINTDLG_PRINTPAGES)}; + const Sequence<OUString> aHelpIds{".HelpID:vcl:PrintDialog:PrintContent:RadioButton:0", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:1"}; + const Sequence<OUString> aWidgetIds{"rbAllPages", + "rbRangePages"}; + m_aUIProperties[1].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(), + aHelpIds, aPrintContentName, + aChoices, 0); + + // create an Edit dependent on "Pages" selected + vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt(aPrintContentName, 1, true); + m_aUIProperties[2].Value = setEditControlOpt("pagerange", OUString(), + OUString(), "PageRange", + OUString(), aPageRangeOpt); + + vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintContentName, 0, true); + m_aUIProperties[3].Value = setChoiceListControlOpt("evenoddbox", + OUString(), + uno::Sequence<OUString>(), + "EvenOdd", + uno::Sequence<OUString>(), + 0, + uno::Sequence< sal_Bool >(), + aEvenOddOpt); +} + +Renderable::~Renderable() +{ +} + +VclPtr< Printer > Renderable::getPrinter() const +{ + VclPtr< Printer > pPrinter; + Any aValue( getValue( "RenderDevice" ) ); + Reference<awt::XDevice> xRenderDevice; + + if( aValue >>= xRenderDevice ) + { + VCLXDevice* pDevice = dynamic_cast<VCLXDevice*>(xRenderDevice.get()); + VclPtr< OutputDevice > pOut = pDevice ? pDevice->GetOutputDevice() : VclPtr< OutputDevice >(); + pPrinter = dynamic_cast<Printer*>(pOut.get()); + } + return pPrinter; +} + +bool Renderable::isPrintOddPages() const +{ + sal_Int64 nContent = getIntValue( "PrintContent", -1 ); + return nContent != 2; +} + +bool Renderable::isPrintEvenPages() const +{ + sal_Int64 nContent = getIntValue( "PrintContent", -1 ); + return nContent != 3; +} + +sal_Int32 SAL_CALL Renderable::getRendererCount ( + const Any&, const Sequence<beans::PropertyValue >& i_xOptions + ) +{ + processProperties( i_xOptions ); + + maValidPages.clear(); + + sal_Int32 nCount = 0; + if( mpWindow ) + { + VclPtr<Printer> pPrinter = getPrinter(); + if (!pPrinter) + throw lang::IllegalArgumentException("no printer", static_cast<cppu::OWeakObject*>(this), -1); + + nCount = mpWindow->countPages( pPrinter ); + + for (sal_Int32 nPage = 1; nPage <= nCount; nPage++) + { + if ( (isPrintEvenPages() && isOnEvenPage( nPage )) + || (isPrintOddPages() && !isOnEvenPage( nPage )) ) + { + maValidPages.push_back( nPage-1 ); + } + } + + sal_Int64 nContent = getIntValue( "PrintContent", -1 ); + sal_Int64 nEOContent = getIntValue ("EvenOdd", -1); + if( nContent == 1 ) + { + OUString aPageRange( getStringValue( "PageRange" ) ); + if( !aPageRange.isEmpty() ) + { + StringRangeEnumerator aRangeEnum( aPageRange, 0, nCount-1 ); + sal_Int32 nSelCount = aRangeEnum.size(); + if( nSelCount >= 0 ) + nCount = nSelCount; + } + } + else if ( nEOContent == 1 || nEOContent == 2 ) // even/odd pages + return static_cast<sal_Int32>( maValidPages.size() ); + } + + return nCount; +} + +Sequence<beans::PropertyValue> SAL_CALL Renderable::getRenderer ( + sal_Int32, const Any&, const Sequence<beans::PropertyValue>& i_xOptions + ) +{ + processProperties( i_xOptions ); + + Sequence< beans::PropertyValue > aVals; + // insert page size here + VclPtr<Printer> pPrinter = getPrinter(); + // no renderdevice is legal; the first call is to get our print ui options + if( pPrinter ) + { + Size aPageSize( pPrinter->PixelToLogic( pPrinter->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ) ); + + awt::Size aSize; + aSize.Width = aPageSize.Width(); + aSize.Height = aPageSize.Height(); + aVals = ::comphelper::InitPropertySequence({ + { "PageSize", Any(aSize) } + }); + } + + appendPrintUIOptions( aVals ); + + return aVals; +} + +void SAL_CALL Renderable::render ( + sal_Int32 nRenderer, const Any&, + const Sequence<beans::PropertyValue>& i_xOptions + ) +{ + processProperties( i_xOptions ); + + if( !mpWindow ) + return; + + VclPtr<Printer> pPrinter = getPrinter(); + if (!pPrinter) + throw lang::IllegalArgumentException("no printer", static_cast<cppu::OWeakObject*>(this), -1); + + sal_Int64 nContent = getIntValue( "PrintContent", -1 ); + if( nContent == 1 ) + { + OUString aPageRange( getStringValue( "PageRange" ) ); + if( !aPageRange.isEmpty() ) + { + sal_Int32 nPageCount = mpWindow->countPages( pPrinter ); + StringRangeEnumerator aRangeEnum( aPageRange, 0, nPageCount-1 ); + StringRangeEnumerator::Iterator it = aRangeEnum.begin(); + for( ; it != aRangeEnum.end() && nRenderer; --nRenderer ) + ++it; + + sal_Int32 nPage = ( it != aRangeEnum.end() ) ? *it : nRenderer; + mpWindow->printPage( nPage, pPrinter ); + } + else + mpWindow->printPage( nRenderer, pPrinter ); + } + else + mpWindow->printPage( maValidPages.at( nRenderer ), pPrinter ); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basicrenderable.hxx b/basctl/source/basicide/basicrenderable.hxx new file mode 100644 index 0000000000..638749c649 --- /dev/null +++ b/basctl/source/basicide/basicrenderable.hxx @@ -0,0 +1,66 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/view/XRenderable.hpp> +#include <cppuhelper/compbase.hxx> + +#include <vcl/print.hxx> + +namespace basctl +{ + +class BaseWindow; + +class Renderable : + public cppu::WeakComponentImplHelper< css::view::XRenderable >, + public vcl::PrinterOptionsHelper +{ + VclPtr<BaseWindow> mpWindow; + osl::Mutex maMutex; + std::vector<sal_Int32> maValidPages; + + VclPtr<Printer> getPrinter() const; + bool isPrintOddPages() const; + bool isPrintEvenPages() const; + static bool isOnEvenPage( sal_Int32 nPage ) { return nPage % 2 == 0; }; +public: + explicit Renderable (BaseWindow*); + virtual ~Renderable() override; + + // XRenderable + virtual sal_Int32 SAL_CALL getRendererCount ( + const css::uno::Any& aSelection, + const css::uno::Sequence<css::beans::PropertyValue >& xOptions) override; + + virtual css::uno::Sequence<css::beans::PropertyValue> SAL_CALL getRenderer ( + sal_Int32 nRenderer, + const css::uno::Any& rSelection, + const css::uno::Sequence<css::beans::PropertyValue>& rxOptions) override; + + virtual void SAL_CALL render ( + sal_Int32 nRenderer, + const css::uno::Any& rSelection, + const css::uno::Sequence<css::beans::PropertyValue>& rxOptions) override; + +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/baside2.cxx b/basctl/source/basicide/baside2.cxx new file mode 100644 index 0000000000..b01e5c238a --- /dev/null +++ b/basctl/source/basicide/baside2.cxx @@ -0,0 +1,1606 @@ +/* -*- 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 "baside2.hxx" +#include <baside3.hxx> +#include <basobj.hxx> +#include <basidesh.hxx> +#include "brkdlg.hxx" +#include <iderdll.hxx> +#include <iderid.hxx> +#include "moduldlg.hxx" +#include <docsignature.hxx> +#include <officecfg/Office/BasicIDE.hxx> + +#include <helpids.h> +#include <strings.hrc> + +#include <basic/basmgr.hxx> +#include <basic/basrdll.hxx> +#include <basic/sbmeth.hxx> +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <comphelper/SetFlagContextHelper.hxx> +#include <comphelper/string.hxx> +#include <svl/srchdefs.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sot/exchange.hxx> +#include <svl/eitem.hxx> +#include <svl/srchitem.hxx> +#include <svl/stritem.hxx> +#include <svl/visitem.hxx> +#include <svl/whiter.hxx> +#include <svx/svxids.hrc> +#include <tools/debug.hxx> +#include <utility> +#include <vcl/locktoplevels.hxx> +#include <vcl/errinf.hxx> +#include <vcl/event.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <vcl/textview.hxx> +#include <vcl/weld.hxx> +#include <vcl/xtextedt.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <cassert> +#include <osl/diagnose.h> +#include <officecfg/Office/Common.hxx> + +namespace basctl +{ + +namespace +{ + +namespace Print +{ + tools::Long const nLeftMargin = 1700; + tools::Long const nRightMargin = 900; + tools::Long const nTopMargin = 2000; + tools::Long const nBottomMargin = 1000; + tools::Long const nBorder = 300; +} + +short const ValidWindow = 0x1234; + +// What (who) are OW and MTF? Compare to baside3.cxx where an +// identically named variable, used in the same way, has the value +// "*.*" on Windows, "*" otherwise. Is that what should be done here, +// too? + +#if defined(OW) || defined(MTF) +char const FilterMask_All[] = "*"; +#else +constexpr OUString FilterMask_All = u"*.*"_ustr; +#endif + +} // end anonymous namespace + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ui::dialogs; +using namespace utl; +using namespace comphelper; + +namespace +{ + +void lcl_PrintHeader( Printer* pPrinter, sal_uInt16 nPages, sal_uInt16 nCurPage, const OUString& rTitle, bool bOutput ) +{ + Size const aSz = pPrinter->GetOutputSize(); + + const Color aOldLineColor( pPrinter->GetLineColor() ); + const Color aOldFillColor( pPrinter->GetFillColor() ); + const vcl::Font aOldFont( pPrinter->GetFont() ); + + pPrinter->SetLineColor( COL_BLACK ); + pPrinter->SetFillColor(); + + vcl::Font aFont( aOldFont ); + aFont.SetWeight( WEIGHT_BOLD ); + aFont.SetAlignment( ALIGN_BOTTOM ); + pPrinter->SetFont( aFont ); + + tools::Long nFontHeight = pPrinter->GetTextHeight(); + + // 1st Border => line, 2+3 Border = free space + tools::Long nYTop = Print::nTopMargin - 3*Print::nBorder - nFontHeight; + + tools::Long nXLeft = Print::nLeftMargin - Print::nBorder; + tools::Long nXRight = aSz.Width() - Print::nRightMargin + Print::nBorder; + + if( bOutput ) + pPrinter->DrawRect(tools::Rectangle( + Point(nXLeft, nYTop), + Size(nXRight - nXLeft, aSz.Height() - nYTop - Print::nBottomMargin + Print::nBorder) + )); + + + tools::Long nY = Print::nTopMargin - 2*Print::nBorder; + Point aPos(Print::nLeftMargin, nY); + if( bOutput ) + pPrinter->DrawText( aPos, rTitle ); + if ( nPages != 1 ) + { + aFont.SetWeight( WEIGHT_NORMAL ); + pPrinter->SetFont( aFont ); + aPos.AdjustX(pPrinter->GetTextWidth( rTitle ) ); + + if( bOutput ) + { + OUString aPageStr = " [" + IDEResId(RID_STR_PAGE) + " " + OUString::number( nCurPage ) + "]"; + pPrinter->DrawText( aPos, aPageStr ); + } + } + + nY = Print::nTopMargin - Print::nBorder; + + if( bOutput ) + pPrinter->DrawLine( Point( nXLeft, nY ), Point( nXRight, nY ) ); + + pPrinter->SetFont( aOldFont ); + pPrinter->SetFillColor( aOldFillColor ); + pPrinter->SetLineColor( aOldLineColor ); +} + +void lcl_ConvertTabsToSpaces( OUString& rLine ) +{ + if ( rLine.isEmpty() ) + return; + + OUStringBuffer aResult( rLine ); + sal_Int32 nPos = 0; + sal_Int32 nMax = aResult.getLength(); + while ( nPos < nMax ) + { + if ( aResult[nPos] == '\t' ) + { + // not 4 Blanks, but at 4 TabPos: + OUStringBuffer aBlanker; + string::padToLength(aBlanker, ( 4 - ( nPos % 4 ) ), ' '); + aResult.remove( nPos, 1 ); + aResult.insert( nPos, aBlanker ); + nMax = aResult.getLength(); + } + ++nPos; + } + rLine = aResult.makeStringAndClear(); +} + +} // namespace + +ModulWindow::ModulWindow (ModulWindowLayout* pParent, ScriptDocument const& rDocument, + const OUString& aLibName, const OUString& aName, OUString aModule) + : BaseWindow(pParent, rDocument, aLibName, aName) + , m_rLayout(*pParent) + , m_nValid(ValidWindow) + , m_aXEditorWindow(VclPtr<ComplexEditorWindow>::Create(this)) + , m_aModule(std::move(aModule)) +{ + m_aXEditorWindow->Show(); + SetBackground(); +} + +SbModuleRef const & ModulWindow::XModule() +{ + // ModuleWindows can now be created as a result of the + // modules getting created via the api. This is a result of an + // elementInserted event from the BasicLibrary container. + // However the SbModule is also created from a different listener to + // the same event ( in basmgr ) Therefore it is possible when we look + // for m_xModule it may not yet be available, here we keep trying to access + // the module until such time as it exists + + if ( !m_xModule.is() ) + { + BasicManager* pBasMgr = GetDocument().getBasicManager(); + if ( pBasMgr ) + { + StarBASIC* pBasic = pBasMgr->GetLib( GetLibName() ); + if ( pBasic ) + { + m_xBasic = pBasic; + m_xModule = pBasic->FindModule( GetName() ); + } + } + } + return m_xModule; +} + +ModulWindow::~ModulWindow() +{ + disposeOnce(); +} + +void ModulWindow::dispose() +{ + m_nValid = 0; + StarBASIC::Stop(); + m_aXEditorWindow.disposeAndClear(); + BaseWindow::dispose(); +} + + +void ModulWindow::GetFocus() +{ + if (m_nValid != ValidWindow) + return; + m_aXEditorWindow->GetEdtWindow().GrabFocus(); + // don't call basic calls because focus is somewhere else... +} + +void ModulWindow::DoInit() +{ + GetEditorWindow().InitScrollBars(); +} + +void ModulWindow::Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle&) +{ +} + +void ModulWindow::Resize() +{ + m_aXEditorWindow->SetPosSizePixel( Point( 0, 0 ), GetOutputSizePixel() ); +} + +void ModulWindow::CheckCompileBasic() +{ + if ( !XModule().is() ) + return; + + // never compile while running! + bool const bRunning = StarBASIC::IsRunning(); + bool const bModified = ( !m_xModule->IsCompiled() || + ( GetEditEngine() && GetEditEngine()->IsModified() ) ); + + if ( bRunning || !bModified ) + return; + + bool bDone = false; + + GetShell()->GetViewFrame().GetWindow().EnterWait(); + + AssertValidEditEngine(); + GetEditorWindow().SetSourceInBasic(); + + bool bWasModified = GetBasic()->IsModified(); + + { + // tdf#106529: only use strict compilation mode when compiling from the IDE + css::uno::ContextLayer layer(comphelper::NewFlagContext("BasicStrict")); + bDone = m_xModule->Compile(); + } + if ( !bWasModified ) + GetBasic()->SetModified(false); + + if ( bDone ) + { + GetBreakPoints().SetBreakPointsInBasic( m_xModule.get() ); + } + + GetShell()->GetViewFrame().GetWindow().LeaveWait(); + + m_aStatus.bError = !bDone; + m_aStatus.bIsRunning = false; +} + +void ModulWindow::BasicExecute() +{ + // #116444# check security settings before macro execution + ScriptDocument aDocument( GetDocument() ); + bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get(); + if (bMacrosDisabled || (aDocument.isDocument() && !aDocument.allowMacros())) + { + std::unique_ptr<weld::MessageDialog> xBox( + Application::CreateMessageDialog(GetFrameWeld(), VclMessageType::Warning, + VclButtonsType::Ok, IDEResId(RID_STR_CANNOTRUNMACRO))); + xBox->run(); + return; + } + + CheckCompileBasic(); + + if ( !XModule().is() || !m_xModule->IsCompiled() || m_aStatus.bError ) + return; + + if ( GetBreakPoints().size() ) + m_aStatus.nBasicFlags = m_aStatus.nBasicFlags | BasicDebugFlags::Break; + + if ( !m_aStatus.bIsRunning ) + { + DBG_ASSERT( m_xModule.is(), "No Module!" ); + AddStatus( BASWIN_RUNNINGBASIC ); + sal_uInt16 nStart, nEnd; + TextSelection aSel = GetEditView()->GetSelection(); + // Init cursor to top + const sal_uInt32 nCurMethodStart = aSel.GetStart().GetPara() + 1; + SbMethod* pMethod = nullptr; + // first Macro, else blind "Main" (ExtSearch?) + for (sal_uInt32 nMacro = 0; nMacro < m_xModule->GetMethods()->Count(); nMacro++) + { + SbMethod* pM = static_cast<SbMethod*>(m_xModule->GetMethods()->Get(nMacro)); + assert(pM && "Method?"); + pM->GetLineRange( nStart, nEnd ); + if ( nCurMethodStart >= nStart && nCurMethodStart <= nEnd ) + { + // matched a method to the cursor position + pMethod = pM; + break; + } + } + if ( !pMethod ) + { + // If not in a method then prompt the user + ChooseMacro(GetFrameWeld(), uno::Reference<frame::XModel>()); + return; + } + pMethod->SetDebugFlags(m_aStatus.nBasicFlags); + BasicDLL::SetDebugMode(true); + RunMethod(pMethod); + BasicDLL::SetDebugMode(false); + // if cancelled during Interactive=false + BasicDLL::EnableBreak(true); + ClearStatus( BASWIN_RUNNINGBASIC ); + } + else + m_aStatus.bIsRunning = false; // cancel of Reschedule() +} + +void ModulWindow::CompileBasic() +{ + CheckCompileBasic(); + + XModule().is() && m_xModule->IsCompiled(); +} + +void ModulWindow::BasicRun() +{ + m_aStatus.nBasicFlags = BasicDebugFlags::NONE; + BasicExecute(); +} + +void ModulWindow::BasicStepOver() +{ + m_aStatus.nBasicFlags = BasicDebugFlags::StepInto | BasicDebugFlags::StepOver; + BasicExecute(); +} + + +void ModulWindow::BasicStepInto() +{ + m_aStatus.nBasicFlags = BasicDebugFlags::StepInto; + BasicExecute(); +} + +void ModulWindow::BasicStepOut() +{ + m_aStatus.nBasicFlags = BasicDebugFlags::StepOut; + BasicExecute(); +} + + +void ModulWindow::BasicStop() +{ + StarBASIC::Stop(); + m_aStatus.bIsRunning = false; +} + +void ModulWindow::LoadBasic() +{ + sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, this->GetFrameWeld()); + aDlg.SetContext(sfx2::FileDialogHelper::BasicImportSource); + Reference<XFilePicker3> xFP = aDlg.GetFilePicker(); + + xFP->appendFilter( "BASIC" , "*.bas" ); + xFP->appendFilter( IDEResId(RID_STR_FILTER_ALLFILES), FilterMask_All ); + xFP->setCurrentFilter( "BASIC" ); + + if( aDlg.Execute() != ERRCODE_NONE ) + return; + + Sequence< OUString > aPaths = xFP->getSelectedFiles(); + SfxMedium aMedium( aPaths[0], StreamMode::READ | StreamMode::SHARE_DENYWRITE | StreamMode::NOCREATE ); + SvStream* pStream = aMedium.GetInStream(); + if ( pStream ) + { + AssertValidEditEngine(); + sal_uInt32 nLines = CalcLineCount( *pStream ); + // nLines*4: ReadText/Formatting/Highlighting/Formatting + GetEditorWindow().CreateProgress( IDEResId(RID_STR_GENERATESOURCE), nLines*4 ); + GetEditEngine()->SetUpdateMode( false ); + // tdf#139196 - import macros using either default or utf-8 text encoding + pStream->StartReadingUnicodeText(RTL_TEXTENCODING_UTF8); + if (pStream->Tell() == 3) + pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); + GetEditView()->Read( *pStream ); + GetEditEngine()->SetUpdateMode( true ); + GetEditorWindow().PaintImmediately(); + GetEditorWindow().ForceSyntaxTimeout(); + GetEditorWindow().DestroyProgress(); + ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning(); + if ( nError ) + ErrorHandler::HandleError( nError ); + } + else + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_COULDNTREAD))); + xBox->run(); + } +} + + +void ModulWindow::SaveBasicSource() +{ + sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, this->GetFrameWeld()); + aDlg.SetContext(sfx2::FileDialogHelper::BasicExportSource); + const Reference<XFilePicker3>& xFP = aDlg.GetFilePicker(); + + xFP.queryThrow<XFilePickerControlAccess>()->setValue(ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, Any(true)); + + xFP->appendFilter( "BASIC", "*.bas" ); + xFP->appendFilter( IDEResId(RID_STR_FILTER_ALLFILES), FilterMask_All ); + xFP->setCurrentFilter( "BASIC" ); + + if( aDlg.Execute() != ERRCODE_NONE ) + return; + + Sequence< OUString > aPaths = xFP->getSelectedFiles(); + SfxMedium aMedium( aPaths[0], StreamMode::WRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC ); + SvStream* pStream = aMedium.GetOutStream(); + if ( pStream ) + { + EnterWait(); + AssertValidEditEngine(); + // tdf#139196 - export macros using utf-8 including BOM + pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); + pStream->WriteUChar(0xEF).WriteUChar(0xBB).WriteUChar(0xBF); + GetEditEngine()->Write( *pStream ); + aMedium.Commit(); + LeaveWait(); + ErrCodeMsg nError = aMedium.GetErrorIgnoreWarning(); + if ( nError ) + ErrorHandler::HandleError( nError ); + } + else + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_COULDNTWRITE))); + xErrorBox->run(); + } +} + +void ModulWindow::ImportDialog() +{ + const ScriptDocument& rDocument = GetDocument(); + OUString aLibName = GetLibName(); + implImportDialog(GetFrameWeld(), rDocument, aLibName); +} + +void ModulWindow::ToggleBreakPoint( sal_uInt16 nLine ) +{ + DBG_ASSERT( XModule().is(), "No Module!" ); + + if ( !XModule().is() ) + return; + + CheckCompileBasic(); + if ( m_aStatus.bError ) + { + return; + } + + BreakPoint* pBrk = GetBreakPoints().FindBreakPoint( nLine ); + if ( pBrk ) // remove + { + m_xModule->ClearBP( nLine ); + GetBreakPoints().remove( pBrk ); + } + else // create one + { + if ( m_xModule->SetBP( nLine )) + { + GetBreakPoints().InsertSorted( BreakPoint( nLine ) ); + if ( StarBASIC::IsRunning() ) + { + for (sal_uInt32 nMethod = 0; nMethod < m_xModule->GetMethods()->Count(); nMethod++) + { + SbMethod* pMethod + = static_cast<SbMethod*>(m_xModule->GetMethods()->Get(nMethod)); + assert(pMethod && "Method not found! (NULL)"); + pMethod->SetDebugFlags( pMethod->GetDebugFlags() | BasicDebugFlags::Break ); + } + } + } + } +} + +void ModulWindow::UpdateBreakPoint( const BreakPoint& rBrk ) +{ + DBG_ASSERT( XModule().is(), "No Module!" ); + + if ( XModule().is() ) + { + CheckCompileBasic(); + + if ( rBrk.bEnabled ) + m_xModule->SetBP( rBrk.nLine ); + else + m_xModule->ClearBP( rBrk.nLine ); + } +} + + +void ModulWindow::BasicToggleBreakPoint() +{ + AssertValidEditEngine(); + + TextSelection aSel = GetEditView()->GetSelection(); + aSel.GetStart().GetPara()++; // Basic lines start at 1! + aSel.GetEnd().GetPara()++; + + for ( sal_uInt32 nLine = aSel.GetStart().GetPara(); nLine <= aSel.GetEnd().GetPara(); ++nLine ) + { + ToggleBreakPoint( nLine ); + } + + m_aXEditorWindow->GetBrkWindow().Invalidate(); +} + + +void ModulWindow::BasicToggleBreakPointEnabled() +{ + AssertValidEditEngine(); + + TextView* pView = GetEditView(); + if ( !pView ) + return; + + TextSelection aSel = pView->GetSelection(); + BreakPointList& rList = GetBreakPoints(); + + for ( sal_uInt32 nLine = ++aSel.GetStart().GetPara(), nEnd = ++aSel.GetEnd().GetPara(); nLine <= nEnd; ++nLine ) + { + BreakPoint* pBrk = rList.FindBreakPoint( nLine ); + if ( pBrk ) + { + pBrk->bEnabled = !pBrk->bEnabled; + UpdateBreakPoint( *pBrk ); + } + } + + GetBreakPointWindow().Invalidate(); +} + +void ModulWindow::ManageBreakPoints() +{ + BreakPointWindow& rBrkWin = GetBreakPointWindow(); + BreakPointDialog aBrkDlg(rBrkWin.GetFrameWeld(), GetBreakPoints()); + aBrkDlg.run(); + rBrkWin.Invalidate(); +} + +void ModulWindow::BasicErrorHdl( StarBASIC const * pBasic ) +{ + GetShell()->GetViewFrame().ToTop(); + + // Return value: BOOL + // FALSE: cancel + // TRUE: go on... + sal_uInt16 nErrorLine = StarBASIC::GetLine() - 1; + sal_uInt16 nErrCol1 = StarBASIC::GetCol1(); + sal_uInt16 nErrCol2 = StarBASIC::GetCol2(); + if ( nErrCol2 != 0xFFFF ) + nErrCol2++; + + AssertValidEditEngine(); + GetEditView()->SetSelection( TextSelection( TextPaM( nErrorLine, nErrCol1 ), TextPaM( nErrorLine, nErrCol2 ) ) ); + + // if other basic, the IDE should try to display the correct module + bool const bMarkError = pBasic == GetBasic(); + if ( bMarkError ) + m_aXEditorWindow->GetBrkWindow().SetMarkerPos(nErrorLine, true); + + // #i47002# + Reference< awt::XWindow > xWindow = VCLUnoHelper::GetInterface( this ); + + // tdf#118572 make a currently running dialog, regardless of what its modal + // to, insensitive to user input until after this error dialog goes away. + TopLevelWindowLocker aBusy; + aBusy.incBusy(nullptr); + + ErrorHandler::HandleError(StarBASIC::GetErrorCode(), GetFrameWeld()); + + aBusy.decBusy(); + + // #i47002# + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if ( !pWindow ) + return; + + if ( bMarkError ) + m_aXEditorWindow->GetBrkWindow().SetNoMarker(); + return; +} + +BasicDebugFlags ModulWindow::BasicBreakHdl() +{ + // Return value: sal_uInt16 => see SB-Debug-Flags + sal_uInt16 nErrorLine = StarBASIC::GetLine(); + + + BreakPoint* pBrk = GetBreakPoints().FindBreakPoint( nErrorLine ); + if ( pBrk ) + { + pBrk->nHitCount++; + if ( pBrk->nHitCount <= pBrk->nStopAfter && GetBasic()->IsBreak() ) + return m_aStatus.nBasicFlags; // go on... + } + + nErrorLine--; // EditEngine starts at 0, Basic at 1 + + AssertValidEditEngine(); + GetEditView()->SetSelection( TextSelection( TextPaM( nErrorLine, 0 ), TextPaM( nErrorLine, 0 ) ) ); + m_aXEditorWindow->GetBrkWindow().SetMarkerPos( nErrorLine ); + + m_rLayout.UpdateDebug(false); + + m_aStatus.bIsInReschedule = true; + m_aStatus.bIsRunning = true; + + AddStatus( BASWIN_INRESCHEDULE ); + + InvalidateDebuggerSlots(); + + while( m_aStatus.bIsRunning && !Application::IsQuit()) + Application::Yield(); + + m_aStatus.bIsInReschedule = false; + m_aXEditorWindow->GetBrkWindow().SetNoMarker(); + + ClearStatus( BASWIN_INRESCHEDULE ); + + return m_aStatus.nBasicFlags; +} + +void ModulWindow::BasicAddWatch() +{ + AssertValidEditEngine(); + bool bAdd = true; + if ( !GetEditView()->HasSelection() ) + { + // tdf#57307 - expand selection to include connector punctuations + TextSelection aSel; + OUString aWord = GetEditEngine()->GetWord( GetEditView()->GetSelection().GetEnd(), &aSel.GetStart(), &aSel.GetEnd() ); + if ( !aWord.isEmpty() ) + GetEditView()->SetSelection( aSel ); + else + bAdd = false; + } + if ( bAdd ) + { + TextSelection aSel = GetEditView()->GetSelection(); + if ( aSel.GetStart().GetPara() == aSel.GetEnd().GetPara() ) // single line selection + m_rLayout.BasicAddWatch(GetEditView()->GetSelected()); + } +} + + +void ModulWindow::EditMacro( const OUString& rMacroName ) +{ + DBG_ASSERT( XModule().is(), "No Module!" ); + + if ( !XModule().is() ) + return; + + CheckCompileBasic(); + + if ( m_aStatus.bError ) + return; + + sal_uInt16 nStart, nEnd; + SbMethod* pMethod = static_cast<SbMethod*>(m_xModule->Find( rMacroName, SbxClassType::Method )); + if ( !pMethod ) + return; + + pMethod->GetLineRange( nStart, nEnd ); + if ( nStart ) + { + nStart--; + nEnd--; + } + TextSelection aSel( TextPaM( nStart, 0 ), TextPaM( nStart, 0 ) ); + AssertValidEditEngine(); + TextView * pView = GetEditView(); + // scroll if applicable so that first line is at the top + tools::Long nVisHeight = GetOutputSizePixel().Height(); + if ( pView->GetTextEngine()->GetTextHeight() > nVisHeight ) + { + tools::Long nMaxY = pView->GetTextEngine()->GetTextHeight() - nVisHeight; + tools::Long nOldStartY = pView->GetStartDocPos().Y(); + tools::Long nNewStartY = static_cast<tools::Long>(nStart) * pView->GetTextEngine()->GetCharHeight(); + nNewStartY = std::min( nNewStartY, nMaxY ); + pView->Scroll( 0, -(nNewStartY-nOldStartY) ); + pView->ShowCursor( false ); + GetEditVScrollBar().SetThumbPos( pView->GetStartDocPos().Y() ); + } + pView->SetSelection( aSel ); + pView->ShowCursor(); + pView->GetWindow()->GrabFocus(); +} + +void ModulWindow::StoreData() +{ + // StoreData is called when the BasicManager is destroyed or + // this window is closed. + // => interrupts undesired! + GetEditorWindow().SetSourceInBasic(); +} + +bool ModulWindow::AllowUndo() +{ + return GetEditorWindow().CanModify(); +} + +void ModulWindow::UpdateData() +{ + DBG_ASSERT( XModule().is(), "No Module!" ); + // UpdateData is called when the source has changed from outside + // => interrupts undesired! + + if ( !XModule().is() ) + return; + + SetModule( m_xModule->GetSource32() ); + + if ( GetEditView() ) + { + TextSelection aSel = GetEditView()->GetSelection(); + setTextEngineText(*GetEditEngine(), m_xModule->GetSource32()); + GetEditView()->SetSelection( aSel ); + GetEditEngine()->SetModified( false ); + MarkDocumentModified( GetDocument() ); + } +} + +sal_Int32 ModulWindow::countPages( Printer* pPrinter ) +{ + return FormatAndPrint( pPrinter, -1 ); +} + +void ModulWindow::printPage( sal_Int32 nPage, Printer* pPrinter ) +{ + FormatAndPrint( pPrinter, nPage ); +} + +/* implementation note: this is totally inefficient for the XRenderable interface + usage since the whole "document" will be format for every page. Should this ever + become a problem we should + - format only once for every new printer + - keep an index list for each page which is the starting paragraph +*/ +sal_Int32 ModulWindow::FormatAndPrint( Printer* pPrinter, sal_Int32 nPrintPage ) +{ + AssertValidEditEngine(); + + MapMode eOldMapMode( pPrinter->GetMapMode() ); + vcl::Font aOldFont( pPrinter->GetFont() ); + + vcl::Font aFont( GetEditEngine()->GetFont() ); + aFont.SetAlignment( ALIGN_BOTTOM ); + aFont.SetTransparent( true ); + aFont.SetFontSize( Size( 0, 360 ) ); + pPrinter->SetFont( aFont ); + pPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); + + OUString aTitle( CreateQualifiedName() ); + + sal_Int32 nLineHeight = pPrinter->GetTextHeight(); + if(nLineHeight == 0) + { + nLineHeight = 1; + } + + Size aPaperSz = pPrinter->GetOutputSize(); + aPaperSz.AdjustWidth( -(Print::nLeftMargin + Print::nRightMargin) ); + aPaperSz.AdjustHeight( -(Print::nTopMargin + Print::nBottomMargin) ); + + // nLinepPage is not correct if there's a line break + sal_Int32 nLinespPage = aPaperSz.Height()/nLineHeight; + tools::Long nXTextWidth = pPrinter->approximate_digit_width(); + + sal_Int32 nCharspLine = aPaperSz.Width() / std::max<tools::Long>(nXTextWidth, 1); + const sal_uInt32 nParas = GetEditEngine()->GetParagraphCount(); + + sal_Int32 nPages = nParas/nLinespPage+1; + sal_Int32 nCurPage = 1; + + lcl_PrintHeader( pPrinter, nPages, nCurPage, aTitle, nPrintPage == 0 ); + Point aPos( Print::nLeftMargin, Print::nTopMargin ); + for ( sal_uInt32 nPara = 0; nPara < nParas; ++nPara ) + { + OUString aLine( GetEditEngine()->GetText( nPara ) ); + lcl_ConvertTabsToSpaces( aLine ); + sal_Int32 nLines = aLine.getLength()/nCharspLine+1; + for (sal_Int32 nLine = 0; nLine < nLines; ++nLine) + { + sal_Int32 nBeginIndex = nLine*nCharspLine; + sal_Int32 nCopyCount = std::min<sal_Int32>(nCharspLine, aLine.getLength()-nBeginIndex); + OUString aTmpLine = aLine.copy(nBeginIndex, nCopyCount); + aPos.AdjustY(nLineHeight ); + if ( aPos.Y() > ( aPaperSz.Height() + Print::nTopMargin ) ) + { + nCurPage++; + lcl_PrintHeader( pPrinter, nPages, nCurPage, aTitle, nCurPage-1 == nPrintPage ); + aPos = Point(Print::nLeftMargin, Print::nTopMargin + nLineHeight); + } + if( nCurPage-1 == nPrintPage ) + pPrinter->DrawText( aPos, aTmpLine ); + } + aPos.AdjustY(10 ); // nParaSpace + } + + pPrinter->SetFont( aOldFont ); + pPrinter->SetMapMode( eOldMapMode ); + + return nCurPage; +} + +void ModulWindow::ExecuteCommand (SfxRequest& rReq) +{ + AssertValidEditEngine(); + switch (rReq.GetSlot()) + { + case SID_DELETE: + { + if (!IsReadOnly()) + { + KeyEvent aFakeDelete(0, KEY_DELETE); + (void)GetEditView()->KeyInput(aFakeDelete); + } + break; + } + case SID_SELECTALL: + { + TextSelection aSel( TextPaM( 0, 0 ), TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ) ); + TextView * pView = GetEditView(); + pView->SetSelection( aSel ); + pView->GetWindow()->GrabFocus(); + break; + } + case SID_BASICRUN: + { + BasicRun(); + } + break; + case SID_BASICCOMPILE: + { + CompileBasic(); + } + break; + case SID_BASICSTEPOVER: + { + BasicStepOver(); + } + break; + case SID_BASICSTEPINTO: + { + BasicStepInto(); + } + break; + case SID_BASICSTEPOUT: + { + BasicStepOut(); + } + break; + case SID_BASICLOAD: + { + LoadBasic(); + } + break; + case SID_BASICSAVEAS: + { + SaveBasicSource(); + } + break; + case SID_IMPORT_DIALOG: + { + ImportDialog(); + } + break; + case SID_BASICIDE_MATCHGROUP: + { + GetEditView()->MatchGroup(); + } + break; + case SID_BASICIDE_TOGGLEBRKPNT: + { + BasicToggleBreakPoint(); + } + break; + case SID_BASICIDE_MANAGEBRKPNTS: + { + ManageBreakPoints(); + } + break; + case SID_BASICIDE_TOGGLEBRKPNTENABLED: + { + BasicToggleBreakPointEnabled(); + } + break; + case SID_BASICIDE_ADDWATCH: + { + BasicAddWatch(); + } + break; + case SID_BASICIDE_REMOVEWATCH: + { + m_rLayout.BasicRemoveWatch(); + } + break; + case SID_CUT: + { + if ( !IsReadOnly() ) + { + GetEditView()->Cut(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + } + break; + case SID_COPY: + { + GetEditView()->Copy(); + } + break; + case SID_PASTE: + { + if ( !IsReadOnly() ) + { + GetEditView()->Paste(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + } + break; + case SID_BASICIDE_BRKPNTSCHANGED: + { + GetBreakPointWindow().Invalidate(); + } + break; + case SID_SHOWLINES: + { + const SfxBoolItem* pItem = rReq.GetArg<SfxBoolItem>(rReq.GetSlot()); + bool bLineNumbers = pItem && pItem->GetValue(); + m_aXEditorWindow->SetLineNumberDisplay(bLineNumbers); + + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::BasicIDE::EditorSettings::LineNumbering::set(bLineNumbers, batch); + batch->commit(); + } + break; + case SID_BASICIDE_DELETECURRENT: + { + if (QueryDelModule(m_aName, GetFrameWeld())) + { + // tdf#134551 don't delete the window if last module is removed until this block + // is complete + VclPtr<ModulWindow> xKeepRef(this); + if (m_aDocument.removeModule(m_aLibName, m_aName)) + MarkDocumentModified(m_aDocument); + } + } + break; + case FID_SEARCH_OFF: + GrabFocus(); + break; + case SID_GOTOLINE: + { + GotoLineDialog aGotoDlg(GetFrameWeld()); + if (aGotoDlg.run() == RET_OK) + { + if (sal_Int32 const nLine = aGotoDlg.GetLineNumber()) + { + TextSelection const aSel(TextPaM(nLine - 1, 0), TextPaM(nLine - 1, 0)); + GetEditView()->SetSelection(aSel); + } + } + break; + } + } +} + +void ModulWindow::ExecuteGlobal (SfxRequest& rReq) +{ + switch (rReq.GetSlot()) + { + case SID_SIGNATURE: + { + DocumentSignature aSignature(m_aDocument); + if (aSignature.supportsSignatures()) + { + aSignature.signScriptingContent(rReq.GetFrameWeld()); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_SIGNATURE); + } + } + break; + } +} + +void ModulWindow::GetState( SfxItemSet &rSet ) +{ + SfxWhichIter aIter(rSet); + for ( sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich() ) + { + switch ( nWh ) + { + case SID_CUT: + { + if ( !GetEditView() || !GetEditView()->HasSelection() ) + rSet.DisableItem( nWh ); + + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + } + break; + case SID_COPY: + { + if ( !GetEditView() || !GetEditView()->HasSelection() ) + rSet.DisableItem( nWh ); + } + break; + case SID_PASTE: + { + if ( !IsPasteAllowed() ) + rSet.DisableItem( nWh ); + + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + } + break; + case SID_BASICIDE_STAT_POS: + { + TextView* pView = GetEditView(); + if ( pView ) + { + TextSelection aSel = pView->GetSelection(); + OUString aPos = IDEResId( RID_STR_LINE ) + + " " + + OUString::number(aSel.GetEnd().GetPara()+1) + + ", " + + IDEResId( RID_STR_COLUMN ) + + " " + + OUString::number(aSel.GetEnd().GetIndex()+1); + SfxStringItem aItem( SID_BASICIDE_STAT_POS, aPos ); + rSet.Put( aItem ); + } + } + break; + case SID_BASICIDE_STAT_TITLE: + { + // search for current procedure name (Sub or Function) + TextView* pView = GetEditView(); + if ( pView ) + { + OUString sProcName; + + TextSelection aSel = pView->GetSelection(); + + sal_uInt32 i = aSel.GetStart().GetPara(); + do + { + OUString aCurrLine = GetEditEngine()->GetText( i ); + OUString sProcType; + if (GetEditorWindow().GetProcedureName(aCurrLine, sProcType, sProcName)) + break; + } while (i--); + + OUString aTitle = CreateQualifiedName(); + if (!sProcName.isEmpty()) + aTitle += "." + sProcName; + + if (IsReadOnly()) + aTitle += " (" + IDEResId(RID_STR_READONLY) + ")"; + + SfxStringItem aTitleItem( SID_BASICIDE_STAT_TITLE, aTitle ); + rSet.Put( aTitleItem ); + } + } + break; + case SID_ATTR_INSERT: + { + TextView* pView = GetEditView(); + if ( pView ) + { + SfxBoolItem aItem( SID_ATTR_INSERT, pView->IsInsertMode() ); + rSet.Put( aItem ); + } + } + break; + case SID_SHOWLINES: + { + bool bLineNumbers = ::officecfg::Office::BasicIDE::EditorSettings::LineNumbering::get(); + rSet.Put(SfxBoolItem(nWh, bLineNumbers)); + break; + } + case SID_SELECTALL: + { + if ( !GetEditView() ) + rSet.DisableItem( nWh ); + } + break; + } + } +} + +void ModulWindow::DoScroll( Scrollable* pCurScrollBar ) +{ + if ( ( pCurScrollBar == GetHScrollBar() ) && GetEditView() ) + { + // don't scroll with the value but rather use the Thumb-Pos for the VisArea: + tools::Long nDiff = GetEditView()->GetStartDocPos().X() - pCurScrollBar->GetThumbPos(); + GetEditView()->Scroll( nDiff, 0 ); + GetEditView()->ShowCursor( false ); + pCurScrollBar->SetThumbPos( GetEditView()->GetStartDocPos().X() ); + + } +} + +bool ModulWindow::IsModified() +{ + return GetEditEngine() && GetEditEngine()->IsModified(); +} + +OUString ModulWindow::GetSbModuleName() +{ + OUString aModuleName; + if ( XModule().is() ) + aModuleName = m_xModule->GetName(); + return aModuleName; +} + +OUString ModulWindow::GetTitle() +{ + return GetSbModuleName(); +} + +void ModulWindow::ShowCursor( bool bOn ) +{ + if ( GetEditEngine() ) + { + TextView* pView = GetEditEngine()->GetActiveView(); + if ( pView ) + { + if ( bOn ) + pView->ShowCursor(); + else + pView->HideCursor(); + } + } +} + +void ModulWindow::AssertValidEditEngine() +{ + if ( !GetEditEngine() ) + GetEditorWindow().CreateEditEngine(); +} + +void ModulWindow::Activating () +{ + bool bLineNumbers = ::officecfg::Office::BasicIDE::EditorSettings::LineNumbering::get(); + m_aXEditorWindow->SetLineNumberDisplay(bLineNumbers); + Show(); +} + +void ModulWindow::Deactivating() +{ + Hide(); +} + +sal_uInt16 ModulWindow::StartSearchAndReplace( const SvxSearchItem& rSearchItem, bool bFromStart ) +{ + if (IsSuspended()) + return 0; + + // one could also relinquish syntaxhighlighting/formatting instead of the stupid replace-everything... + AssertValidEditEngine(); + TextView* pView = GetEditView(); + TextSelection aSel; + if ( bFromStart ) + { + aSel = pView->GetSelection(); + if ( !rSearchItem.GetBackward() ) + pView->SetSelection( TextSelection() ); + else + pView->SetSelection( TextSelection( TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ), TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ) ) ); + } + + bool const bForward = !rSearchItem.GetBackward(); + sal_uInt16 nFound = 0; + if ( ( rSearchItem.GetCommand() == SvxSearchCmd::FIND ) || + ( rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL ) ) + { + nFound = pView->Search( rSearchItem.GetSearchOptions() , bForward ) ? 1 : 0; + } + else if ( ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE ) || + ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL ) ) + { + if ( !IsReadOnly() ) + { + bool const bAll = rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL; + nFound = pView->Replace( rSearchItem.GetSearchOptions() , bAll , bForward ); + } + } + + if ( bFromStart && !nFound ) + pView->SetSelection( aSel ); + + return nFound; +} + +SfxUndoManager* ModulWindow::GetUndoManager() +{ + if ( GetEditEngine() ) + return &GetEditEngine()->GetUndoManager(); + return nullptr; +} + +SearchOptionFlags ModulWindow::GetSearchOptions() +{ + SearchOptionFlags nOptions = SearchOptionFlags::SEARCH | + SearchOptionFlags::WHOLE_WORDS | + SearchOptionFlags::BACKWARDS | + SearchOptionFlags::REG_EXP | + SearchOptionFlags::EXACT | + SearchOptionFlags::SELECTION | + SearchOptionFlags::SIMILARITY; + + if ( !IsReadOnly() ) + { + nOptions |= SearchOptionFlags::REPLACE; + nOptions |= SearchOptionFlags::REPLACE_ALL; + } + + return nOptions; +} + +void ModulWindow::BasicStarted() +{ + if ( !XModule().is() ) + return; + + m_aStatus.bIsRunning = true; + BreakPointList& rList = GetBreakPoints(); + if ( rList.size() ) + { + rList.ResetHitCount(); + rList.SetBreakPointsInBasic( m_xModule.get() ); + for (sal_uInt32 nMethod = 0; nMethod < m_xModule->GetMethods()->Count(); nMethod++) + { + SbMethod* pMethod = static_cast<SbMethod*>(m_xModule->GetMethods()->Get(nMethod)); + assert(pMethod && "Method not found! (NULL)"); + pMethod->SetDebugFlags( pMethod->GetDebugFlags() | BasicDebugFlags::Break ); + } + } +} + +void ModulWindow::BasicStopped() +{ + m_aStatus.bIsRunning = false; + GetBreakPointWindow().SetNoMarker(); +} + +EntryDescriptor ModulWindow::CreateEntryDescriptor() +{ + ScriptDocument aDocument( GetDocument() ); + OUString aLibName( GetLibName() ); + LibraryLocation eLocation = aDocument.getLibraryLocation( aLibName ); + OUString aModName( GetName() ); + OUString aLibSubName; + if( m_xBasic.is() && aDocument.isInVBAMode() && XModule().is() ) + { + switch( m_xModule->GetModuleType() ) + { + case script::ModuleType::DOCUMENT: + { + aLibSubName = IDEResId( RID_STR_DOCUMENT_OBJECTS ); + uno::Reference< container::XNameContainer > xLib = aDocument.getOrCreateLibrary( E_SCRIPTS, aLibName ); + if( xLib.is() ) + { + OUString sObjName; + ModuleInfoHelper::getObjectName( xLib, aModName, sObjName ); + if( !sObjName.isEmpty() ) + { + aModName += " (" + sObjName + ")"; + } + } + break; + } + case script::ModuleType::FORM: + aLibSubName = IDEResId( RID_STR_USERFORMS ); + break; + case script::ModuleType::NORMAL: + aLibSubName = IDEResId( RID_STR_NORMAL_MODULES ); + break; + case script::ModuleType::CLASS: + aLibSubName = IDEResId( RID_STR_CLASS_MODULES ); + break; + } + } + return EntryDescriptor( aDocument, eLocation, aLibName, aLibSubName, aModName, OBJ_TYPE_MODULE ); +} + +void ModulWindow::SetReadOnly (bool b) +{ + if ( GetEditView() ) + GetEditView()->SetReadOnly( b ); +} + +bool ModulWindow::IsReadOnly() +{ + return GetEditView() && GetEditView()->IsReadOnly(); +} + +bool ModulWindow::IsPasteAllowed() +{ + bool bPaste = false; + + // get clipboard + Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard(); + if ( xClipboard.is() ) + { + + Reference< datatransfer::XTransferable > xTransf = xClipboard->getContents(); + if ( xTransf.is() ) + { + datatransfer::DataFlavor aFlavor; + SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); + if ( xTransf->isDataFlavorSupported( aFlavor ) ) + bPaste = true; + } + } + + return bPaste; +} + +void ModulWindow::OnNewDocument () +{ + bool bLineNumbers = ::officecfg::Office::BasicIDE::EditorSettings::LineNumbering::get(); + m_aXEditorWindow->SetLineNumberDisplay(bLineNumbers); +} + +OUString ModulWindow::GetHid () const +{ + return HID_BASICIDE_MODULWINDOW; +} +ItemType ModulWindow::GetType () const +{ + return TYPE_MODULE; +} + +bool ModulWindow::HasActiveEditor () const +{ + return !IsSuspended(); +} + + +void ModulWindow::UpdateModule () +{ + OUString const aModule = getTextEngineText(*GetEditEngine()); + + // update module in basic + assert(m_xModule.is()); + + // update module in module window + SetModule(aModule); + + // update module in library + OSL_VERIFY(m_aDocument.updateModule(m_aLibName, m_aName, aModule)); + + GetEditEngine()->SetModified(false); + MarkDocumentModified(m_aDocument); +} + +ModulWindowLayout::ModulWindowLayout (vcl::Window* pParent, ObjectCatalog& rObjectCatalog_) : + Layout(pParent), + pChild(nullptr), + aWatchWindow(VclPtr<WatchWindow>::Create(this)), + aStackWindow(VclPtr<StackWindow>::Create(this)), + rObjectCatalog(rObjectCatalog_) +{ } + +ModulWindowLayout::~ModulWindowLayout() +{ + disposeOnce(); +} + +void ModulWindowLayout::dispose() +{ + aWatchWindow.disposeAndClear(); + aStackWindow.disposeAndClear(); + pChild.clear(); + Layout::dispose(); +} + +void ModulWindowLayout::UpdateDebug (bool bBasicStopped) +{ + aWatchWindow->UpdateWatches(bBasicStopped); + aStackWindow->UpdateCalls(); +} + +void ModulWindowLayout::Paint (vcl::RenderContext& rRenderContext, tools::Rectangle const&) +{ + rRenderContext.DrawText(Point(), IDEResId(RID_STR_NOMODULE)); +} + +void ModulWindowLayout::Activating (BaseWindow& rChild) +{ + assert(dynamic_cast<ModulWindow*>(&rChild)); + pChild = &static_cast<ModulWindow&>(rChild); + aWatchWindow->Show(); + aStackWindow->Show(); + rObjectCatalog.Show(); + rObjectCatalog.SetLayoutWindow(this); + rObjectCatalog.UpdateEntries(); + Layout::Activating(rChild); + aSyntaxColors.SetActiveEditor(&pChild->GetEditorWindow()); +} + +void ModulWindowLayout::Deactivating () +{ + aSyntaxColors.SetActiveEditor(nullptr); + Layout::Deactivating(); + aWatchWindow->Hide(); + aStackWindow->Hide(); + rObjectCatalog.Hide(); + pChild = nullptr; +} + +void ModulWindowLayout::GetState (SfxItemSet &rSet, unsigned nWhich) +{ + switch (nWhich) + { + case SID_SHOW_PROPERTYBROWSER: + rSet.Put(SfxVisibilityItem(nWhich, false)); + break; + + case SID_BASICIDE_CHOOSEMACRO: + rSet.Put(SfxVisibilityItem(nWhich, true)); + break; + } +} + +void ModulWindowLayout::BasicAddWatch (OUString const& rWatchStr) +{ + aWatchWindow->AddWatch(rWatchStr); +} + +void ModulWindowLayout::BasicRemoveWatch () +{ + aWatchWindow->RemoveSelectedWatch(); +} + +void ModulWindowLayout::ShowWatchWindow(bool bVisible) +{ + aWatchWindow->Show(bVisible); + ArrangeWindows(); +} + +void ModulWindowLayout::ShowStackWindow(bool bVisible) +{ + aStackWindow->Show(bVisible); + ArrangeWindows(); +} + +void ModulWindowLayout::OnFirstSize (tools::Long const nWidth, tools::Long const nHeight) +{ + AddToLeft(&rObjectCatalog, Size(nWidth * 0.20, nHeight * 0.75)); + AddToBottom(aWatchWindow.get(), Size(nWidth * 0.67, nHeight * 0.25)); + AddToBottom(aStackWindow.get(), Size(nWidth * 0.33, nHeight * 0.25)); +} + +ModulWindowLayout::SyntaxColors::SyntaxColors () : + pEditor(nullptr) +{ + aConfig.AddListener(this); + + NewConfig(true); +} + +ModulWindowLayout::SyntaxColors::~SyntaxColors () +{ + aConfig.RemoveListener(this); +} + +// virtual +void ModulWindowLayout::SyntaxColors::ConfigurationChanged (utl::ConfigurationBroadcaster*, ConfigurationHints) +{ + NewConfig(false); +} + +// when a new configuration has to be set +void ModulWindowLayout::SyntaxColors::NewConfig (bool bFirst) +{ + static struct + { + TokenType eTokenType; + svtools::ColorConfigEntry eEntry; + } + const vIds[] = + { + { TokenType::Unknown, svtools::FONTCOLOR }, + { TokenType::Identifier, svtools::BASICIDENTIFIER }, + { TokenType::Whitespace, svtools::FONTCOLOR }, + { TokenType::Number, svtools::BASICNUMBER }, + { TokenType::String, svtools::BASICSTRING }, + { TokenType::EOL, svtools::FONTCOLOR }, + { TokenType::Comment, svtools::BASICCOMMENT }, + { TokenType::Error, svtools::BASICERROR }, + { TokenType::Operator, svtools::BASICOPERATOR }, + { TokenType::Keywords, svtools::BASICKEYWORD }, + }; + + Color aDocColor = aConfig.GetColorValue(svtools::BASICEDITOR).nColor; + if (bFirst || aDocColor != m_aBackgroundColor) + { + m_aBackgroundColor = aDocColor; + if (!bFirst && pEditor) + { + pEditor->SetBackground(Wallpaper(m_aBackgroundColor)); + pEditor->Invalidate(); + } + } + + Color aFontColor = aConfig.GetColorValue(svtools::FONTCOLOR).nColor; + if (bFirst || aFontColor != m_aFontColor) + { + m_aFontColor = aFontColor; + if (!bFirst && pEditor) + pEditor->ChangeFontColor(m_aFontColor); + } + + bool bChanged = false; + for (const auto& vId: vIds) + { + Color const aColor = aConfig.GetColorValue(vId.eEntry).nColor; + Color& rMyColor = aColors[vId.eTokenType]; + if (bFirst || aColor != rMyColor) + { + rMyColor = aColor; + bChanged = true; + } + } + if (bChanged && !bFirst && pEditor) + pEditor->UpdateSyntaxHighlighting(); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/baside2.hxx b/basctl/source/basicide/baside2.hxx new file mode 100644 index 0000000000..116dab2bb9 --- /dev/null +++ b/basctl/source/basicide/baside2.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 . + */ + +#pragma once + +#include <memory> +#include <mutex> +#include <layout.hxx> +#include "breakpoint.hxx" +#include "linenumberwindow.hxx" + +#include <basic/sbmod.hxx> +#include <basic/sbstar.hxx> +#include <vcl/InterimItemWindow.hxx> +#include <vcl/idle.hxx> +#include <vcl/weld.hxx> + +#include <svtools/colorcfg.hxx> +#include <svtools/scrolladaptor.hxx> +#include <o3tl/enumarray.hxx> +#include <rtl/ustrbuf.hxx> + +#include <set> +#include <string_view> + +#include <vcl/textdata.hxx> +#include <basic/codecompletecache.hxx> +#include <com/sun/star/reflection/XIdlClass.hpp> +#include <comphelper/syntaxhighlight.hxx> + +class ExtTextEngine; +class TextView; +class SvxSearchItem; +namespace com::sun::star::beans { class XMultiPropertySet; } + +namespace basctl +{ + +class ObjectCatalog; +class CodeCompleteWindow; +class ModulWindowLayout; + +// #108672 Helper functions to get/set text in TextEngine +// using the stream interface (get/setText() only supports +// tools Strings limited to 64K). +// defined in baside2b.cxx +OUString getTextEngineText (ExtTextEngine&); +void setTextEngineText (ExtTextEngine&, std::u16string_view); + +class EditorWindow final : public vcl::Window, public SfxListener +{ +friend class CodeCompleteWindow; +friend class EditorWindowUIObject; +private: + class ChangesListener; + + std::unique_ptr<TextView> pEditView; + std::unique_ptr<ExtTextEngine> pEditEngine; + ModulWindow& rModulWindow; + + rtl::Reference< ChangesListener > listener_; + std::mutex mutex_; + css::uno::Reference< css::beans::XMultiPropertySet > + notifier_; + + tools::Long nCurTextWidth; + + ImplSVEvent* m_nSetSourceInBasicId; + + SyntaxHighlighter aHighlighter; + Idle aSyntaxIdle; + std::set<sal_uInt16> aSyntaxLineTable; + DECL_LINK(SyntaxTimerHdl, Timer *, void); + DECL_LINK(SetSourceInBasicHdl, void*, void); + + // progress bar + class ProgressInfo; + std::unique_ptr<ProgressInfo> pProgress; + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + void ImpDoHighlight( sal_uInt32 nLineOff ); + void ImplSetFont(); + sal_uInt16 nCurrentZoomLevel; + + bool bHighlighting; + bool bDoSyntaxHighlight; + bool bDelayHighlight; + + virtual css::uno::Reference< css::awt::XVclWindowPeer > GetComponentInterface(bool bCreate = true) override; + CodeCompleteDataCache aCodeCompleteCache; + VclPtr<CodeCompleteWindow> pCodeCompleteWnd; + OUString GetActualSubName( sal_uInt32 nLine ); // gets the actual subroutine name according to line number + void SetupAndShowCodeCompleteWnd(const std::vector< OUString >& aEntryVect, TextSelection aSel ); + void HandleAutoCorrect(); + void HandleAutoCloseParen(); + void HandleAutoCloseDoubleQuotes(); + void HandleCodeCompletion(); + void HandleProcedureCompletion(); + TextSelection GetLastHighlightPortionTextSelection() const; + + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override; + virtual void Resize() override; + virtual void KeyInput( const KeyEvent& rKeyEvt ) override; + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; + virtual void Command( const CommandEvent& rCEvt ) override; + virtual void LoseFocus() override; + virtual void RequestHelp( const HelpEvent& rHEvt ) override; + + void DoSyntaxHighlight( sal_uInt32 nPara ); + OUString GetWordAtCursor(); + bool ImpCanModify(); + +public: + EditorWindow (vcl::Window* pParent, ModulWindow*); + virtual ~EditorWindow() override; + virtual void dispose() override; + + ExtTextEngine* GetEditEngine() const { return pEditEngine.get(); } + TextView* GetEditView() const { return pEditView.get(); } + + void CreateProgress( const OUString& rText, sal_uInt32 nRange ); + void DestroyProgress(); + + void ParagraphInsertedDeleted( sal_uInt32 nNewPara, bool bInserted ); + void DoDelayedSyntaxHighlight( sal_uInt32 nPara ); + + void CreateEditEngine(); + void SetScrollBarRanges(); + void InitScrollBars(); + + void ForceSyntaxTimeout(); + void SetSourceInBasic(); + + bool CanModify() { return ImpCanModify(); } + + void ChangeFontColor( Color aColor ); + void UpdateSyntaxHighlighting (); + + void SetEditorZoomLevel(sal_uInt16 nNewZoomLevel); + sal_uInt16 GetCurrentZoom() { return nCurrentZoomLevel; } + + bool GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const; + + FactoryFunction GetUITestFactory() const override; +}; + +class BreakPointWindow final : public vcl::Window +{ + ModulWindow& rModulWindow; + tools::Long nCurYOffset; + sal_uInt16 nMarkerPos; + BreakPointList aBreakPointList; + bool bErrorMarker; + + virtual void DataChanged(DataChangedEvent const & rDCEvt) override; + + void setBackgroundColor(Color aColor); + + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; + BreakPoint* FindBreakPoint( const Point& rMousePos ); + void ShowMarker(vcl::RenderContext& rRenderContext); + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void Command( const CommandEvent& rCEvt ) override; + + bool SyncYOffset(); + +public: + BreakPointWindow (vcl::Window* pParent, ModulWindow*); + + void SetMarkerPos( sal_uInt16 nLine, bool bErrorMarker = false ); + void SetNoMarker (); + + void DoScroll( tools::Long nVertScroll ); + tools::Long& GetCurYOffset() { return nCurYOffset; } + BreakPointList& GetBreakPoints() { return aBreakPointList; } +}; + +class WatchWindow final : public DockingWindow +{ +private: + std::unique_ptr<weld::Container> m_xTitleArea; + std::unique_ptr<weld::Label> m_xTitle; + std::unique_ptr<weld::Entry> m_xEdit; + std::unique_ptr<weld::Button> m_xRemoveWatchButton; + std::unique_ptr<weld::TreeView> m_xTreeListBox; + + ImplSVEvent* m_nUpdateWatchesId; + OUString aEditingRes; + + virtual void Resize() override; + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + + SbxBase* ImplGetSBXForEntry(const weld::TreeIter& rEntry, bool& rbArrayElement); + + void implEnableChildren(const weld::TreeIter& rEntry, bool bEnable); + + DECL_STATIC_LINK(WatchWindow, ButtonHdl, weld::Button&, void); + DECL_LINK(TreeListHdl, weld::TreeView&, void); + DECL_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool); + DECL_LINK(ActivateHdl, weld::Entry&, bool); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool); + typedef std::pair<const weld::TreeIter&, OUString> IterString; + DECL_LINK(EditedEntryHdl, const IterString&, bool); + DECL_LINK(ExecuteUpdateWatches, void*, void); + +public: + explicit WatchWindow (Layout* pParent); + virtual ~WatchWindow() override; + virtual void dispose() override; + + void AddWatch( const OUString& rVName ); + void RemoveSelectedWatch(); + void UpdateWatches(bool bBasicStopped = false); +}; + +class StackWindow : public DockingWindow +{ +private: + std::unique_ptr<weld::Label> m_xTitle; + std::unique_ptr<weld::TreeView> m_xTreeListBox; + +protected: + virtual void Resize() override; + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override; + +public: + explicit StackWindow (Layout* pParent); + virtual ~StackWindow() override; + virtual void dispose() override; + + void UpdateCalls(); +}; + + +class ComplexEditorWindow final : public vcl::Window +{ +private: + VclPtr<BreakPointWindow> aBrkWindow; + VclPtr<LineNumberWindow> aLineNumberWindow; + VclPtr<EditorWindow> aEdtWindow; + VclPtr<ScrollAdaptor> aEWVScrollBar; + VclPtr<ScrollAdaptor> aEWHScrollBar; + + virtual void DataChanged(DataChangedEvent const & rDCEvt) override; + + virtual void Resize() override; + DECL_LINK(ScrollHdl, weld::Scrollbar&, void); + +public: + explicit ComplexEditorWindow( ModulWindow* pParent ); + virtual ~ComplexEditorWindow() override; + virtual void dispose() override; + BreakPointWindow& GetBrkWindow() { return *aBrkWindow; } + LineNumberWindow& GetLineNumberWindow() { return *aLineNumberWindow; } + EditorWindow& GetEdtWindow() { return *aEdtWindow; } + ScrollAdaptor& GetEWVScrollBar() { return *aEWVScrollBar; } + ScrollAdaptor& GetEWHScrollBar() { return *aEWHScrollBar; } + + void SetLineNumberDisplay(bool b); +}; + + +class ModulWindow: public BaseWindow +{ +private: + ModulWindowLayout& m_rLayout; + StarBASICRef m_xBasic; + short m_nValid; + VclPtr<ComplexEditorWindow> m_aXEditorWindow; + BasicStatus m_aStatus; + SbModuleRef m_xModule; + OUString m_aModule; + + void CheckCompileBasic(); + void BasicExecute(); + + sal_Int32 FormatAndPrint( Printer* pPrinter, sal_Int32 nPage ); + SbModuleRef const & XModule(); +protected: + virtual void Resize() override; + virtual void GetFocus() override; + virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override; + virtual void DoInit() override; + virtual void DoScroll(Scrollable* pCurScrollBar) override; + +public: + ModulWindow( ModulWindowLayout* pParent, const ScriptDocument& rDocument, const OUString& aLibName, const OUString& aName, OUString aModule ); + + virtual ~ModulWindow() override; + virtual void dispose() override; + + virtual void ExecuteCommand (SfxRequest& rReq) override; + virtual void ExecuteGlobal (SfxRequest& rReq) override; + virtual void GetState( SfxItemSet& ) override; + virtual void StoreData() override; + virtual void UpdateData() override; + // return number of pages to be printed + virtual sal_Int32 countPages( Printer* pPrinter ) override; + // print page + virtual void printPage( sal_Int32 nPage, Printer* pPrinter ) override; + virtual OUString GetTitle() override; + virtual EntryDescriptor CreateEntryDescriptor() override; + virtual bool AllowUndo() override; + virtual void SetReadOnly (bool bReadOnly) override; + virtual bool IsReadOnly() override; + + StarBASIC* GetBasic() { XModule(); return m_xBasic.get(); } + + SbModule* GetSbModule() { return m_xModule.get(); } + void SetSbModule( SbModule* pModule ) { m_xModule = pModule; } + OUString GetSbModuleName(); + + void CompileBasic(); + void BasicRun(); + void BasicStepOver(); + void BasicStepInto(); + void BasicStepOut(); + void BasicStop(); + void BasicToggleBreakPoint(); + void BasicToggleBreakPointEnabled(); + void ManageBreakPoints(); + void UpdateBreakPoint( const BreakPoint& rBrk ); + void BasicAddWatch(); + + void BasicErrorHdl( StarBASIC const * pBasic ); + BasicDebugFlags BasicBreakHdl(); + void AssertValidEditEngine(); + + void LoadBasic(); + void SaveBasicSource(); + void ImportDialog(); + + void EditMacro( const OUString& rMacroName ); + + void ToggleBreakPoint( sal_uInt16 nLine ); + + BasicStatus& GetBasicStatus() { return m_aStatus; } + + virtual bool IsModified () override; + bool IsPasteAllowed (); + + void ShowCursor( bool bOn ); + + virtual SearchOptionFlags GetSearchOptions() override; + virtual sal_uInt16 StartSearchAndReplace (SvxSearchItem const&, bool bFromStart = false) override; + + EditorWindow& GetEditorWindow() { return m_aXEditorWindow->GetEdtWindow(); } + BreakPointWindow& GetBreakPointWindow() { return m_aXEditorWindow->GetBrkWindow(); } + LineNumberWindow& GetLineNumberWindow() { return m_aXEditorWindow->GetLineNumberWindow(); } + ScrollAdaptor& GetEditVScrollBar() { return m_aXEditorWindow->GetEWVScrollBar(); } + ScrollAdaptor& GetEditHScrollBar() { return m_aXEditorWindow->GetEWHScrollBar(); } + ExtTextEngine* GetEditEngine() { return GetEditorWindow().GetEditEngine(); } + TextView* GetEditView() { return GetEditorWindow().GetEditView(); } + BreakPointList& GetBreakPoints() { return GetBreakPointWindow().GetBreakPoints(); } + ModulWindowLayout& GetLayout () { return m_rLayout; } + + virtual void BasicStarted() override; + virtual void BasicStopped() override; + + virtual SfxUndoManager* + GetUndoManager() override; + + const OUString& GetModule() const { return m_aModule; } + void SetModule( const OUString& aModule ) { m_aModule = aModule; } + + virtual void Activating () override; + virtual void Deactivating () override; + + virtual void OnNewDocument () override; + virtual OUString GetHid () const override; + virtual ItemType GetType () const override; + virtual bool HasActiveEditor () const override; + + void UpdateModule (); +}; + +class ModulWindowLayout: public Layout +{ +public: + ModulWindowLayout (vcl::Window* pParent, ObjectCatalog&); + virtual ~ModulWindowLayout() override; + virtual void dispose() override; +public: + // Layout: + virtual void Activating (BaseWindow&) override; + virtual void Deactivating () override; + virtual void GetState (SfxItemSet&, unsigned nWhich) override; + virtual void UpdateDebug (bool bBasicStopped) override; +public: + void BasicAddWatch (OUString const&); + void BasicRemoveWatch (); + void ShowWatchWindow(bool bVisible); + void ShowStackWindow(bool bVisible); + bool IsWatchWindowVisible() { return aWatchWindow->IsVisible(); } + bool IsStackWindowVisible() { return aStackWindow->IsVisible(); } + Color const & GetSyntaxBackgroundColor () const { return aSyntaxColors.GetBackgroundColor(); } + Color const & GetFontColor () const { return aSyntaxColors.GetFontColor(); } + Color const & GetSyntaxColor (TokenType eType) const { return aSyntaxColors.GetColor(eType); } + +protected: + // Window: + virtual void Paint (vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + // Layout: + virtual void OnFirstSize (tools::Long nWidth, tools::Long nHeight) override; + +private: + // main child window + VclPtr<ModulWindow> pChild; + // dockable windows + VclPtr<WatchWindow> aWatchWindow; + VclPtr<StackWindow> aStackWindow; + ObjectCatalog& rObjectCatalog; + + // SyntaxColors -- stores Basic syntax highlighting colors + class SyntaxColors : public utl::ConfigurationListener + { + public: + SyntaxColors (); + virtual ~SyntaxColors () override; + public: + void SetActiveEditor (EditorWindow* pEditor_) { pEditor = pEditor_; } + public: + Color const & GetBackgroundColor () const { return m_aBackgroundColor; }; + Color const & GetFontColor () const { return m_aFontColor; } + Color const & GetColor(TokenType eType) const { return aColors[eType]; } + + private: + virtual void ConfigurationChanged (utl::ConfigurationBroadcaster*, ConfigurationHints) override; + void NewConfig (bool bFirst); + + private: + Color m_aBackgroundColor; + Color m_aFontColor; + // the color values (the indexes are TokenType, see comphelper/syntaxhighlight.hxx) + o3tl::enumarray<TokenType, Color> aColors; + // the configuration + svtools::ColorConfig aConfig; + // the active editor + VclPtr<EditorWindow> pEditor; + + } aSyntaxColors; +}; + +class CodeCompleteWindow final : public InterimItemWindow +{ +private: + VclPtr<EditorWindow> pParent; // parent window + TextSelection m_aTextSelection; + std::unique_ptr<weld::TreeView> m_xListBox; + + /* a buffer to build up function name when typing + * a function name, used for showing/hiding listbox values + * */ + OUStringBuffer aFuncBuffer; + + void InsertSelectedEntry(); // insert the selected entry + void SetMatchingEntries(); // sets the visible entries based on aFuncBuffer variable + TextView* GetParentEditView(); + + DECL_LINK(ImplDoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(ImplSelectHdl, weld::TreeView&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + +public: + explicit CodeCompleteWindow( EditorWindow* pPar ); + virtual ~CodeCompleteWindow() override; + virtual void dispose() override; + + void InsertEntry( const OUString& aStr ); + void ClearListBox(); + void SetTextSelection( const TextSelection& aSel ); + const TextSelection& GetTextSelection() const { return m_aTextSelection;} + void ResizeAndPositionListBox(); + void SelectFirstEntry(); //selects first entry in ListBox + + /* + * clears if typed anything, then hides + * the window, clear internal variables + * */ + void ClearAndHide(); + void HideAndRestoreFocus(); + + bool HandleKeyInput(const KeyEvent& rKeyEvt); +}; + +class UnoTypeCodeCompletetor +{ +private: + css::uno::Reference< css::reflection::XIdlClass > xClass; + bool bCanComplete; + + bool CheckField( const OUString& sFieldName ); + bool CheckMethod( const OUString& sMethName ); + +public: + UnoTypeCodeCompletetor( const std::vector< OUString >& aVect, const OUString& sVarType ); + + std::vector< OUString > GetXIdlClassMethods() const; + std::vector< OUString > GetXIdlClassFields() const; + + bool CanCodeComplete() const { return bCanComplete;} +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/baside2b.cxx b/basctl/source/basicide/baside2b.cxx new file mode 100644 index 0000000000..0cb1316117 --- /dev/null +++ b/basctl/source/basicide/baside2b.cxx @@ -0,0 +1,2985 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> +#include <string_view> + +#include <helpids.h> +#include <iderid.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> + +#include "baside2.hxx" +#include "brkdlg.hxx" +#include <basidesh.hxx> +#include <basobj.hxx> +#include <iderdll.hxx> + +#include <basic/sbmeth.hxx> +#include <basic/sbuno.hxx> +#include <com/sun/star/beans/XMultiPropertySet.hpp> +#include <com/sun/star/beans/XPropertiesChangeListener.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/script/XLibraryContainer2.hpp> +#include <comphelper/string.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <o3tl/string_view.hxx> +#include <officecfg/Office/Common.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/progress.hxx> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> +#include <utility> +#include <vcl/image.hxx> +#include <vcl/weld.hxx> +#include <vcl/weldutils.hxx> +#include <svl/urihelper.hxx> +#include <svx/svxids.hrc> +#include <vcl/commandevent.hxx> +#include <vcl/xtextedt.hxx> +#include <vcl/textview.hxx> +#include <vcl/txtattr.hxx> +#include <vcl/settings.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/taskpanelist.hxx> +#include <vcl/help.hxx> +#include <cppuhelper/implbase.hxx> +#include <vector> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <unotools/charclass.hxx> +#include "textwindowpeer.hxx" +#include "uiobject.hxx" +#include <basegfx/utils/zoomtools.hxx> +#include <svl/itemset.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace +{ + +sal_uInt16 const NoMarker = 0xFFFF; +tools::Long const nBasePad = 2; +tools::Long const nCursorPad = 5; + +tools::Long nVirtToolBoxHeight; // inited in WatchWindow, used in Stackwindow + +// Returns pBase converted to SbxVariable if valid and is not an SbxMethod. +SbxVariable* IsSbxVariable (SbxBase* pBase) +{ + if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pBase)) + if (!dynamic_cast<SbxMethod*>(pVar)) + return pVar; + return nullptr; +} + +Image GetImage(const OUString& rId) +{ + return Image(StockImage::Yes, rId); +} + +int const nScrollLine = 12; +int const nScrollPage = 60; +int const DWBORDER = 3; + +std::u16string_view const cSuffixes = u"%&!#@$"; + +} // namespace + + +/** + * Helper functions to get/set text in TextEngine using + * the stream interface. + * + * get/setText() only supports tools Strings limited to 64K). + */ +OUString getTextEngineText (ExtTextEngine& rEngine) +{ + SvMemoryStream aMemStream; + aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 ); + aMemStream.SetLineDelimiter( LINEEND_LF ); + rEngine.Write( aMemStream ); + std::size_t nSize = aMemStream.Tell(); + OUString aText( static_cast<const char*>(aMemStream.GetData()), + nSize, RTL_TEXTENCODING_UTF8 ); + return aText; +} + +void setTextEngineText (ExtTextEngine& rEngine, std::u16string_view aStr) +{ + rEngine.SetText(OUString()); + OString aUTF8Str = OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 ); + SvMemoryStream aMemStream( const_cast<char *>(aUTF8Str.getStr()), aUTF8Str.getLength(), + StreamMode::READ ); + aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 ); + aMemStream.SetLineDelimiter( LINEEND_LF ); + rEngine.Read(aMemStream); +} + +namespace +{ + +void lcl_DrawIDEWindowFrame(DockingWindow const * pWin, vcl::RenderContext& rRenderContext) +{ + if (pWin->IsFloatingMode()) + return; + + Size aSz(pWin->GetOutputSizePixel()); + const Color aOldLineColor(rRenderContext.GetLineColor()); + rRenderContext.SetLineColor(COL_WHITE); + // White line on top + rRenderContext.DrawLine(Point(0, 0), Point(aSz.Width(), 0)); + // Black line at bottom + rRenderContext.SetLineColor(COL_BLACK); + rRenderContext.DrawLine(Point(0, aSz.Height() - 1), + Point(aSz.Width(), aSz.Height() - 1)); + rRenderContext.SetLineColor(aOldLineColor); +} + +void lcl_SeparateNameAndIndex( const OUString& rVName, OUString& rVar, OUString& rIndex ) +{ + rVar = rVName; + rIndex.clear(); + sal_Int32 nIndexStart = rVar.indexOf( '(' ); + if ( nIndexStart != -1 ) + { + sal_Int32 nIndexEnd = rVar.indexOf( ')', nIndexStart ); + if (nIndexEnd != -1) + { + rIndex = rVar.copy(nIndexStart + 1, nIndexEnd - nIndexStart - 1); + rVar = rVar.copy(0, nIndexStart); + rVar = comphelper::string::stripEnd(rVar, ' '); + rIndex = comphelper::string::strip(rIndex, ' '); + } + } + + if ( !rVar.isEmpty() ) + { + sal_uInt16 nLastChar = rVar.getLength()-1; + if ( cSuffixes.find(rVar[ nLastChar ] ) != std::u16string_view::npos ) + rVar = rVar.replaceAt( nLastChar, 1, u"" ); + } + if ( !rIndex.isEmpty() ) + { + sal_uInt16 nLastChar = rIndex.getLength()-1; + if ( cSuffixes.find(rIndex[ nLastChar ] ) != std::u16string_view::npos ) + rIndex = rIndex.replaceAt( nLastChar, 1, u"" ); + } +} + +} // namespace + + +// EditorWindow + + +class EditorWindow::ChangesListener: + public cppu::WeakImplHelper< beans::XPropertiesChangeListener > +{ +public: + explicit ChangesListener(EditorWindow & editor): editor_(editor) {} + +private: + virtual ~ChangesListener() override {} + + virtual void SAL_CALL disposing(lang::EventObject const &) override + { + std::unique_lock g(editor_.mutex_); + editor_.notifier_.clear(); + } + + virtual void SAL_CALL propertiesChange( + Sequence< beans::PropertyChangeEvent > const &) override + { + SolarMutexGuard g; + editor_.ImplSetFont(); + } + + EditorWindow & editor_; +}; + +class EditorWindow::ProgressInfo : public SfxProgress +{ +public: + ProgressInfo (SfxObjectShell* pObjSh, OUString const& rText, sal_uInt32 nRange) : + SfxProgress(pObjSh, rText, nRange), + nCurState(0) + { } + + void StepProgress () + { + SetState(++nCurState); + } + +private: + sal_uInt32 nCurState; +}; + +EditorWindow::EditorWindow (vcl::Window* pParent, ModulWindow* pModulWindow) : + Window(pParent, WB_BORDER), + rModulWindow(*pModulWindow), + nCurTextWidth(0), + m_nSetSourceInBasicId(nullptr), + aHighlighter(HighlighterLanguage::Basic), + aSyntaxIdle( "basctl EditorWindow aSyntaxIdle" ), + bHighlighting(false), + bDoSyntaxHighlight(true), + bDelayHighlight(true), + pCodeCompleteWnd(VclPtr<CodeCompleteWindow>::Create(this)) +{ + set_id("EditorWindow"); + const Wallpaper aBackground(rModulWindow.GetLayout().GetSyntaxBackgroundColor()); + SetBackground(aBackground); + GetWindow(GetWindowType::Border)->SetBackground(aBackground); + SetPointer( PointerStyle::Text ); + SetHelpId( HID_BASICIDE_EDITORWINDOW ); + + listener_ = new ChangesListener(*this); + Reference< beans::XMultiPropertySet > n( + officecfg::Office::Common::Font::SourceViewFont::get(), + UNO_QUERY_THROW); + { + std::unique_lock g(mutex_); + notifier_ = n; + } + + // The zoom level applied to the editor window is the zoom slider value in the shell + nCurrentZoomLevel = GetShell()->GetCurrentZoomSliderValue(); + + const Sequence<OUString> aPropertyNames{"FontHeight", "FontName"}; + n->addPropertiesChangeListener(aPropertyNames, listener_); +} + + +EditorWindow::~EditorWindow() +{ + disposeOnce(); +} + +void EditorWindow::dispose() +{ + if (m_nSetSourceInBasicId) + { + Application::RemoveUserEvent(m_nSetSourceInBasicId); + m_nSetSourceInBasicId = nullptr; + } + + Reference< beans::XMultiPropertySet > n; + { + std::unique_lock g(mutex_); + n = notifier_; + } + if (n.is()) { + n->removePropertiesChangeListener(listener_); + } + + aSyntaxIdle.Stop(); + + if ( pEditEngine ) + { + EndListening( *pEditEngine ); + pEditEngine->RemoveView(pEditView.get()); + } + pCodeCompleteWnd.disposeAndClear(); + vcl::Window::dispose(); +} + +OUString EditorWindow::GetWordAtCursor() +{ + OUString aWord; + + if ( pEditView ) + { + TextEngine* pTextEngine = pEditView->GetTextEngine(); + if ( pTextEngine ) + { + // check first, if the cursor is at a help URL + const TextSelection& rSelection = pEditView->GetSelection(); + const TextPaM& rSelStart = rSelection.GetStart(); + const TextPaM& rSelEnd = rSelection.GetEnd(); + OUString aText = pTextEngine->GetText( rSelEnd.GetPara() ); + CharClass aClass( ::comphelper::getProcessComponentContext() , Application::GetSettings().GetLanguageTag() ); + sal_Int32 nSelStart = rSelStart.GetIndex(); + sal_Int32 nSelEnd = rSelEnd.GetIndex(); + sal_Int32 nLength = aText.getLength(); + sal_Int32 nStart = 0; + sal_Int32 nEnd = nLength; + while ( nStart < nLength ) + { + OUString aURL( URIHelper::FindFirstURLInText( aText, nStart, nEnd, aClass ) ); + INetURLObject aURLObj( aURL ); + if ( aURLObj.GetProtocol() == INetProtocol::VndSunStarHelp + && nSelStart >= nStart && nSelStart <= nEnd && nSelEnd >= nStart && nSelEnd <= nEnd ) + { + aWord = aURL; + break; + } + nStart = nEnd; + nEnd = nLength; + } + + // Not the selected range, but at the CursorPosition, + // if a word is partially selected. + if ( aWord.isEmpty() ) + aWord = pTextEngine->GetWord( rSelEnd ); + + // Can be empty when full word selected, as Cursor behind it + if ( aWord.isEmpty() && pEditView->HasSelection() ) + aWord = pTextEngine->GetWord( rSelStart ); + } + } + + return aWord; +} + +void EditorWindow::RequestHelp( const HelpEvent& rHEvt ) +{ + bool bDone = false; + + // Should have been activated at some point + if ( pEditEngine ) + { + if ( rHEvt.GetMode() & HelpEventMode::CONTEXT ) + { + OUString aKeyword = GetWordAtCursor(); + Application::GetHelp()->SearchKeyword( aKeyword ); + bDone = true; + } + else if ( rHEvt.GetMode() & HelpEventMode::QUICK ) + { + OUString aHelpText; + tools::Rectangle aHelpRect; + if ( StarBASIC::IsRunning() ) + { + Point aWindowPos = rHEvt.GetMousePosPixel(); + aWindowPos = ScreenToOutputPixel( aWindowPos ); + Point aDocPos = GetEditView()->GetDocPos( aWindowPos ); + TextPaM aCursor = GetEditView()->GetTextEngine()->GetPaM(aDocPos); + TextPaM aStartOfWord; + OUString aWord = GetEditView()->GetTextEngine()->GetWord( aCursor, &aStartOfWord ); + if ( !aWord.isEmpty() && !comphelper::string::isdigitAsciiString(aWord) ) + { + sal_uInt16 nLastChar = aWord.getLength() - 1; + if ( cSuffixes.find(aWord[ nLastChar ] ) != std::u16string_view::npos ) + aWord = aWord.replaceAt( nLastChar, 1, u"" ); + SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord ); + if (SbxVariable const* pVar = IsSbxVariable(pSBX)) + { + SbxDataType eType = pVar->GetType(); + if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) ) + // might cause a crash e. g. at the selections-object + // Type == Object does not mean pVar == Object! + ; // aHelpText = ((SbxObject*)pVar)->GetClassName(); + else if ( eType & SbxARRAY ) + ; // aHelpText = "{...}"; + else if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxEMPTY) ) + { + aHelpText = pVar->GetName(); + if ( aHelpText.isEmpty() ) // name is not copied with the passed parameters + aHelpText = aWord; + aHelpText += "=" + pVar->GetOUString(); + } + } + if ( !aHelpText.isEmpty() ) + { + tools::Rectangle aStartWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aStartOfWord)); + TextPaM aEndOfWord(aStartOfWord.GetPara(), aStartOfWord.GetIndex() + aWord.getLength()); + tools::Rectangle aEndWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aEndOfWord)); + aHelpRect = aStartWordRect.GetUnion(aEndWordRect); + + Point aTopLeft = GetEditView()->GetWindowPos(aHelpRect.TopLeft()); + aTopLeft = GetEditView()->GetWindow()->OutputToScreenPixel(aTopLeft); + + aHelpRect.SetPos(aTopLeft); + } + } + } + Help::ShowQuickHelp( this, aHelpRect, aHelpText, QuickHelpFlags::NONE); + bDone = true; + } + } + + if ( !bDone ) + Window::RequestHelp( rHEvt ); +} + + +void EditorWindow::Resize() +{ + // ScrollBars, etc. happens in Adjust... + if ( !pEditView ) + return; + + tools::Long nVisY = pEditView->GetStartDocPos().Y(); + + pEditView->ShowCursor(); + Size aOutSz( GetOutputSizePixel() ); + tools::Long nMaxVisAreaStart = pEditView->GetTextEngine()->GetTextHeight() - aOutSz.Height(); + if ( nMaxVisAreaStart < 0 ) + nMaxVisAreaStart = 0; + if ( pEditView->GetStartDocPos().Y() > nMaxVisAreaStart ) + { + Point aStartDocPos( pEditView->GetStartDocPos() ); + aStartDocPos.setY( nMaxVisAreaStart ); + pEditView->SetStartDocPos( aStartDocPos ); + pEditView->ShowCursor(); + rModulWindow.GetBreakPointWindow().GetCurYOffset() = aStartDocPos.Y(); + rModulWindow.GetLineNumberWindow().GetCurYOffset() = aStartDocPos.Y(); + } + InitScrollBars(); + if ( nVisY != pEditView->GetStartDocPos().Y() ) + Invalidate(); +} + + +void EditorWindow::MouseMove( const MouseEvent &rEvt ) +{ + if ( pEditView ) + pEditView->MouseMove( rEvt ); +} + + +void EditorWindow::MouseButtonUp( const MouseEvent &rEvt ) +{ + if ( pEditView ) + { + pEditView->MouseButtonUp( rEvt ); + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_STAT_POS ); + pBindings->Invalidate( SID_BASICIDE_STAT_TITLE ); + } + } +} + +void EditorWindow::MouseButtonDown( const MouseEvent &rEvt ) +{ + GrabFocus(); + if (!pEditView) + return; + pEditView->MouseButtonDown(rEvt); + if( pCodeCompleteWnd->IsVisible() ) + { + if (pEditView->GetSelection() != pCodeCompleteWnd->GetTextSelection()) + { + //selection changed, code complete window should be hidden + pCodeCompleteWnd->HideAndRestoreFocus(); + } + } +} + +void EditorWindow::Command( const CommandEvent& rCEvt ) +{ + if ( !pEditView ) + return; + + pEditView->Command( rCEvt ); + if ( ( rCEvt.GetCommand() == CommandEventId::Wheel ) || + ( rCEvt.GetCommand() == CommandEventId::StartAutoScroll ) || + ( rCEvt.GetCommand() == CommandEventId::AutoScroll ) ) + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + + // Check if it is a Ctrl+Wheel zoom command + if (pData && pData->IsMod1()) + { + const sal_uInt16 nOldZoom = GetCurrentZoom(); + sal_uInt16 nNewZoom; + if( pData->GetDelta() < 0 ) + nNewZoom = std::max<sal_uInt16>(basctl::Shell::GetMinZoom(), + basegfx::zoomtools::zoomOut(nOldZoom)); + else + nNewZoom = std::min<sal_uInt16>(basctl::Shell::GetMaxZoom(), + basegfx::zoomtools::zoomIn(nOldZoom)); + GetShell()->SetGlobalEditorZoomLevel(nNewZoom); + } + else + HandleScrollCommand(rCEvt, &rModulWindow.GetEditHScrollBar(), &rModulWindow.GetEditVScrollBar()); + } + else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) { + SfxDispatcher* pDispatcher = GetDispatcher(); + if ( pDispatcher ) + { + SfxDispatcher::ExecutePopup(); + } + if( pCodeCompleteWnd->IsVisible() ) // hide the code complete window + pCodeCompleteWnd->ClearAndHide(); + } +} + +bool EditorWindow::ImpCanModify() +{ + bool bCanModify = true; + if ( StarBASIC::IsRunning() && rModulWindow.GetBasicStatus().bIsRunning ) + { + // If in Trace-mode, abort the trace or refuse input + // Remove markers in the modules in Notify at Basic::Stopped + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::OkCancel, + IDEResId(RID_STR_WILLSTOPPRG))); + if (xQueryBox->run() == RET_OK) + { + rModulWindow.GetBasicStatus().bIsRunning = false; + StopBasic(); + } + else + bCanModify = false; + } + return bCanModify; +} + +void EditorWindow::KeyInput( const KeyEvent& rKEvt ) +{ + if ( !pEditView ) // Happens in Win95 + return; + + bool const bWasModified = pEditEngine->IsModified(); + // see if there is an accelerator to be processed first + SfxViewShell *pVS( SfxViewShell::Current()); + bool bDone = pVS && pVS->KeyInput( rKEvt ); + + if (pCodeCompleteWnd->IsVisible() && CodeCompleteOptions::IsCodeCompleteOn()) + { + pCodeCompleteWnd->HandleKeyInput(rKEvt); + if( rKEvt.GetKeyCode().GetCode() == KEY_UP + || rKEvt.GetKeyCode().GetCode() == KEY_DOWN + || rKEvt.GetKeyCode().GetCode() == KEY_TAB + || rKEvt.GetKeyCode().GetCode() == KEY_POINT) + return; + } + + if( (rKEvt.GetKeyCode().GetCode() == KEY_SPACE || + rKEvt.GetKeyCode().GetCode() == KEY_TAB || + rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) && CodeCompleteOptions::IsAutoCorrectOn() ) + { + HandleAutoCorrect(); + } + + if( rKEvt.GetCharCode() == '"' && CodeCompleteOptions::IsAutoCloseQuotesOn() ) + {//autoclose double quotes + HandleAutoCloseDoubleQuotes(); + } + + if( rKEvt.GetCharCode() == '(' && CodeCompleteOptions::IsAutoCloseParenthesisOn() ) + {//autoclose parenthesis + HandleAutoCloseParen(); + } + + if( rKEvt.GetKeyCode().GetCode() == KEY_RETURN && CodeCompleteOptions::IsProcedureAutoCompleteOn() ) + {//autoclose implementation + HandleProcedureCompletion(); + } + + if( rKEvt.GetKeyCode().GetCode() == KEY_POINT && CodeCompleteOptions::IsCodeCompleteOn() ) + { + HandleCodeCompletion(); + } + if ( !bDone && ( !TextEngine::DoesKeyChangeText( rKEvt ) || ImpCanModify() ) ) + { + if ( ( rKEvt.GetKeyCode().GetCode() == KEY_TAB ) && !rKEvt.GetKeyCode().IsMod1() && + !rKEvt.GetKeyCode().IsMod2() && !GetEditView()->IsReadOnly() ) + { + TextSelection aSel( pEditView->GetSelection() ); + if ( aSel.GetStart().GetPara() != aSel.GetEnd().GetPara() ) + { + bDelayHighlight = false; + if ( !rKEvt.GetKeyCode().IsShift() ) + pEditView->IndentBlock(); + else + pEditView->UnindentBlock(); + bDelayHighlight = true; + bDone = true; + } + } + if ( !bDone ) + bDone = pEditView->KeyInput( rKEvt ); + } + if ( !bDone ) + { + Window::KeyInput( rKEvt ); + } + else + { + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_STAT_POS ); + pBindings->Invalidate( SID_BASICIDE_STAT_TITLE ); + if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR ) + { + pBindings->Update( SID_BASICIDE_STAT_POS ); + pBindings->Update( SID_BASICIDE_STAT_TITLE ); + } + if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA || + rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM ) + { + // If the module is read-only, warn that it can't be edited + if ( rModulWindow.IsReadOnly() ) + rModulWindow.ShowReadOnlyInfoBar(); + } + if ( !bWasModified && pEditEngine->IsModified() ) + { + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Invalidate( SID_DOC_MODIFIED ); + pBindings->Invalidate( SID_UNDO ); + } + if ( rKEvt.GetKeyCode().GetCode() == KEY_INSERT ) + pBindings->Invalidate( SID_ATTR_INSERT ); + } + } +} + +void EditorWindow::HandleAutoCorrect() +{ + TextSelection aSel = GetEditView()->GetSelection(); + const sal_uInt32 nLine = aSel.GetStart().GetPara(); + const sal_Int32 nIndex = aSel.GetStart().GetIndex(); + OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified + const OUString& sActSubName = GetActualSubName( nLine ); // the actual procedure + + std::vector<HighlightPortion> aPortions; + aHighlighter.getHighlightPortions( aLine, aPortions ); + + if( aPortions.empty() ) + return; + + HighlightPortion& r = aPortions.back(); + if( static_cast<size_t>(nIndex) != aPortions.size()-1 ) + {//cursor is not standing at the end of the line + for (auto const& portion : aPortions) + { + if( portion.nEnd == nIndex ) + { + r = portion; + break; + } + } + } + + OUString sStr = aLine.copy( r.nBegin, r.nEnd - r.nBegin ); + //if WS or empty string: stop, nothing to do + if( ( r.tokenType == TokenType::Whitespace ) || sStr.isEmpty() ) + return; + //create the appropriate TextSelection, and update the cache + TextPaM aStart( nLine, r.nBegin ); + TextPaM aEnd( nLine, r.nBegin + sStr.getLength() ); + TextSelection sTextSelection( aStart, aEnd ); + rModulWindow.UpdateModule(); + rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse( aCodeCompleteCache ); + // correct the last entered keyword + if( r.tokenType == TokenType::Keywords ) + { + sStr = sStr.toAsciiLowerCase(); + if( !SbModule::GetKeywordCase(sStr).isEmpty() ) + // if it is a keyword, get its correct case + sStr = SbModule::GetKeywordCase(sStr); + else + // else capitalize first letter/select the correct one, and replace + sStr = sStr.replaceAt( 0, 1, OUString(sStr[0]).toAsciiUpperCase() ); + + pEditEngine->ReplaceText( sTextSelection, sStr ); + pEditView->SetSelection( aSel ); + } + if( r.tokenType != TokenType::Identifier ) + return; + +// correct variables + if( !aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName ).isEmpty() ) + { + sStr = aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName ); + pEditEngine->ReplaceText( sTextSelection, sStr ); + pEditView->SetSelection( aSel ); + } + else + { + //autocorrect procedures + SbxArray* pArr = rModulWindow.GetSbModule()->GetMethods().get(); + for (sal_uInt32 i = 0; i < pArr->Count(); ++i) + { + if (pArr->Get(i)->GetName().equalsIgnoreAsciiCase(sStr)) + { + sStr = pArr->Get(i)->GetName(); //if found, get the correct case + pEditEngine->ReplaceText( sTextSelection, sStr ); + pEditView->SetSelection( aSel ); + return; + } + } + } +} + +TextSelection EditorWindow::GetLastHighlightPortionTextSelection() const +{//creates a text selection from the highlight portion on the cursor + const sal_uInt32 nLine = GetEditView()->GetSelection().GetStart().GetPara(); + const sal_Int32 nIndex = GetEditView()->GetSelection().GetStart().GetIndex(); + OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified + std::vector<HighlightPortion> aPortions; + aHighlighter.getHighlightPortions( aLine, aPortions ); + + assert(!aPortions.empty()); + HighlightPortion& r = aPortions.back(); + if( static_cast<size_t>(nIndex) != aPortions.size()-1 ) + {//cursor is not standing at the end of the line + for (auto const& portion : aPortions) + { + if( portion.nEnd == nIndex ) + { + r = portion; + break; + } + } + } + + if( aPortions.empty() ) + return TextSelection(); + + std::u16string_view sStr = aLine.subView( r.nBegin, r.nEnd - r.nBegin ); + TextPaM aStart( nLine, r.nBegin ); + TextPaM aEnd( nLine, r.nBegin + sStr.size() ); + return TextSelection( aStart, aEnd ); +} + +void EditorWindow::HandleAutoCloseParen() +{ + TextSelection aSel = GetEditView()->GetSelection(); + const sal_uInt32 nLine = aSel.GetStart().GetPara(); + OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified + + if( aLine.getLength() > 0 && aLine[aSel.GetEnd().GetIndex()-1] != '(' ) + { + GetEditView()->InsertText(")"); + //leave the cursor on its place: inside the parenthesis + TextPaM aEnd(nLine, aSel.GetEnd().GetIndex()); + GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) ); + } +} + +void EditorWindow::HandleAutoCloseDoubleQuotes() +{ + TextSelection aSel = GetEditView()->GetSelection(); + const sal_uInt32 nLine = aSel.GetStart().GetPara(); + OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified + + std::vector<HighlightPortion> aPortions; + aHighlighter.getHighlightPortions( aLine, aPortions ); + + if( aPortions.empty() ) + return; + + if( aLine.getLength() > 0 && !aLine.endsWith("\"") && (aPortions.back().tokenType != TokenType::String) ) + { + GetEditView()->InsertText("\""); + //leave the cursor on its place: inside the two double quotes + TextPaM aEnd(nLine, aSel.GetEnd().GetIndex()); + GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) ); + } +} + +void EditorWindow::HandleProcedureCompletion() +{ + + TextSelection aSel = GetEditView()->GetSelection(); + const sal_uInt32 nLine = aSel.GetStart().GetPara(); + OUString aLine( pEditEngine->GetText( nLine ) ); + + OUString sProcType; + OUString sProcName; + bool bFoundName = GetProcedureName(aLine, sProcType, sProcName); + if (!bFoundName) + return; + + OUString sText("\nEnd "); + aSel = GetEditView()->GetSelection(); + if( sProcType.equalsIgnoreAsciiCase("function") ) + sText += "Function\n"; + if( sProcType.equalsIgnoreAsciiCase("sub") ) + sText += "Sub\n"; + + if( nLine+1 == pEditEngine->GetParagraphCount() ) + { + pEditView->InsertText( sText );//append to the end + GetEditView()->SetSelection(aSel); + } + else + { + for( sal_uInt32 i = nLine+1; i < pEditEngine->GetParagraphCount(); ++i ) + {//searching forward for end token, or another sub/function definition + OUString aCurrLine = pEditEngine->GetText( i ); + std::vector<HighlightPortion> aCurrPortions; + aHighlighter.getHighlightPortions( aCurrLine, aCurrPortions ); + + if( aCurrPortions.size() >= 3 ) + {//at least 3 tokens: (sub|function) whitespace identifier... + HighlightPortion& r = aCurrPortions.front(); + std::u16string_view sStr = aCurrLine.subView(r.nBegin, r.nEnd - r.nBegin); + + if( r.tokenType == TokenType::Keywords ) + { + if( o3tl::equalsIgnoreAsciiCase(sStr, u"sub") || o3tl::equalsIgnoreAsciiCase(sStr, u"function") ) + { + pEditView->InsertText( sText );//append to the end + GetEditView()->SetSelection(aSel); + break; + } + if( o3tl::equalsIgnoreAsciiCase(sStr, u"end") ) + break; + } + } + } + } +} + +bool EditorWindow::GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const +{ + std::vector<HighlightPortion> aPortions; + aHighlighter.getHighlightPortions(rLine, aPortions); + + if( aPortions.empty() ) + return false; + + bool bFoundType = false; + bool bFoundName = false; + + for (auto const& portion : aPortions) + { + std::u16string_view sTokStr = rLine.substr(portion.nBegin, portion.nEnd - portion.nBegin); + + if( portion.tokenType == TokenType::Keywords && ( o3tl::equalsIgnoreAsciiCase(sTokStr, u"sub") + || o3tl::equalsIgnoreAsciiCase(sTokStr, u"function")) ) + { + rProcType = sTokStr; + bFoundType = true; + } + if( portion.tokenType == TokenType::Identifier && bFoundType ) + { + rProcName = sTokStr; + bFoundName = true; + break; + } + } + + if( !bFoundType || !bFoundName ) + return false;// no sub/function keyword or there is no identifier + + return true; + +} + +void EditorWindow::HandleCodeCompletion() +{ + rModulWindow.UpdateModule(); + rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse(aCodeCompleteCache); + TextSelection aSel = GetEditView()->GetSelection(); + const sal_uInt32 nLine = aSel.GetStart().GetPara(); + OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified + std::vector< OUString > aVect; //vector to hold the base variable+methods for the nested reflection + + std::vector<HighlightPortion> aPortions; + aLine = aLine.copy(0, aSel.GetEnd().GetIndex()); + aHighlighter.getHighlightPortions( aLine, aPortions ); + if( aPortions.empty() ) + return; + + //use the syntax highlighter to grab out nested reflection calls, eg. aVar.aMethod("aa").aOtherMethod .. + for( std::vector<HighlightPortion>::reverse_iterator i( + aPortions.rbegin()); + i != aPortions.rend(); ++i) + { + if( i->tokenType == TokenType::Whitespace ) // a whitespace: stop; if there is no ws, it goes to the beginning of the line + break; + if( i->tokenType == TokenType::Identifier || i->tokenType == TokenType::Keywords ) // extract the identifiers(methods, base variable) + /* an example: Dim aLocVar2 as com.sun.star.beans.PropertyValue + * here, aLocVar2.Name, and PropertyValue's Name field is treated as a keyword(?!) + * */ + aVect.insert( aVect.begin(), aLine.copy(i->nBegin, i->nEnd - i->nBegin) ); + } + + if( aVect.empty() )//nothing to do + return; + + OUString sBaseName = aVect[aVect.size()-1];//variable name + OUString sVarType = aCodeCompleteCache.GetVarType( sBaseName ); + + if( !sVarType.isEmpty() && CodeCompleteOptions::IsAutoCorrectOn() ) + {//correct variable name, if autocorrection on + const OUString& sStr = aCodeCompleteCache.GetCorrectCaseVarName( sBaseName, GetActualSubName(nLine) ); + if( !sStr.isEmpty() ) + { + TextPaM aStart(nLine, aSel.GetStart().GetIndex() - sStr.getLength() ); + TextSelection sTextSelection(aStart, TextPaM(nLine, aSel.GetStart().GetIndex())); + pEditEngine->ReplaceText( sTextSelection, sStr ); + pEditView->SetSelection( aSel ); + } + } + + UnoTypeCodeCompletetor aTypeCompletor( aVect, sVarType ); + + if( !aTypeCompletor.CanCodeComplete() ) + return; + + std::vector< OUString > aEntryVect;//entries to be inserted into the list + std::vector< OUString > aFieldVect = aTypeCompletor.GetXIdlClassFields();//fields + aEntryVect.insert(aEntryVect.end(), aFieldVect.begin(), aFieldVect.end() ); + if( CodeCompleteOptions::IsExtendedTypeDeclaration() ) + {// if extended types on, reflect classes, else just the structs (XIdlClass without methods) + std::vector< OUString > aMethVect = aTypeCompletor.GetXIdlClassMethods();//methods + aEntryVect.insert(aEntryVect.end(), aMethVect.begin(), aMethVect.end() ); + } + if( !aEntryVect.empty() ) + SetupAndShowCodeCompleteWnd( aEntryVect, aSel ); +} + +void EditorWindow::SetupAndShowCodeCompleteWnd( const std::vector< OUString >& aEntryVect, TextSelection aSel ) +{ + // clear the listbox + pCodeCompleteWnd->ClearListBox(); + // fill the listbox + for(const auto & l : aEntryVect) + { + pCodeCompleteWnd->InsertEntry( l ); + } + // show it + pCodeCompleteWnd->Show(); + pCodeCompleteWnd->ResizeAndPositionListBox(); + pCodeCompleteWnd->SelectFirstEntry(); + // correct text selection, and set it + ++aSel.GetStart().GetIndex(); + ++aSel.GetEnd().GetIndex(); + pCodeCompleteWnd->SetTextSelection( aSel ); + //give the focus to the EditView + pEditView->GetWindow()->GrabFocus(); +} + +void EditorWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + if (!pEditEngine) // We need it now at latest + CreateEditEngine(); + + pEditView->Paint(rRenderContext, rRect); +} + +void EditorWindow::LoseFocus() +{ + // tdf#114258 wait until the next event loop cycle to do this so it doesn't + // happen during a mouse down/up selection in the treeview whose contents + // this may update + if (!m_nSetSourceInBasicId) + m_nSetSourceInBasicId = Application::PostUserEvent(LINK(this, EditorWindow, SetSourceInBasicHdl)); + Window::LoseFocus(); +} + +IMPL_LINK_NOARG(EditorWindow, SetSourceInBasicHdl, void*, void) +{ + m_nSetSourceInBasicId = nullptr; + SetSourceInBasic(); +} + +void EditorWindow::SetSourceInBasic() +{ + if ( pEditEngine && pEditEngine->IsModified() + && !GetEditView()->IsReadOnly() ) // Added for #i60626, otherwise + // any read only bug in the text engine could lead to a crash later + { + if ( !StarBASIC::IsRunning() ) // Not at runtime! + { + rModulWindow.UpdateModule(); + } + } +} + +// Returns the position of the last character of any of the following +// EOL char combinations: CR, CR/LF, LF, return -1 if no EOL is found +sal_Int32 searchEOL( std::u16string_view rStr, sal_Int32 fromIndex ) +{ + size_t iLF = rStr.find( LINE_SEP, fromIndex ); + if( iLF != std::u16string_view::npos ) + return iLF; + + size_t iCR = rStr.find( LINE_SEP_CR, fromIndex ); + return iCR == std::u16string_view::npos ? -1 : iCR; +} + +void EditorWindow::CreateEditEngine() +{ + if (pEditEngine) + return; + + pEditEngine.reset(new ExtTextEngine); + pEditView.reset(new TextView(pEditEngine.get(), this)); + pEditView->SetAutoIndentMode(true); + pEditEngine->SetUpdateMode(false); + pEditEngine->InsertView(pEditView.get()); + + ImplSetFont(); + + aSyntaxIdle.SetInvokeHandler( LINK( this, EditorWindow, SyntaxTimerHdl ) ); + + bool bWasDoSyntaxHighlight = bDoSyntaxHighlight; + bDoSyntaxHighlight = false; // too slow for large texts... + OUString aOUSource(rModulWindow.GetModule()); + sal_Int32 nLines = 0; + sal_Int32 nIndex = -1; + do + { + nLines++; + nIndex = searchEOL( aOUSource, nIndex+1 ); + } + while (nIndex >= 0); + + // nLines*4: SetText+Formatting+DoHighlight+Formatting + // it could be cut down on one formatting but you would wait even longer + // for the text then if the source code is long... + pProgress.reset(new ProgressInfo(GetShell()->GetViewFrame().GetObjectShell(), + IDEResId(RID_STR_GENERATESOURCE), + nLines * 4)); + setTextEngineText(*pEditEngine, aOUSource); + + pEditView->SetStartDocPos(Point(0, 0)); + pEditView->SetSelection(TextSelection()); + rModulWindow.GetBreakPointWindow().GetCurYOffset() = 0; + rModulWindow.GetLineNumberWindow().GetCurYOffset() = 0; + pEditEngine->SetUpdateMode(true); + rModulWindow.PaintImmediately(); // has only been invalidated at UpdateMode = true + + pEditView->ShowCursor(); + + StartListening(*pEditEngine); + + aSyntaxIdle.Stop(); + bDoSyntaxHighlight = bWasDoSyntaxHighlight; + + for (sal_Int32 nLine = 0; nLine < nLines; nLine++) + aSyntaxLineTable.insert(nLine); + ForceSyntaxTimeout(); + + pProgress.reset(); + + pEditEngine->SetModified( false ); + pEditEngine->EnableUndo( true ); + + InitScrollBars(); + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate(SID_BASICIDE_STAT_POS); + pBindings->Invalidate(SID_BASICIDE_STAT_TITLE); + } + + DBG_ASSERT(rModulWindow.GetBreakPointWindow().GetCurYOffset() == 0, "CreateEditEngine: breakpoints moved?"); + + // set readonly mode for readonly libraries + ScriptDocument aDocument(rModulWindow.GetDocument()); + OUString aOULibName(rModulWindow.GetLibName()); + Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + if (xModLibContainer.is() + && xModLibContainer->hasByName(aOULibName) + && xModLibContainer->isLibraryReadOnly(aOULibName)) + { + rModulWindow.SetReadOnly(true); + } + + if (aDocument.isDocument() && aDocument.isReadOnly()) + rModulWindow.SetReadOnly(true); +} + +void EditorWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + TextHint const* pTextHint = dynamic_cast<TextHint const*>(&rHint); + if (!pTextHint) + return; + + TextHint const& rTextHint = *pTextHint; + if( rTextHint.GetId() == SfxHintId::TextViewScrolled ) + { + rModulWindow.GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() ); + rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() ); + rModulWindow.GetBreakPointWindow().DoScroll + ( rModulWindow.GetBreakPointWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() ); + rModulWindow.GetLineNumberWindow().DoScroll + ( rModulWindow.GetLineNumberWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() ); + } + else if( rTextHint.GetId() == SfxHintId::TextHeightChanged ) + { + if ( pEditView->GetStartDocPos().Y() ) + { + tools::Long nOutHeight = GetOutputSizePixel().Height(); + tools::Long nTextHeight = pEditEngine->GetTextHeight(); + if ( nTextHeight < nOutHeight ) + pEditView->Scroll( 0, pEditView->GetStartDocPos().Y() ); + + rModulWindow.GetLineNumberWindow().Invalidate(); + } + + SetScrollBarRanges(); + } + else if( rTextHint.GetId() == SfxHintId::TextFormatted ) + { + + const tools::Long nWidth = pEditEngine->CalcTextWidth(); + if ( nWidth != nCurTextWidth ) + { + nCurTextWidth = nWidth; + rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1) ); + rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() ); + } + tools::Long nPrevTextWidth = nCurTextWidth; + nCurTextWidth = pEditEngine->CalcTextWidth(); + if ( nCurTextWidth != nPrevTextWidth ) + SetScrollBarRanges(); + } + else if( rTextHint.GetId() == SfxHintId::TextParaInserted ) + { + ParagraphInsertedDeleted( rTextHint.GetValue(), true ); + DoDelayedSyntaxHighlight( rTextHint.GetValue() ); + } + else if( rTextHint.GetId() == SfxHintId::TextParaRemoved ) + { + ParagraphInsertedDeleted( rTextHint.GetValue(), false ); + } + else if( rTextHint.GetId() == SfxHintId::TextParaContentChanged ) + { + DoDelayedSyntaxHighlight( rTextHint.GetValue() ); + } + else if( rTextHint.GetId() == SfxHintId::TextViewSelectionChanged ) + { + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_CUT ); + pBindings->Invalidate( SID_COPY ); + } + } +} + +OUString EditorWindow::GetActualSubName( sal_uInt32 nLine ) +{ + SbxArrayRef pMethods = rModulWindow.GetSbModule()->GetMethods(); + for (sal_uInt32 i = 0; i < pMethods->Count(); i++) + { + SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i)); + if( pMeth ) + { + sal_uInt16 l1,l2; + pMeth->GetLineRange(l1,l2); + if( (l1 <= nLine+1) && (nLine+1 <= l2) ) + { + return pMeth->GetName(); + } + } + } + return OUString(); +} + +void EditorWindow::SetScrollBarRanges() +{ + // extra method, not InitScrollBars, because for EditEngine events too + if ( !pEditEngine ) + return; + + rModulWindow.GetEditVScrollBar().SetRange( Range( 0, pEditEngine->GetTextHeight()-1 ) ); + rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1 ) ); +} + +void EditorWindow::InitScrollBars() +{ + if (!pEditEngine) + return; + + SetScrollBarRanges(); + Size aOutSz(GetOutputSizePixel()); + rModulWindow.GetEditVScrollBar().SetVisibleSize(aOutSz.Height()); + rModulWindow.GetEditVScrollBar().SetPageSize(aOutSz.Height() * 8 / 10); + rModulWindow.GetEditVScrollBar().SetLineSize(GetTextHeight()); + rModulWindow.GetEditVScrollBar().SetThumbPos(pEditView->GetStartDocPos().Y()); + rModulWindow.GetEditVScrollBar().Show(); + + rModulWindow.GetEditHScrollBar().SetVisibleSize(aOutSz.Width()); + rModulWindow.GetEditHScrollBar().SetPageSize(aOutSz.Width() * 8 / 10); + rModulWindow.GetEditHScrollBar().SetLineSize(GetTextWidth( "x" )); + rModulWindow.GetEditHScrollBar().SetThumbPos(pEditView->GetStartDocPos().X()); + rModulWindow.GetEditHScrollBar().Show(); +} + +void EditorWindow::ImpDoHighlight( sal_uInt32 nLine ) +{ + if ( !bDoSyntaxHighlight ) + return; + + OUString aLine( pEditEngine->GetText( nLine ) ); + bool const bWasModified = pEditEngine->IsModified(); + pEditEngine->RemoveAttribs( nLine ); + std::vector<HighlightPortion> aPortions; + aHighlighter.getHighlightPortions( aLine, aPortions ); + + for (auto const& portion : aPortions) + { + Color const aColor = rModulWindow.GetLayout().GetSyntaxColor(portion.tokenType); + pEditEngine->SetAttrib(TextAttribFontColor(aColor), nLine, portion.nBegin, portion.nEnd); + } + + pEditEngine->SetModified(bWasModified); +} + +void EditorWindow::ChangeFontColor( Color aColor ) +{ + if (pEditEngine) + { + vcl::Font aFont(pEditEngine->GetFont()); + aFont.SetColor(aColor); + pEditEngine->SetFont(aFont); + } +} + +void EditorWindow::UpdateSyntaxHighlighting () +{ + const sal_uInt32 nCount = pEditEngine->GetParagraphCount(); + for (sal_uInt32 i = 0; i < nCount; ++i) + DoDelayedSyntaxHighlight(i); +} + +void EditorWindow::ImplSetFont() +{ + // Get default font name and height defined in the Options dialog + OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString())); + if (sFontName.isEmpty()) + { + vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED, + Application::GetSettings().GetUILanguageTag().getLanguageType(), + GetDefaultFontFlags::NONE, GetOutDev())); + sFontName = aTmpFont.GetFamilyName(); + } + sal_uInt16 nDefaultFontHeight = officecfg::Office::Common::Font::SourceViewFont::FontHeight::get(); + + // Calculate font size considering zoom level + sal_uInt16 nNewFontHeight = nDefaultFontHeight * (static_cast<float>(nCurrentZoomLevel) / 100); + Size aFontSize(0, nNewFontHeight); + + vcl::Font aFont(sFontName, aFontSize); + aFont.SetColor(rModulWindow.GetLayout().GetFontColor()); + SetPointFont(*GetOutDev(), aFont); // FIXME RenderContext + aFont = GetFont(); + + rModulWindow.GetBreakPointWindow().SetFont(aFont); + rModulWindow.GetLineNumberWindow().SetFont(aFont); + rModulWindow.Invalidate(); + + if (pEditEngine) + { + bool const bModified = pEditEngine->IsModified(); + pEditEngine->SetFont(aFont); + pEditEngine->SetModified(bModified); + } + + // Update controls + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM ); + pBindings->Invalidate( SID_ATTR_ZOOMSLIDER ); + } +} + +void EditorWindow::SetEditorZoomLevel(sal_uInt16 nNewZoomLevel) +{ + if (nCurrentZoomLevel == nNewZoomLevel) + return; + + if (nNewZoomLevel < MIN_ZOOM_LEVEL || nNewZoomLevel > MAX_ZOOM_LEVEL) + return; + + nCurrentZoomLevel = nNewZoomLevel; + ImplSetFont(); +} + +void EditorWindow::DoSyntaxHighlight( sal_uInt32 nPara ) +{ + // because of the DelayedSyntaxHighlight it's possible + // that this line does not exist anymore! + if ( nPara < pEditEngine->GetParagraphCount() ) + { + // unfortunately I'm not sure that exactly this line does Modified()... + if ( pProgress ) + pProgress->StepProgress(); + ImpDoHighlight( nPara ); + } +} + +void EditorWindow::DoDelayedSyntaxHighlight( sal_uInt32 nPara ) +{ + // line is only added to list, processed in TimerHdl + // => don't manipulate breaks while EditEngine is formatting + if ( pProgress ) + pProgress->StepProgress(); + + if ( !bHighlighting && bDoSyntaxHighlight ) + { + if ( bDelayHighlight ) + { + aSyntaxLineTable.insert( nPara ); + aSyntaxIdle.Start(); + } + else + DoSyntaxHighlight( nPara ); + } +} + +IMPL_LINK_NOARG(EditorWindow, SyntaxTimerHdl, Timer *, void) +{ + DBG_ASSERT( pEditView, "Not yet a View, but Syntax-Highlight?!" ); + + bool const bWasModified = pEditEngine->IsModified(); + //pEditEngine->SetUpdateMode(false); + + bHighlighting = true; + for (auto const& syntaxLine : aSyntaxLineTable) + { + DoSyntaxHighlight(syntaxLine); + } + + // #i45572# + if ( pEditView ) + pEditView->ShowCursor( false ); + + pEditEngine->SetModified( bWasModified ); + + aSyntaxLineTable.clear(); + bHighlighting = false; +} + +void EditorWindow::ParagraphInsertedDeleted( sal_uInt32 nPara, bool bInserted ) +{ + if ( pProgress ) + pProgress->StepProgress(); + + if ( !bInserted && ( nPara == TEXT_PARA_ALL ) ) + { + rModulWindow.GetBreakPoints().reset(); + rModulWindow.GetBreakPointWindow().Invalidate(); + rModulWindow.GetLineNumberWindow().Invalidate(); + } + else + { + rModulWindow.GetBreakPoints().AdjustBreakPoints( static_cast<sal_uInt16>(nPara)+1, bInserted ); + + tools::Long nLineHeight = GetTextHeight(); + Size aSz = rModulWindow.GetBreakPointWindow().GetOutDev()->GetOutputSize(); + tools::Rectangle aInvRect( Point( 0, 0 ), aSz ); + tools::Long nY = nPara*nLineHeight - rModulWindow.GetBreakPointWindow().GetCurYOffset(); + aInvRect.SetTop( nY ); + rModulWindow.GetBreakPointWindow().Invalidate( aInvRect ); + + Size aLnSz(rModulWindow.GetLineNumberWindow().GetWidth(), + GetOutputSizePixel().Height() - 2 * DWBORDER); + rModulWindow.GetLineNumberWindow().SetPosSizePixel(Point(DWBORDER + 19, DWBORDER), aLnSz); + rModulWindow.GetLineNumberWindow().Invalidate(); + } +} + +void EditorWindow::CreateProgress( const OUString& rText, sal_uInt32 nRange ) +{ + DBG_ASSERT( !pProgress, "ProgressInfo exists already" ); + pProgress.reset(new ProgressInfo( + GetShell()->GetViewFrame().GetObjectShell(), + rText, + nRange + )); +} + +void EditorWindow::DestroyProgress() +{ + pProgress.reset(); +} + +void EditorWindow::ForceSyntaxTimeout() +{ + aSyntaxIdle.Stop(); + aSyntaxIdle.Invoke(); +} + +FactoryFunction EditorWindow::GetUITestFactory() const +{ + return EditorWindowUIObject::create; +} + + +// BreakPointWindow + +BreakPointWindow::BreakPointWindow (vcl::Window* pParent, ModulWindow* pModulWindow) + : Window(pParent, WB_BORDER) + , rModulWindow(*pModulWindow) + , nCurYOffset(0) // memorize nCurYOffset and not take it from EditEngine + , nMarkerPos(NoMarker) + , bErrorMarker(false) +{ + setBackgroundColor(GetSettings().GetStyleSettings().GetFieldColor()); + SetHelpId(HID_BASICIDE_BREAKPOINTWINDOW); +} + +void BreakPointWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + if (SyncYOffset()) + return; + + Size const aOutSz = rRenderContext.GetOutputSize(); + tools::Long const nLineHeight = rRenderContext.GetTextHeight(); + + Image const aBrk[2] = + { + GetImage(RID_BMP_BRKDISABLED), + GetImage(RID_BMP_BRKENABLED) + }; + + Size const aBmpSz = rRenderContext.PixelToLogic(aBrk[1].GetSizePixel()); + Point const aBmpOff((aOutSz.Width() - aBmpSz.Width()) / 2, + (nLineHeight - aBmpSz.Height()) / 2); + + for (size_t i = 0, n = GetBreakPoints().size(); i < n; ++i) + { + BreakPoint& rBrk = GetBreakPoints().at(i); + sal_uInt16 const nLine = rBrk.nLine - 1; + size_t const nY = nLine*nLineHeight - nCurYOffset; + rRenderContext.DrawImage(Point(0, nY) + aBmpOff, aBrk[rBrk.bEnabled]); + } + + ShowMarker(rRenderContext); +} + +void BreakPointWindow::ShowMarker(vcl::RenderContext& rRenderContext) +{ + if (nMarkerPos == NoMarker) + return; + + Size const aOutSz = GetOutDev()->GetOutputSize(); + tools::Long const nLineHeight = GetTextHeight(); + + Image aMarker = GetImage(bErrorMarker ? RID_BMP_ERRORMARKER : RID_BMP_STEPMARKER); + + Size aMarkerSz(aMarker.GetSizePixel()); + aMarkerSz = rRenderContext.PixelToLogic(aMarkerSz); + Point aMarkerOff(0, 0); + aMarkerOff.setX( (aOutSz.Width() - aMarkerSz.Width()) / 2 ); + aMarkerOff.setY( (nLineHeight - aMarkerSz.Height()) / 2 ); + + tools::Long nY = nMarkerPos * nLineHeight - nCurYOffset; + Point aPos(0, nY); + aPos += aMarkerOff; + + rRenderContext.DrawImage(aPos, aMarker); +} + +void BreakPointWindow::DoScroll( tools::Long nVertScroll ) +{ + nCurYOffset -= nVertScroll; + Window::Scroll( 0, nVertScroll ); +} + +void BreakPointWindow::SetMarkerPos( sal_uInt16 nLine, bool bError ) +{ + if ( SyncYOffset() ) + PaintImmediately(); + + nMarkerPos = nLine; + bErrorMarker = bError; + Invalidate(); +} + +void BreakPointWindow::SetNoMarker () +{ + SetMarkerPos(NoMarker); +} + +BreakPoint* BreakPointWindow::FindBreakPoint( const Point& rMousePos ) +{ + size_t nLineHeight = GetTextHeight(); + nLineHeight = nLineHeight > 0 ? nLineHeight : 1; + size_t nYPos = rMousePos.Y() + nCurYOffset; + + for ( size_t i = 0, n = GetBreakPoints().size(); i < n ; ++i ) + { + BreakPoint& rBrk = GetBreakPoints().at( i ); + sal_uInt16 nLine = rBrk.nLine-1; + size_t nY = nLine*nLineHeight; + if ( ( nYPos > nY ) && ( nYPos < ( nY + nLineHeight ) ) ) + return &rBrk; + } + return nullptr; +} + +void BreakPointWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( rMEvt.GetClicks() == 2 ) + { + Point aMousePos( PixelToLogic( rMEvt.GetPosPixel() ) ); + tools::Long nLineHeight = GetTextHeight(); + if(nLineHeight) + { + tools::Long nYPos = aMousePos.Y() + nCurYOffset; + tools::Long nLine = nYPos / nLineHeight + 1; + rModulWindow.ToggleBreakPoint( static_cast<sal_uInt16>(nLine) ); + Invalidate(); + } + } +} + +void BreakPointWindow::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() != CommandEventId::ContextMenu ) + return; + + Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) ); + tools::Rectangle aRect(aPos, Size(1, 1)); + weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect); + + std::unique_ptr<weld::Builder> xUIBuilder(Application::CreateBuilder(pPopupParent, "modules/BasicIDE/ui/breakpointmenus.ui")); + + Point aEventPos( PixelToLogic( aPos ) ); + BreakPoint* pBrk = rCEvt.IsMouseEvent() ? FindBreakPoint( aEventPos ) : nullptr; + if ( pBrk ) + { + // test if break point is enabled... + std::unique_ptr<weld::Menu> xBrkPropMenu = xUIBuilder->weld_menu("breakmenu"); + xBrkPropMenu->set_active("active", pBrk->bEnabled); + OUString sCommand = xBrkPropMenu->popup_at_rect(pPopupParent, aRect); + if (sCommand == "active") + { + pBrk->bEnabled = !pBrk->bEnabled; + rModulWindow.UpdateBreakPoint( *pBrk ); + Invalidate(); + } + else if (sCommand == "properties") + { + BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints()); + aBrkDlg.SetCurrentBreakPoint( *pBrk ); + aBrkDlg.run(); + Invalidate(); + } + } + else + { + std::unique_ptr<weld::Menu> xBrkListMenu = xUIBuilder->weld_menu("breaklistmenu"); + OUString sCommand = xBrkListMenu->popup_at_rect(pPopupParent, aRect); + if (sCommand == "manage") + { + BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints()); + aBrkDlg.run(); + Invalidate(); + } + } +} + +bool BreakPointWindow::SyncYOffset() +{ + TextView* pView = rModulWindow.GetEditView(); + if ( pView ) + { + tools::Long nViewYOffset = pView->GetStartDocPos().Y(); + if ( nCurYOffset != nViewYOffset ) + { + nCurYOffset = nViewYOffset; + Invalidate(); + return true; + } + } + return false; +} + +// virtual +void BreakPointWindow::DataChanged(DataChangedEvent const & rDCEvt) +{ + Window::DataChanged(rDCEvt); + if (rDCEvt.GetType() == DataChangedEventType::SETTINGS + && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) + { + Color aColor(GetSettings().GetStyleSettings().GetFieldColor()); + const AllSettings* pOldSettings = rDCEvt.GetOldSettings(); + if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFieldColor()) + { + setBackgroundColor(aColor); + Invalidate(); + } + } +} + +void BreakPointWindow::setBackgroundColor(Color aColor) +{ + SetBackground(Wallpaper(aColor)); +} + +namespace { + +struct WatchItem +{ + OUString maName; + OUString maDisplayName; + SbxObjectRef mpObject; + std::vector<OUString> maMemberList; + + SbxDimArrayRef mpArray; + int nDimLevel; // 0 = Root + int nDimCount; + std::vector<sal_Int32> vIndices; + + WatchItem* mpArrayParentItem; + + explicit WatchItem (OUString aName): + maName(std::move(aName)), + nDimLevel(0), + nDimCount(0), + mpArrayParentItem(nullptr) + { } + + void clearWatchItem () + { + maMemberList.clear(); + } + + WatchItem* GetRootItem(); + SbxDimArray* GetRootArray(); +}; + +} + +WatchWindow::WatchWindow(Layout* pParent) + : DockingWindow(pParent, "modules/BasicIDE/ui/dockingwatch.ui", "DockingWatch") + , m_nUpdateWatchesId(nullptr) +{ + m_xTitleArea = m_xBuilder->weld_container("titlearea"); + + nVirtToolBoxHeight = m_xTitleArea->get_preferred_size().Height(); + + m_xTitle = m_xBuilder->weld_label("title"); + m_xTitle->set_label(IDEResId(RID_STR_REMOVEWATCH)); + + m_xEdit = m_xBuilder->weld_entry("edit"); + m_xRemoveWatchButton = m_xBuilder->weld_button("remove"); + m_xTreeListBox = m_xBuilder->weld_tree_view("treeview"); + + m_xEdit->set_accessible_name(IDEResId(RID_STR_WATCHNAME)); + m_xEdit->set_help_id(HID_BASICIDE_WATCHWINDOW_EDIT); + m_xEdit->set_size_request(LogicToPixel(Size(80, 0), MapMode(MapUnit::MapAppFont)).Width(), -1); + m_xEdit->connect_activate(LINK( this, WatchWindow, ActivateHdl)); + m_xEdit->connect_key_press(LINK( this, WatchWindow, KeyInputHdl)); + m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_WATCHNAME)); + + m_xRemoveWatchButton->set_sensitive(false); + m_xRemoveWatchButton->connect_clicked(LINK( this, WatchWindow, ButtonHdl)); + m_xRemoveWatchButton->set_help_id(HID_BASICIDE_REMOVEWATCH); + m_xRemoveWatchButton->set_tooltip_text(IDEResId(RID_STR_REMOVEWATCHTIP)); + + m_xTreeListBox->set_help_id(HID_BASICIDE_WATCHWINDOW_LIST); + m_xTreeListBox->connect_editing(LINK(this, WatchWindow, EditingEntryHdl), + LINK(this, WatchWindow, EditedEntryHdl)); + m_xTreeListBox->connect_changed( LINK( this, WatchWindow, TreeListHdl ) ); + m_xTreeListBox->connect_expanding(LINK(this, WatchWindow, RequestingChildrenHdl)); + + // VarTabWidth, ValueTabWidth, TypeTabWidth + std::vector<int> aWidths { 220, 100, 1250 }; + std::vector<bool> aEditables { false, true, false }; + m_xTreeListBox->set_column_fixed_widths(aWidths); + m_xTreeListBox->set_column_editables(aEditables); + + SetText(IDEResId(RID_STR_WATCHNAME)); + + SetHelpId( HID_BASICIDE_WATCHWINDOW ); + + // make watch window keyboard accessible + GetSystemWindow()->GetTaskPaneList()->AddWindow( this ); +} + +WatchWindow::~WatchWindow() +{ + disposeOnce(); +} + +void WatchWindow::dispose() +{ + if (m_nUpdateWatchesId) + { + Application::RemoveUserEvent(m_nUpdateWatchesId); + m_nUpdateWatchesId = nullptr; + } + + // Destroy user data + m_xTreeListBox->all_foreach([this](weld::TreeIter& rEntry){ + WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry)); + delete pItem; + return false; + }); + + m_xTitle.reset(); + m_xEdit.reset(); + m_xRemoveWatchButton.reset(); + m_xTitleArea.reset(); + m_xTreeListBox.reset(); + GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this ); + DockingWindow::dispose(); +} + +void WatchWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + lcl_DrawIDEWindowFrame(this, rRenderContext); +} + +void WatchWindow::Resize() +{ + Size aSz = GetOutputSizePixel(); + Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER); + + if ( aBoxSz.Width() < 4 ) + aBoxSz.setWidth( 0 ); + if ( aBoxSz.Height() < 4 ) + aBoxSz.setHeight( 0 ); + + m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz); + + Invalidate(); +} + +WatchItem* WatchItem::GetRootItem() +{ + WatchItem* pItem = mpArrayParentItem; + while( pItem ) + { + if( pItem->mpArray.is() ) + break; + pItem = pItem->mpArrayParentItem; + } + return pItem; +} + +SbxDimArray* WatchItem::GetRootArray() +{ + WatchItem* pRootItem = GetRootItem(); + SbxDimArray* pRet = nullptr; + if( pRootItem ) + pRet = pRootItem->mpArray.get(); + return pRet; +} + +void WatchWindow::AddWatch( const OUString& rVName ) +{ + OUString aVar, aIndex; + lcl_SeparateNameAndIndex( rVName, aVar, aIndex ); + WatchItem* pWatchItem = new WatchItem(aVar); + + OUString sId(weld::toId(pWatchItem)); + std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator(); + m_xTreeListBox->insert(nullptr, -1, &aVar, &sId, nullptr, nullptr, false, xRet.get()); + m_xTreeListBox->set_text(*xRet, "", 1); + m_xTreeListBox->set_text(*xRet, "", 2); + + m_xTreeListBox->set_cursor(*xRet); + m_xTreeListBox->select(*xRet); + m_xTreeListBox->scroll_to_row(*xRet); + m_xRemoveWatchButton->set_sensitive(true); + + UpdateWatches(false); +} + +void WatchWindow::RemoveSelectedWatch() +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeListBox->make_iterator(); + bool bEntry = m_xTreeListBox->get_cursor(xEntry.get()); + if (bEntry) + { + m_xTreeListBox->remove(*xEntry); + bEntry = m_xTreeListBox->get_cursor(xEntry.get()); + if (bEntry) + m_xEdit->set_text(weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xEntry))->maName); + else + m_xEdit->set_text(OUString()); + if ( !m_xTreeListBox->n_children() ) + m_xRemoveWatchButton->set_sensitive(false); + } +} + +IMPL_STATIC_LINK_NOARG(WatchWindow, ButtonHdl, weld::Button&, void) +{ + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute(SID_BASICIDE_REMOVEWATCH); +} + +IMPL_LINK_NOARG(WatchWindow, TreeListHdl, weld::TreeView&, void) +{ + std::unique_ptr<weld::TreeIter> xCurEntry = m_xTreeListBox->make_iterator(); + bool bCurEntry = m_xTreeListBox->get_cursor(xCurEntry.get()); + if (!bCurEntry) + return; + WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xCurEntry)); + if (!pItem) + return; + m_xEdit->set_text(pItem->maName); +} + +IMPL_LINK_NOARG(WatchWindow, ActivateHdl, weld::Entry&, bool) +{ + OUString aCurText(m_xEdit->get_text()); + if (!aCurText.isEmpty()) + { + AddWatch(aCurText); + m_xEdit->select_region(0, -1); + } + return true; +} + +IMPL_LINK(WatchWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + + sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode(); + if (nKeyCode == KEY_ESCAPE) + { + m_xEdit->set_text(OUString()); + bHandled = true; + } + + return bHandled; +} + +// StackWindow +StackWindow::StackWindow(Layout* pParent) + : DockingWindow(pParent, "modules/BasicIDE/ui/dockingstack.ui", "DockingStack") +{ + m_xTitle = m_xBuilder->weld_label("title"); + m_xTitle->set_label(IDEResId(RID_STR_STACK)); + + m_xTitle->set_size_request(-1, nVirtToolBoxHeight); // so the two title areas are the same height + + m_xTreeListBox = m_xBuilder->weld_tree_view("stack"); + + m_xTreeListBox->set_help_id(HID_BASICIDE_STACKWINDOW_LIST); + m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_STACKNAME)); + m_xTreeListBox->set_selection_mode(SelectionMode::NONE); + m_xTreeListBox->append_text(OUString()); + + SetText(IDEResId(RID_STR_STACKNAME)); + + SetHelpId( HID_BASICIDE_STACKWINDOW ); + + // make stack window keyboard accessible + GetSystemWindow()->GetTaskPaneList()->AddWindow( this ); +} + +StackWindow::~StackWindow() +{ + disposeOnce(); +} + +void StackWindow::dispose() +{ + GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this ); + m_xTitle.reset(); + m_xTreeListBox.reset(); + DockingWindow::dispose(); +} + +void StackWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + lcl_DrawIDEWindowFrame(this, rRenderContext); +} + +void StackWindow::Resize() +{ + Size aSz = GetOutputSizePixel(); + Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER); + + if ( aBoxSz.Width() < 4 ) + aBoxSz.setWidth( 0 ); + if ( aBoxSz.Height() < 4 ) + aBoxSz.setHeight( 0 ); + + m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz); + + Invalidate(); +} + +void StackWindow::UpdateCalls() +{ + m_xTreeListBox->freeze(); + m_xTreeListBox->clear(); + + if (StarBASIC::IsRunning()) + { + ErrCode eOld = SbxBase::GetError(); + m_xTreeListBox->set_selection_mode(SelectionMode::Single); + + sal_Int32 nScope = 0; + SbMethod* pMethod = StarBASIC::GetActiveMethod( nScope ); + while ( pMethod ) + { + OUStringBuffer aEntry( OUString::number(nScope )); + if ( aEntry.getLength() < 2 ) + aEntry.insert(0, " "); + aEntry.append(": " + pMethod->GetName()); + SbxArray* pParams = pMethod->GetParameters(); + SbxInfo* pInfo = pMethod->GetInfo(); + if ( pParams ) + { + aEntry.append("("); + // 0 is the sub's name... + for (sal_uInt32 nParam = 1; nParam < pParams->Count(); nParam++) + { + SbxVariable* pVar = pParams->Get(nParam); + assert(pVar && "Parameter?!"); + if ( !pVar->GetName().isEmpty() ) + { + aEntry.append(pVar->GetName()); + } + else if ( pInfo ) + { + assert(nParam <= std::numeric_limits<sal_uInt16>::max()); + const SbxParamInfo* pParam = pInfo->GetParam( sal::static_int_cast<sal_uInt16>(nParam) ); + if ( pParam ) + { + aEntry.append(pParam->aName); + } + } + aEntry.append("="); + SbxDataType eType = pVar->GetType(); + if( eType & SbxARRAY ) + { + aEntry.append("..."); + } + else if( eType != SbxOBJECT ) + { + aEntry.append(pVar->GetOUString()); + } + if (nParam < (pParams->Count() - 1)) + { + aEntry.append(", "); + } + } + aEntry.append(")"); + } + m_xTreeListBox->append_text(aEntry.makeStringAndClear()); + nScope++; + pMethod = StarBASIC::GetActiveMethod( nScope ); + } + + SbxBase::ResetError(); + if( eOld != ERRCODE_NONE ) + SbxBase::SetError( eOld ); + } + else + { + m_xTreeListBox->set_selection_mode(SelectionMode::NONE); + m_xTreeListBox->append_text(OUString()); + } + + m_xTreeListBox->thaw(); +} + +ComplexEditorWindow::ComplexEditorWindow( ModulWindow* pParent ) : + Window( pParent, WB_3DLOOK | WB_CLIPCHILDREN ), + aBrkWindow(VclPtr<BreakPointWindow>::Create(this, pParent)), + aLineNumberWindow(VclPtr<LineNumberWindow>::Create(this, pParent)), + aEdtWindow(VclPtr<EditorWindow>::Create(this, pParent)), + aEWVScrollBar(VclPtr<ScrollAdaptor>::Create(this, false)), + aEWHScrollBar(VclPtr<ScrollAdaptor>::Create(this, true)) +{ + aEdtWindow->Show(); + aBrkWindow->Show(); + + aEWVScrollBar->SetLineSize(nScrollLine); + aEWVScrollBar->SetPageSize(nScrollPage); + aEWVScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) ); + aEWVScrollBar->Show(); + + aEWHScrollBar->SetLineSize(nScrollLine); + aEWHScrollBar->SetPageSize(nScrollPage); + aEWHScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) ); + aEWHScrollBar->Show(); +} + +ComplexEditorWindow::~ComplexEditorWindow() +{ + disposeOnce(); +} + +void ComplexEditorWindow::dispose() +{ + aBrkWindow.disposeAndClear(); + aLineNumberWindow.disposeAndClear(); + aEdtWindow.disposeAndClear(); + aEWVScrollBar.disposeAndClear(); + aEWHScrollBar.disposeAndClear(); + vcl::Window::dispose(); +} + +void ComplexEditorWindow::Resize() +{ + Size aOutSz = GetOutputSizePixel(); + Size aSz(aOutSz); + aSz.AdjustWidth( -(2*DWBORDER) ); + aSz.AdjustHeight( -(2*DWBORDER) ); + tools::Long nBrkWidth = 20; + tools::Long nSBWidth = aEWVScrollBar->GetSizePixel().Width(); + tools::Long nSBHeight = aEWHScrollBar->GetSizePixel().Height(); + + Size aBrkSz(nBrkWidth, aSz.Height() - nSBHeight); + + if (aLineNumberWindow->IsVisible()) + { + Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height() - nSBHeight); + Size aEWSz(aSz.Width() - nBrkWidth - aLineNumberWindow->GetWidth() - nSBWidth, aSz.Height() - nSBHeight); + aBrkWindow->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBrkSz); + aLineNumberWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aLnSz); + aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth + aLnSz.Width(), DWBORDER), aEWSz); + } + else + { + Size aEWSz(aSz.Width() - nBrkWidth - nSBWidth, aSz.Height() - nSBHeight); + aBrkWindow->SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz ); + aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aEWSz); + } + + aEWVScrollBar->SetPosSizePixel(Point(aOutSz.Width() - DWBORDER - nSBWidth, DWBORDER), + Size(nSBWidth, aSz.Height() - nSBHeight)); + aEWHScrollBar->SetPosSizePixel(Point(DWBORDER, aOutSz.Height() - DWBORDER - nSBHeight), + Size(aSz.Width() - nSBWidth, nSBHeight)); +} + +IMPL_LINK_NOARG(ComplexEditorWindow, ScrollHdl, weld::Scrollbar&, void) +{ + if (aEdtWindow->GetEditView()) + { + tools::Long nXDiff = aEdtWindow->GetEditView()->GetStartDocPos().X() - aEWHScrollBar->GetThumbPos(); + tools::Long nYDiff = aEdtWindow->GetEditView()->GetStartDocPos().Y() - aEWVScrollBar->GetThumbPos(); + aEdtWindow->GetEditView()->Scroll(nXDiff, nYDiff); + aBrkWindow->DoScroll( nYDiff ); + aLineNumberWindow->DoScroll( nYDiff ); + aEdtWindow->GetEditView()->ShowCursor(false); + aEWVScrollBar->SetThumbPos( aEdtWindow->GetEditView()->GetStartDocPos().Y() ); + } +} + +void ComplexEditorWindow::DataChanged(DataChangedEvent const & rDCEvt) +{ + Window::DataChanged(rDCEvt); + if (rDCEvt.GetType() == DataChangedEventType::SETTINGS + && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) + { + Color aColor(GetSettings().GetStyleSettings().GetFaceColor()); + const AllSettings* pOldSettings = rDCEvt.GetOldSettings(); + if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFaceColor()) + { + SetBackground(Wallpaper(aColor)); + Invalidate(); + } + } +} + +void ComplexEditorWindow::SetLineNumberDisplay(bool b) +{ + aLineNumberWindow->Show(b); + Resize(); +} + +uno::Reference< awt::XVclWindowPeer > +EditorWindow::GetComponentInterface(bool bCreate) +{ + uno::Reference< awt::XVclWindowPeer > xPeer( + Window::GetComponentInterface(false)); + if (!xPeer.is() && bCreate) + { + // Make sure edit engine and view are available: + if (!pEditEngine) + CreateEditEngine(); + + xPeer = createTextWindowPeer(*GetEditView()); + SetComponentInterface(xPeer); + } + return xPeer; +} + +static sal_uInt32 getCorrectedPropCount(SbxArray* p) +{ + sal_uInt32 nPropCount = p->Count(); + if (nPropCount >= 3 && p->Get(nPropCount - 1)->GetName().equalsIgnoreAsciiCase("Dbg_Methods") + && p->Get(nPropCount - 2)->GetName().equalsIgnoreAsciiCase("Dbg_Properties") + && p->Get(nPropCount - 3)->GetName().equalsIgnoreAsciiCase("Dbg_SupportedInterfaces")) + { + nPropCount -= 3; + } + return nPropCount; +} + +IMPL_LINK(WatchWindow, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool) +{ + if( !StarBASIC::IsRunning() ) + return true; + + if (m_xTreeListBox->iter_has_child(rParent)) + return true; + + WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rParent)); + std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator(); + + SbxDimArray* pArray = pItem->mpArray.get(); + SbxDimArray* pRootArray = pItem->GetRootArray(); + bool bArrayIsRootArray = false; + if( !pArray && pRootArray ) + { + pArray = pRootArray; + bArrayIsRootArray = true; + } + + SbxObject* pObj = pItem->mpObject.get(); + if( pObj ) + { + createAllObjectProperties( pObj ); + SbxArray* pProps = pObj->GetProperties(); + const sal_uInt32 nPropCount = getCorrectedPropCount(pProps); + pItem->maMemberList.reserve(nPropCount); + + for( sal_uInt32 i = 0 ; i < nPropCount ; ++i ) + { + SbxVariable* pVar = pProps->Get(i); + + pItem->maMemberList.push_back(pVar->GetName()); + OUString const& rName = pItem->maMemberList.back(); + + WatchItem* pWatchItem = new WatchItem(rName); + OUString sId(weld::toId(pWatchItem)); + + m_xTreeListBox->insert(&rParent, -1, &rName, &sId, nullptr, nullptr, false, xRet.get()); + m_xTreeListBox->set_text(*xRet, "", 1); + m_xTreeListBox->set_text(*xRet, "", 2); + } + + if (nPropCount > 0 && !m_nUpdateWatchesId) + { + m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches)); + } + } + else if( pArray ) + { + sal_uInt16 nElementCount = 0; + + // Loop through indices of current level + int nParentLevel = bArrayIsRootArray ? pItem->nDimLevel : 0; + int nThisLevel = nParentLevel + 1; + sal_Int32 nMin, nMax; + if (pArray->GetDim(nThisLevel, nMin, nMax)) + { + for (sal_Int32 i = nMin; i <= nMax; i++) + { + WatchItem* pChildItem = new WatchItem(pItem->maName); + + // Copy data and create name + + OUStringBuffer aIndexStr = "("; + pChildItem->mpArrayParentItem = pItem; + pChildItem->nDimLevel = nThisLevel; + pChildItem->nDimCount = pItem->nDimCount; + pChildItem->vIndices.resize(pChildItem->nDimCount); + sal_Int32 j; + for (j = 0; j < nParentLevel; j++) + { + sal_Int32 n = pChildItem->vIndices[j] = pItem->vIndices[j]; + aIndexStr.append( OUString::number(n) + "," ); + } + pChildItem->vIndices[nParentLevel] = i; + aIndexStr.append( OUString::number(i) + ")" ); + + OUString aDisplayName; + WatchItem* pArrayRootItem = pChildItem->GetRootItem(); + if (pArrayRootItem && pArrayRootItem->mpArrayParentItem) + aDisplayName = pItem->maDisplayName; + else + aDisplayName = pItem->maName; + aDisplayName += aIndexStr; + pChildItem->maDisplayName = aDisplayName; + + OUString sId(weld::toId(pChildItem)); + + m_xTreeListBox->insert(&rParent, -1, &aDisplayName, &sId, nullptr, nullptr, false, + xRet.get()); + m_xTreeListBox->set_text(*xRet, "", 1); + m_xTreeListBox->set_text(*xRet, "", 2); + + nElementCount++; + } + } + if (nElementCount > 0 && !m_nUpdateWatchesId) + { + m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches)); + } + } + + return true; +} + +IMPL_LINK_NOARG(WatchWindow, ExecuteUpdateWatches, void*, void) +{ + m_nUpdateWatchesId = nullptr; + UpdateWatches(); +} + +SbxBase* WatchWindow::ImplGetSBXForEntry(const weld::TreeIter& rEntry, bool& rbArrayElement) +{ + SbxBase* pSBX = nullptr; + rbArrayElement = false; + + WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry)); + OUString aVName( pItem->maName ); + + std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeListBox->make_iterator(&rEntry); + bool bParentEntry = m_xTreeListBox->iter_parent(*xParentEntry); + WatchItem* pParentItem = bParentEntry ? weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xParentEntry)) : nullptr; + if( pParentItem ) + { + SbxObject* pObj = pParentItem->mpObject.get(); + SbxDimArray* pArray; + if( pObj ) + { + pSBX = pObj->Find( aVName, SbxClassType::DontCare ); + if (SbxVariable const* pVar = IsSbxVariable(pSBX)) + { + // Force getting value + SbxValues aRes; + aRes.eType = SbxVOID; + pVar->Get( aRes ); + } + } + // Array? + else if( (pArray = pItem->GetRootArray()) != nullptr ) + { + rbArrayElement = true; + if( pParentItem->nDimLevel + 1 == pParentItem->nDimCount ) + pSBX = pArray->Get(pItem->vIndices.empty() ? nullptr : &*pItem->vIndices.begin()); + } + } + else + { + pSBX = StarBASIC::FindSBXInCurrentScope( aVName ); + } + return pSBX; +} + +IMPL_LINK(WatchWindow, EditingEntryHdl, const weld::TreeIter&, rIter, bool) +{ + WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rIter)); + + bool bEdit = false; + if (StarBASIC::IsRunning() && StarBASIC::GetActiveMethod() && !SbxBase::IsError()) + { + // No out of scope entries + bool bArrayElement; + SbxBase* pSbx = ImplGetSBXForEntry(rIter, bArrayElement); + if (IsSbxVariable(pSbx) || bArrayElement) + { + // Accept no objects and only end nodes of arrays for editing + if( !pItem->mpObject.is() && ( !pItem->mpArray.is() || pItem->nDimLevel == pItem->nDimCount ) ) + { + aEditingRes = m_xTreeListBox->get_text(rIter, 1); + aEditingRes = comphelper::string::strip(aEditingRes, ' '); + bEdit = true; + } + } + } + + return bEdit; +} + +IMPL_LINK(WatchWindow, EditedEntryHdl, const IterString&, rIterString, bool) +{ + const weld::TreeIter& rIter = rIterString.first; + OUString aResult = comphelper::string::strip(rIterString.second, ' '); + + sal_uInt16 nResultLen = aResult.getLength(); + sal_Unicode cFirst = aResult[0]; + sal_Unicode cLast = aResult[ nResultLen - 1 ]; + if( cFirst == '\"' && cLast == '\"' ) + aResult = aResult.copy( 1, nResultLen - 2 ); + + if (aResult == aEditingRes) + return false; + + bool bArrayElement; + SbxBase* pSBX = ImplGetSBXForEntry(rIter, bArrayElement); + + if (SbxVariable* pVar = IsSbxVariable(pSBX)) + { + SbxDataType eType = pVar->GetType(); + if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxOBJECT) + && ( eType & SbxARRAY ) == 0 ) + { + // If the type is variable, the conversion of the SBX does not matter, + // else the string is converted. + pVar->PutStringExt( aResult ); + } + } + + if ( SbxBase::IsError() ) + { + SbxBase::ResetError(); + } + + UpdateWatches(); + + // The text should never be taken/copied 1:1, + // as the UpdateWatches will be lost + return false; +} + +namespace +{ + +void implCollapseModifiedObjectEntry(const weld::TreeIter& rParent, weld::TreeView& rTree) +{ + rTree.collapse_row(rParent); + + std::unique_ptr<weld::TreeIter> xDeleteEntry = rTree.make_iterator(&rParent); + + while (rTree.iter_children(*xDeleteEntry)) + { + implCollapseModifiedObjectEntry(*xDeleteEntry, rTree); + + WatchItem* pItem = weld::fromId<WatchItem*>(rTree.get_id(*xDeleteEntry)); + delete pItem; + rTree.remove(*xDeleteEntry); + rTree.copy_iterator(rParent, *xDeleteEntry); + } +} + +OUString implCreateTypeStringForDimArray( WatchItem* pItem, SbxDataType eType ) +{ + OUString aRetStr = getBasicTypeName( eType ); + + SbxDimArray* pArray = pItem->mpArray.get(); + if( !pArray ) + pArray = pItem->GetRootArray(); + if( pArray ) + { + int nDimLevel = pItem->nDimLevel; + int nDims = pItem->nDimCount; + if( nDimLevel < nDims ) + { + aRetStr += "("; + for( int i = nDimLevel ; i < nDims ; i++ ) + { + sal_Int32 nMin, nMax; + pArray->GetDim(sal::static_int_cast<sal_Int32>(i + 1), nMin, nMax); + aRetStr += OUString::number(nMin) + " to " + OUString::number(nMax); + if( i < nDims - 1 ) + aRetStr += ", "; + } + aRetStr += ")"; + } + } + return aRetStr; +} + +} // namespace + +void WatchWindow::implEnableChildren(const weld::TreeIter& rEntry, bool bEnable) +{ + if (bEnable) + { + if (!m_xTreeListBox->get_row_expanded(rEntry)) + m_xTreeListBox->set_children_on_demand(rEntry, true); + } + else + { + assert(!m_xTreeListBox->get_row_expanded(rEntry)); + m_xTreeListBox->set_children_on_demand(rEntry, false); + } +} + +void WatchWindow::UpdateWatches(bool bBasicStopped) +{ + SbMethod* pCurMethod = StarBASIC::GetActiveMethod(); + + ErrCode eOld = SbxBase::GetError(); + setBasicWatchMode( true ); + + m_xTreeListBox->all_foreach([this, pCurMethod, bBasicStopped](weld::TreeIter& rEntry){ + WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry)); + DBG_ASSERT( !pItem->maName.isEmpty(), "Var? - Must not be empty!" ); + OUString aWatchStr; + OUString aTypeStr; + if ( pCurMethod ) + { + bool bCollapse = false; + TriState eEnableChildren = TRISTATE_INDET; + + bool bArrayElement; + SbxBase* pSBX = ImplGetSBXForEntry(rEntry, bArrayElement); + + // Array? If no end node create type string + if( bArrayElement && pItem->nDimLevel < pItem->nDimCount ) + { + SbxDimArray* pRootArray = pItem->GetRootArray(); + SbxDataType eType = pRootArray->GetType(); + aTypeStr = implCreateTypeStringForDimArray( pItem, eType ); + eEnableChildren = TRISTATE_TRUE; + } + + if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pSBX)) + { + // extra treatment of arrays + SbxDataType eType = pVar->GetType(); + if ( eType & SbxARRAY ) + { + // consider multidimensional arrays! + if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(pVar->GetObject())) + { + SbxDimArray* pOldArray = pItem->mpArray.get(); + + bool bArrayChanged = false; + if (pOldArray != nullptr) + { + // Compare Array dimensions to see if array has changed + // Can be a copy, so comparing pointers does not work + sal_Int32 nOldDims = pOldArray->GetDims(); + sal_Int32 nNewDims = pNewArray->GetDims(); + if( nOldDims != nNewDims ) + { + bArrayChanged = true; + } + else + { + for( sal_Int32 i = 0 ; i < nOldDims ; i++ ) + { + sal_Int32 nOldMin, nOldMax; + sal_Int32 nNewMin, nNewMax; + + pOldArray->GetDim(i + 1, nOldMin, nOldMax); + pNewArray->GetDim(i + 1, nNewMin, nNewMax); + if( nOldMin != nNewMin || nOldMax != nNewMax ) + { + bArrayChanged = true; + break; + } + } + } + } + else + { + bArrayChanged = true; + } + eEnableChildren = TRISTATE_TRUE; + // #i37227 Clear always and replace array + if( pNewArray != pOldArray ) + { + pItem->clearWatchItem(); + eEnableChildren = TRISTATE_TRUE; + + pItem->mpArray = pNewArray; + sal_Int32 nDims = pNewArray->GetDims(); + pItem->nDimLevel = 0; + pItem->nDimCount = nDims; + } + if( bArrayChanged && pOldArray != nullptr ) + { + bCollapse = true; + } + aTypeStr = implCreateTypeStringForDimArray( pItem, eType ); + } + else + { + aWatchStr += "<?>"; + } + } + else if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) ) + { + if (SbxObject* pObj = dynamic_cast<SbxObject*>(pVar->GetObject())) + { + if ( pItem->mpObject.is() && !pItem->maMemberList.empty() ) + { + createAllObjectProperties(pObj); + SbxArray* pProps = pObj->GetProperties(); + const sal_uInt32 nPropCount = getCorrectedPropCount(pProps); + // Check if member list has changed + bCollapse = pItem->maMemberList.size() != nPropCount; + for( sal_uInt32 i = 0 ; !bCollapse && i < nPropCount ; i++ ) + { + SbxVariable* pVar_ = pProps->Get(i); + if( pItem->maMemberList[i] != pVar_->GetName() ) + bCollapse = true; + } + } + + pItem->mpObject = pObj; + eEnableChildren = TRISTATE_TRUE; + aTypeStr = getBasicObjectTypeName( pObj ); + } + else + { + aWatchStr = "Null"; + if( pItem->mpObject.is() ) + { + bCollapse = true; + eEnableChildren = TRISTATE_FALSE; + } + } + } + else + { + if( pItem->mpObject.is() ) + { + bCollapse = true; + eEnableChildren = TRISTATE_FALSE; + } + + bool bString = (static_cast<sal_uInt8>(eType) == sal_uInt8(SbxSTRING)); + OUString aStrStr( "\"" ); + if( bString ) + { + aWatchStr += aStrStr; + } + // tdf#57308 - avoid a second call to retrieve the data + const SbxFlagBits nFlags = pVar->GetFlags(); + pVar->SetFlag(SbxFlagBits::NoBroadcast); + aWatchStr += pVar->GetOUString(); + pVar->SetFlags(nFlags); + if( bString ) + { + aWatchStr += aStrStr; + } + } + if( aTypeStr.isEmpty() ) + { + if( !pVar->IsFixed() ) + { + aTypeStr = "Variant/"; + } + aTypeStr += getBasicTypeName( pVar->GetType() ); + } + } + else if( !bArrayElement ) + { + aWatchStr += "<Out of Scope>"; + } + + if( bCollapse ) + { + implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox); + pItem->clearWatchItem(); + } + + if (eEnableChildren != TRISTATE_INDET) + implEnableChildren(rEntry, eEnableChildren == TRISTATE_TRUE); + } + else if( bBasicStopped ) + { + if( pItem->mpObject.is() || pItem->mpArray.is() ) + { + implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox); + pItem->mpObject.clear(); + pItem->mpArray.clear(); + } + pItem->clearWatchItem(); + } + + m_xTreeListBox->set_text(rEntry, aWatchStr, 1); + m_xTreeListBox->set_text(rEntry, aTypeStr, 2); + + return false; + }); + + SbxBase::ResetError(); + if( eOld != ERRCODE_NONE ) + SbxBase::SetError( eOld ); + setBasicWatchMode( false ); +} + +IMPL_LINK_NOARG(CodeCompleteWindow, ImplDoubleClickHdl, weld::TreeView&, bool) +{ + InsertSelectedEntry(); + return true; +} + +IMPL_LINK_NOARG(CodeCompleteWindow, ImplSelectHdl, weld::TreeView&, void) +{ + //give back the focus to the parent + pParent->GrabFocus(); +} + +TextView* CodeCompleteWindow::GetParentEditView() +{ + return pParent->GetEditView(); +} + +void CodeCompleteWindow::InsertSelectedEntry() +{ + OUString sSelectedEntry = m_xListBox->get_selected_text(); + + if( !aFuncBuffer.isEmpty() ) + { + // if the user typed in something: remove, and insert + GetParentEditView()->SetSelection(pParent->GetLastHighlightPortionTextSelection()); + GetParentEditView()->DeleteSelected(); + + if (!sSelectedEntry.isEmpty()) + { + // if the user selected something + GetParentEditView()->InsertText(sSelectedEntry); + } + } + else + { + if (!sSelectedEntry.isEmpty()) + { + // if the user selected something + GetParentEditView()->InsertText(sSelectedEntry); + } + } + HideAndRestoreFocus(); +} + +void CodeCompleteWindow::SetMatchingEntries() +{ + for (sal_Int32 i = 0, nEntryCount = m_xListBox->n_children(); i< nEntryCount; ++i) + { + OUString sEntry = m_xListBox->get_text(i); + if (sEntry.startsWithIgnoreAsciiCase(aFuncBuffer)) + { + m_xListBox->select(i); + break; + } + } +} + +IMPL_LINK(CodeCompleteWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return HandleKeyInput(rKEvt); +} + +bool CodeCompleteWindow::HandleKeyInput( const KeyEvent& rKeyEvt ) +{ + bool bHandled = true; + + sal_Unicode aChar = rKeyEvt.GetKeyCode().GetCode(); + if( (( aChar >= KEY_A ) && ( aChar <= KEY_Z )) + || ((aChar >= KEY_0) && (aChar <= KEY_9)) ) + { + aFuncBuffer.append(rKeyEvt.GetCharCode()); + SetMatchingEntries(); + } + else + { + switch( aChar ) + { + case KEY_POINT: + break; + case KEY_ESCAPE: // hide, do nothing + HideAndRestoreFocus(); + break; + case KEY_RIGHT: + { + TextSelection aTextSelection( GetParentEditView()->GetSelection() ); + if( aTextSelection.GetEnd().GetPara() != GetTextSelection().GetEnd().GetPara()-1 ) + { + HideAndRestoreFocus(); + } + break; + } + case KEY_LEFT: + { + TextSelection aTextSelection( GetParentEditView()->GetSelection() ); + if( aTextSelection.GetStart().GetIndex()-1 < GetTextSelection().GetStart().GetIndex() ) + {//leave the cursor where it is + HideAndRestoreFocus(); + } + break; + } + case KEY_TAB: + { + TextSelection aTextSelection = pParent->GetLastHighlightPortionTextSelection(); + OUString sTypedText = pParent->GetEditEngine()->GetText(aTextSelection); + if( !aFuncBuffer.isEmpty() ) + { + sal_Int32 nInd = m_xListBox->get_selected_index(); + if (nInd != -1) + { + int nEntryCount = m_xListBox->n_children(); + //if there is something selected + bool bFound = false; + for (sal_Int32 i = nInd; i != nEntryCount; ++i) + { + OUString sEntry = m_xListBox->get_text(i); + if( sEntry.startsWithIgnoreAsciiCase( aFuncBuffer ) + && (std::u16string_view(aFuncBuffer) != sTypedText) && (i != nInd) ) + { + m_xListBox->select(i); + bFound = true; + break; + } + } + if( !bFound ) + SetMatchingEntries(); + + GetParentEditView()->SetSelection( aTextSelection ); + GetParentEditView()->DeleteSelected(); + GetParentEditView()->InsertText(m_xListBox->get_selected_text()); + } + } + break; + } + case KEY_SPACE: + HideAndRestoreFocus(); + break; + case KEY_BACKSPACE: case KEY_DELETE: + if( !aFuncBuffer.isEmpty() ) + { + //if there was something inserted by tab: add it to aFuncBuffer + TextSelection aSel( GetParentEditView()->GetSelection() ); + TextPaM aEnd( GetParentEditView()->CursorEndOfLine(GetTextSelection().GetEnd()) ); + GetParentEditView()->SetSelection(TextSelection(GetTextSelection().GetStart(), aEnd ) ); + OUString aTabInsertedStr( GetParentEditView()->GetSelected() ); + GetParentEditView()->SetSelection( aSel ); + + if( !aTabInsertedStr.isEmpty() && aTabInsertedStr != std::u16string_view(aFuncBuffer) ) + { + aFuncBuffer = aTabInsertedStr; + } + aFuncBuffer.remove(aFuncBuffer.getLength()-1, 1); + SetMatchingEntries(); + } + else + { + ClearAndHide(); + bHandled = false; + } + break; + case KEY_RETURN: + InsertSelectedEntry(); + break; + case KEY_UP: + { + int nInd = m_xListBox->get_selected_index(); + if (nInd) + m_xListBox->select(nInd - 1); + break; + } + case KEY_DOWN: + { + int nInd = m_xListBox->get_selected_index(); + if (nInd + 1 < m_xListBox->n_children()) + m_xListBox->select(nInd + 1); + break; + } + default: + bHandled = false; + break; + } + } + + return bHandled; +} + +void CodeCompleteWindow::HideAndRestoreFocus() +{ + Hide(); + pParent->GrabFocus(); +} + +CodeCompleteWindow::CodeCompleteWindow(EditorWindow* pPar) + : InterimItemWindow(pPar, "modules/BasicIDE/ui/codecomplete.ui", "CodeComplete") + , pParent(pPar) + , m_xListBox(m_xBuilder->weld_tree_view("treeview")) +{ + m_xListBox->connect_row_activated(LINK(this, CodeCompleteWindow, ImplDoubleClickHdl)); + m_xListBox->connect_changed(LINK(this, CodeCompleteWindow, ImplSelectHdl)); + m_xListBox->connect_key_press(LINK(this, CodeCompleteWindow, KeyInputHdl)); + m_xListBox->make_sorted(); + + m_xListBox->set_size_request(150, 150); // default, this will adopt the line length + SetSizePixel(m_xContainer->get_preferred_size()); +} + +CodeCompleteWindow::~CodeCompleteWindow() +{ + disposeOnce(); +} + +void CodeCompleteWindow::dispose() +{ + m_xListBox.reset(); + pParent.clear(); + InterimItemWindow::dispose(); +} + +void CodeCompleteWindow::InsertEntry( const OUString& aStr ) +{ + m_xListBox->append_text(aStr); +} + +void CodeCompleteWindow::ClearListBox() +{ + m_xListBox->clear(); + aFuncBuffer.setLength(0); +} + +void CodeCompleteWindow::SetTextSelection( const TextSelection& aSel ) +{ + m_aTextSelection = aSel; +} + +void CodeCompleteWindow::ResizeAndPositionListBox() +{ + if (m_xListBox->n_children() < 1) + return; + + // if there is at least one element inside + // calculate basic position: under the current line + tools::Rectangle aRect = static_cast<TextEngine*>(pParent->GetEditEngine())->PaMtoEditCursor( pParent->GetEditView()->GetSelection().GetEnd() ); + tools::Long nViewYOffset = pParent->GetEditView()->GetStartDocPos().Y(); + Point aPos = aRect.BottomRight();// this variable will be used later (if needed) + aPos.setY( (aPos.Y() - nViewYOffset) + nBasePad ); + + // get line count + const sal_uInt16 nLines = static_cast<sal_uInt16>(std::min(6, m_xListBox->n_children())); + + m_xListBox->set_size_request(-1, m_xListBox->get_height_rows(nLines)); + + Size aSize = m_xContainer->get_preferred_size(); + //set the size + SetSizePixel( aSize ); + + //calculate position + const tools::Rectangle aVisArea( pParent->GetEditView()->GetStartDocPos(), pParent->GetOutputSizePixel() ); //the visible area + const Point& aBottomPoint = aVisArea.BottomRight(); + + if( aVisArea.TopRight().getY() + aPos.getY() + aSize.getHeight() > aBottomPoint.getY() ) + {//clipped at the bottom: move it up + const tools::Long& nParentFontHeight = pParent->GetEditEngine()->GetFont().GetFontHeight(); //parent's font (in the IDE): needed for height + aPos.AdjustY( -(aSize.getHeight() + nParentFontHeight + nCursorPad) ); + } + + if( aVisArea.TopLeft().getX() + aPos.getX() + aSize.getWidth() > aBottomPoint.getX() ) + {//clipped at the right side, move it a bit left + aPos.AdjustX( -(aSize.getWidth() + aVisArea.TopLeft().getX()) ); + } + //set the position + SetPosPixel( aPos ); +} + +void CodeCompleteWindow::SelectFirstEntry() +{ + if (m_xListBox->n_children() > 0) + m_xListBox->select(0); +} + +void CodeCompleteWindow::ClearAndHide() +{ + ClearListBox(); + HideAndRestoreFocus(); +} + +UnoTypeCodeCompletetor::UnoTypeCodeCompletetor( const std::vector< OUString >& aVect, const OUString& sVarType ) +: bCanComplete( true ) +{ + if( aVect.empty() || sVarType.isEmpty() ) + { + bCanComplete = false;//invalid parameters, nothing to code complete + return; + } + + try + { + // Get the base class for reflection: + xClass = css::reflection::theCoreReflection::get( + comphelper::getProcessComponentContext())->forName(sVarType); + } + catch( const Exception& ) + { + bCanComplete = false; + return; + } + + //start from aVect[1]: aVect[0] is the variable name + bCanComplete = std::none_of(aVect.begin() + 1, aVect.end(), [this](const OUString& rMethName) { + return (!CodeCompleteOptions::IsExtendedTypeDeclaration() || !CheckMethod(rMethName)) && !CheckField(rMethName); }); +} + +std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassMethods() const +{ + std::vector< OUString > aRetVect; + if( bCanComplete && ( xClass != nullptr ) ) + { + const Sequence< Reference< reflection::XIdlMethod > > aMethods = xClass->getMethods(); + for(Reference< reflection::XIdlMethod > const & rMethod : aMethods) + { + aRetVect.push_back( rMethod->getName() ); + } + } + return aRetVect;//this is empty when cannot code complete +} + +std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassFields() const +{ + std::vector< OUString > aRetVect; + if( bCanComplete && ( xClass != nullptr ) ) + { + const Sequence< Reference< reflection::XIdlField > > aFields = xClass->getFields(); + for(Reference< reflection::XIdlField > const & rxField : aFields) + { + aRetVect.push_back( rxField->getName() ); + } + } + return aRetVect;//this is empty when cannot code complete +} + + +bool UnoTypeCodeCompletetor::CheckField( const OUString& sFieldName ) +{// modifies xClass!!! + + if ( xClass == nullptr ) + return false; + + Reference< reflection::XIdlField> xField = xClass->getField( sFieldName ); + if( xField != nullptr ) + { + xClass = xField->getType(); + if( xClass != nullptr ) + { + return true; + } + } + return false; +} + +bool UnoTypeCodeCompletetor::CheckMethod( const OUString& sMethName ) +{// modifies xClass!!! + + + if ( xClass == nullptr ) + return false; + + Reference< reflection::XIdlMethod> xMethod = xClass->getMethod( sMethName ); + if( xMethod != nullptr ) //method OK, check return type + { + xClass = xMethod->getReturnType(); + if( xClass != nullptr ) + { + return true; + } + } + return false; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/baside3.cxx b/basctl/source/basicide/baside3.cxx new file mode 100644 index 0000000000..4a6b1dfbfb --- /dev/null +++ b/basctl/source/basicide/baside3.cxx @@ -0,0 +1,1325 @@ +/* -*- 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 <strings.hrc> +#include <helpids.h> +#include <iderid.hxx> + +#include <accessibledialogwindow.hxx> +#include <baside3.hxx> +#include <basidesh.hxx> +#include <bastype2.hxx> +#include <basobj.hxx> +#include <dlged.hxx> +#include <dlgeddef.hxx> +#include <dlgedmod.hxx> +#include <dlgedview.hxx> +#include <iderdll.hxx> +#include <localizationmgr.hxx> +#include <managelang.hxx> + +#include <com/sun/star/script/XLibraryContainer2.hpp> +#include <com/sun/star/resource/StringResourceWithLocation.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <comphelper/processfactory.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/visitem.hxx> +#include <svl/whiter.hxx> +#include <svx/svdundo.hxx> +#include <svx/svxids.hrc> +#include <comphelper/diagnose_ex.hxx> +#include <tools/urlobj.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/weld.hxx> +#include <vcl/settings.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> +#include <xmlscript/xmldlg_imexp.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::resource; +using namespace ::com::sun::star::ui::dialogs; + +#ifdef _WIN32 +constexpr OUString FilterMask_All = u"*.*"_ustr; +#else +constexpr OUString FilterMask_All = u"*"_ustr; +#endif + +DialogWindow::DialogWindow(DialogWindowLayout* pParent, ScriptDocument const& rDocument, + const OUString& aLibName, const OUString& aName, + css::uno::Reference<css::container::XNameContainer> const& xDialogModel) + : BaseWindow(pParent, rDocument, aLibName, aName) + ,m_rLayout(*pParent) + ,m_pEditor(new DlgEditor(*this, m_rLayout, rDocument.isDocument() + ? rDocument.getDocument() + : Reference<frame::XModel>(), xDialogModel)) + ,m_pUndoMgr(new SfxUndoManager) + ,m_nControlSlotId(SID_INSERT_SELECT) +{ + InitSettings(); + + m_pEditor->GetModel().SetNotifyUndoActionHdl( + &DialogWindow::NotifyUndoActionHdl + ); + + SetHelpId( HID_BASICIDE_DIALOGWINDOW ); + + // set readonly mode for readonly libraries + Reference< script::XLibraryContainer2 > xDlgLibContainer( GetDocument().getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) ) + SetReadOnly(true); + + if ( rDocument.isDocument() && rDocument.isReadOnly() ) + SetReadOnly(true); +} + +void DialogWindow::dispose() +{ + m_pEditor.reset(); + BaseWindow::dispose(); +} + +void DialogWindow::LoseFocus() +{ + if ( IsModified() ) + StoreData(); + + Window::LoseFocus(); +} + +void DialogWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + m_pEditor->Paint(rRenderContext, rRect); +} + +void DialogWindow::Resize() +{ + if (GetHScrollBar() && GetVScrollBar()) + { + m_pEditor->SetScrollBars( GetHScrollBar(), GetVScrollBar() ); + } +} + +void DialogWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + m_pEditor->MouseButtonDown( rMEvt ); + + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_SHOW_PROPERTYBROWSER ); +} + +void DialogWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + m_pEditor->MouseButtonUp( rMEvt ); + if( (m_pEditor->GetMode() == DlgEditor::INSERT) && !m_pEditor->IsCreateOK() ) + { + m_nControlSlotId = SID_INSERT_SELECT; + m_pEditor->SetMode( DlgEditor::SELECT ); + Shell::InvalidateControlSlots(); + } + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_SHOW_PROPERTYBROWSER ); + pBindings->Invalidate( SID_DOC_MODIFIED ); + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Invalidate( SID_COPY ); + pBindings->Invalidate( SID_CUT ); + } +} + +void DialogWindow::MouseMove( const MouseEvent& rMEvt ) +{ + m_pEditor->MouseMove( rMEvt ); +} + +void DialogWindow::KeyInput( const KeyEvent& rKEvt ) +{ + SfxBindings* pBindings = GetBindingsPtr(); + + if( rKEvt.GetKeyCode() == KEY_BACKSPACE ) + { + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute( SID_BACKSPACE ); + } + else + { + if( pBindings && rKEvt.GetKeyCode() == KEY_TAB ) + pBindings->Invalidate( SID_SHOW_PROPERTYBROWSER ); + + if( !m_pEditor->KeyInput( rKEvt ) ) + { + if( !SfxViewShell::Current()->KeyInput( rKEvt ) ) + Window::KeyInput( rKEvt ); + } + } + + // may be KEY_TAB, KEY_BACKSPACE, KEY_ESCAPE + if( pBindings ) + { + pBindings->Invalidate( SID_COPY ); + pBindings->Invalidate( SID_CUT ); + } +} + +void DialogWindow::Command( const CommandEvent& rCEvt ) +{ + if ( ( rCEvt.GetCommand() == CommandEventId::Wheel ) || + ( rCEvt.GetCommand() == CommandEventId::StartAutoScroll ) || + ( rCEvt.GetCommand() == CommandEventId::AutoScroll ) ) + { + HandleScrollCommand( rCEvt, GetHScrollBar(), GetVScrollBar() ); + } + else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + if (GetDispatcher()) + { + SdrView& rView = GetView(); + if( !rCEvt.IsMouseEvent() && rView.AreObjectsMarked() ) + { + tools::Rectangle aMarkedRect( rView.GetMarkedRect() ); + Point MarkedCenter( aMarkedRect.Center() ); + Point PosPixel( LogicToPixel( MarkedCenter ) ); + SfxDispatcher::ExecutePopup( this, &PosPixel ); + } + else + { + SfxDispatcher::ExecutePopup(); + } + + } + } + else + BaseWindow::Command( rCEvt ); +} + + +void DialogWindow::NotifyUndoActionHdl( std::unique_ptr<SdrUndoAction> ) +{ + // #i120515# pUndoAction needs to be deleted, this hand over is an ownership + // change. As long as it does not get added to the undo manager, it needs at + // least to be deleted. +} + +void DialogWindow::DoInit() +{ + m_pEditor->SetScrollBars( GetHScrollBar(), GetVScrollBar() ); +} + +void DialogWindow::DoScroll( Scrollable* ) +{ + m_pEditor->DoScroll(); +} + +void DialogWindow::GetState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter(rSet); + bool bIsCalc = false; + if ( GetDocument().isDocument() ) + { + Reference< frame::XModel > xModel= GetDocument().getDocument(); + if ( xModel.is() ) + { + Reference< lang::XServiceInfo > xServiceInfo ( xModel, UNO_QUERY ); + if ( xServiceInfo.is() && xServiceInfo->supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) ) + bIsCalc = true; + } + } + + for ( sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich() ) + { + switch ( nWh ) + { + case SID_PASTE: + { + if ( !IsPasteAllowed() ) + rSet.DisableItem( nWh ); + + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + } + break; + case SID_COPY: + { + // any object selected? + if ( !m_pEditor->GetView().AreObjectsMarked() ) + rSet.DisableItem( nWh ); + } + break; + case SID_CUT: + case SID_DELETE: + case SID_BACKSPACE: + { + // any object selected? + if ( !m_pEditor->GetView().AreObjectsMarked() ) + rSet.DisableItem( nWh ); + + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + } + break; + case SID_REDO: + { + if ( !m_pUndoMgr->GetUndoActionCount() ) + rSet.DisableItem( nWh ); + } + break; + + case SID_DIALOG_TESTMODE: + { + // is the IDE still active? + bool const bBool = GetShell()->GetFrame() && + m_pEditor->GetMode() == DlgEditor::TEST; + rSet.Put(SfxBoolItem(SID_DIALOG_TESTMODE, bBool)); + } + break; + + case SID_CHOOSE_CONTROLS: + { + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + } + break; + + case SID_SHOW_PROPERTYBROWSER: + { + Shell* pShell = GetShell(); + SfxViewFrame* pViewFrame = pShell ? &pShell->GetViewFrame() : nullptr; + if ( pViewFrame && !pViewFrame->HasChildWindow( SID_SHOW_PROPERTYBROWSER ) && !m_pEditor->GetView().AreObjectsMarked() ) + rSet.DisableItem( nWh ); + + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + } + break; + case SID_INSERT_FORM_RADIO: + case SID_INSERT_FORM_CHECK: + case SID_INSERT_FORM_LIST: + case SID_INSERT_FORM_COMBO: + case SID_INSERT_FORM_VSCROLL: + case SID_INSERT_FORM_HSCROLL: + case SID_INSERT_FORM_SPIN: + { + if ( !bIsCalc || IsReadOnly() ) + rSet.DisableItem( nWh ); + else + rSet.Put( SfxBoolItem( nWh, m_nControlSlotId == nWh ) ); + } + break; + + case SID_INSERT_SELECT: + case SID_INSERT_PUSHBUTTON: + case SID_INSERT_RADIOBUTTON: + case SID_INSERT_CHECKBOX: + case SID_INSERT_LISTBOX: + case SID_INSERT_COMBOBOX: + case SID_INSERT_GROUPBOX: + case SID_INSERT_EDIT: + case SID_INSERT_FIXEDTEXT: + case SID_INSERT_IMAGECONTROL: + case SID_INSERT_PROGRESSBAR: + case SID_INSERT_HSCROLLBAR: + case SID_INSERT_VSCROLLBAR: + case SID_INSERT_HFIXEDLINE: + case SID_INSERT_VFIXEDLINE: + case SID_INSERT_DATEFIELD: + case SID_INSERT_TIMEFIELD: + case SID_INSERT_NUMERICFIELD: + case SID_INSERT_CURRENCYFIELD: + case SID_INSERT_FORMATTEDFIELD: + case SID_INSERT_PATTERNFIELD: + case SID_INSERT_FILECONTROL: + case SID_INSERT_SPINBUTTON: + case SID_INSERT_GRIDCONTROL: + case SID_INSERT_HYPERLINKCONTROL: + case SID_INSERT_TREECONTROL: + { + if ( IsReadOnly() ) + rSet.DisableItem( nWh ); + else + rSet.Put( SfxBoolItem( nWh, m_nControlSlotId == nWh ) ); + } + break; + case SID_SHOWLINES: + { + // if this is not a module window hide the + // setting, doesn't make sense for example if the + // dialog editor is open + rSet.DisableItem(nWh); + rSet.Put(SfxVisibilityItem(nWh, false)); + break; + } + case SID_SELECTALL: + { + rSet.DisableItem( nWh ); + } + break; + } + } +} + +void DialogWindow::ExecuteCommand( SfxRequest& rReq ) +{ + const sal_uInt16 nSlotId(rReq.GetSlot()); + SdrObjKind nInsertObj(SdrObjKind::NONE); + + switch ( nSlotId ) + { + case SID_CUT: + if ( !IsReadOnly() ) + { + GetEditor().Cut(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + break; + case SID_DELETE: + if ( !IsReadOnly() ) + { + GetEditor().Delete(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + break; + case SID_COPY: + GetEditor().Copy(); + break; + case SID_PASTE: + if ( !IsReadOnly() ) + { + GetEditor().Paste(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + break; + + case SID_INSERT_FORM_RADIO: + nInsertObj = SdrObjKind::BasicDialogFormRadio; + break; + case SID_INSERT_FORM_CHECK: + nInsertObj = SdrObjKind::BasicDialogFormCheck; + break; + case SID_INSERT_FORM_LIST: + nInsertObj = SdrObjKind::BasicDialogFormList; + break; + case SID_INSERT_FORM_COMBO: + nInsertObj = SdrObjKind::BasicDialogFormCombo; + break; + case SID_INSERT_FORM_SPIN: + nInsertObj = SdrObjKind::BasicDialogFormSpin; + break; + case SID_INSERT_FORM_VSCROLL: + nInsertObj = SdrObjKind::BasicDialogFormVerticalScroll; + break; + case SID_INSERT_FORM_HSCROLL: + nInsertObj = SdrObjKind::BasicDialogFormHorizontalScroll; + break; + case SID_INSERT_PUSHBUTTON: + nInsertObj = SdrObjKind::BasicDialogPushButton; + break; + case SID_INSERT_RADIOBUTTON: + nInsertObj = SdrObjKind::BasicDialogRadioButton; + break; + case SID_INSERT_CHECKBOX: + nInsertObj = SdrObjKind::BasicDialogCheckbox; + break; + case SID_INSERT_LISTBOX: + nInsertObj = SdrObjKind::BasicDialogListbox; + break; + case SID_INSERT_COMBOBOX: + nInsertObj = SdrObjKind::BasicDialogCombobox; + break; + case SID_INSERT_GROUPBOX: + nInsertObj = SdrObjKind::BasicDialogGroupBox; + break; + case SID_INSERT_EDIT: + nInsertObj = SdrObjKind::BasicDialogEdit; + break; + case SID_INSERT_FIXEDTEXT: + nInsertObj = SdrObjKind::BasicDialogFixedText; + break; + case SID_INSERT_IMAGECONTROL: + nInsertObj = SdrObjKind::BasicDialogImageControl; + break; + case SID_INSERT_PROGRESSBAR: + nInsertObj = SdrObjKind::BasicDialogProgressbar; + break; + case SID_INSERT_HSCROLLBAR: + nInsertObj = SdrObjKind::BasicDialogHorizontalScrollbar; + break; + case SID_INSERT_VSCROLLBAR: + nInsertObj = SdrObjKind::BasicDialogVerticalScrollbar; + break; + case SID_INSERT_HFIXEDLINE: + nInsertObj = SdrObjKind::BasicDialogHorizontalFixedLine; + break; + case SID_INSERT_VFIXEDLINE: + nInsertObj = SdrObjKind::BasicDialogVerticalFixedLine; + break; + case SID_INSERT_DATEFIELD: + nInsertObj = SdrObjKind::BasicDialogDateField; + break; + case SID_INSERT_TIMEFIELD: + nInsertObj = SdrObjKind::BasicDialogTimeField; + break; + case SID_INSERT_NUMERICFIELD: + nInsertObj = SdrObjKind::BasicDialogNumericField; + break; + case SID_INSERT_CURRENCYFIELD: + nInsertObj = SdrObjKind::BasicDialogCurencyField; + break; + case SID_INSERT_FORMATTEDFIELD: + nInsertObj = SdrObjKind::BasicDialogFormattedField; + break; + case SID_INSERT_PATTERNFIELD: + nInsertObj = SdrObjKind::BasicDialogPatternField; + break; + case SID_INSERT_FILECONTROL: + nInsertObj = SdrObjKind::BasicDialogFileControl; + break; + case SID_INSERT_SPINBUTTON: + nInsertObj = SdrObjKind::BasicDialogSpinButton; + break; + case SID_INSERT_GRIDCONTROL: + nInsertObj = SdrObjKind::BasicDialogGridControl; + break; + case SID_INSERT_HYPERLINKCONTROL: + nInsertObj = SdrObjKind::BasicDialogHyperlinkControl; + break; + case SID_INSERT_TREECONTROL: + nInsertObj = SdrObjKind::BasicDialogTreeControl; + break; + case SID_INSERT_SELECT: + m_nControlSlotId = nSlotId; + GetEditor().SetMode( DlgEditor::SELECT ); + Shell::InvalidateControlSlots(); + break; + + case SID_DIALOG_TESTMODE: + { + DlgEditor::Mode eOldMode = GetEditor().GetMode(); + GetEditor().SetMode( DlgEditor::TEST ); + GetEditor().SetMode( eOldMode ); + rReq.Done(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DIALOG_TESTMODE ); + return; + } + case SID_EXPORT_DIALOG: + SaveDialog(); + break; + + case SID_IMPORT_DIALOG: + ImportDialog(); + break; + + case SID_BASICIDE_DELETECURRENT: + if (QueryDelDialog(m_aName, GetFrameWeld())) + { + if (RemoveDialog(m_aDocument, m_aLibName, m_aName)) + { + MarkDocumentModified(m_aDocument); + GetShell()->RemoveWindow(this, true); + } + } + break; + } + + if ( nInsertObj != SdrObjKind::NONE ) + { + m_nControlSlotId = nSlotId; + GetEditor().SetMode( DlgEditor::INSERT ); + GetEditor().SetInsertObj( nInsertObj ); + + if ( rReq.GetModifier() & KEY_MOD1 ) + { + GetEditor().CreateDefaultObject(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + + Shell::InvalidateControlSlots(); + } + + rReq.Done(); +} + +Reference< container::XNameContainer > const & DialogWindow::GetDialog() const +{ + return m_pEditor->GetDialog(); +} + +bool DialogWindow::RenameDialog( const OUString& rNewName ) +{ + if (!basctl::RenameDialog(GetFrameWeld(), GetDocument(), GetLibName(), GetName(), rNewName)) + return false; + + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_DOC_MODIFIED ); + + return true; +} + +void DialogWindow::DisableBrowser() +{ + m_rLayout.DisablePropertyBrowser(); +} + +void DialogWindow::UpdateBrowser() +{ + m_rLayout.UpdatePropertyBrowser(); +} + +void DialogWindow::SaveDialog() +{ + sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, + FileDialogFlags::NONE, this->GetFrameWeld()); + aDlg.SetContext(sfx2::FileDialogHelper::BasicExportDialog); + Reference<XFilePicker3> xFP = aDlg.GetFilePicker(); + + xFP.queryThrow<XFilePickerControlAccess>()->setValue(ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, Any(true)); + xFP->setDefaultName( GetName() ); + + OUString aDialogStr(IDEResId(RID_STR_STDDIALOGNAME)); + xFP->appendFilter( aDialogStr, "*.xdl" ); + xFP->appendFilter( IDEResId(RID_STR_FILTER_ALLFILES), FilterMask_All ); + xFP->setCurrentFilter( aDialogStr ); + + if( aDlg.Execute() != ERRCODE_NONE ) + return; + + OUString aSelectedFileURL = xFP->getSelectedFiles()[0]; + + Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext()); + Reference< XSimpleFileAccess3 > xSFI( SimpleFileAccess::create(xContext) ); + + Reference< XOutputStream > xOutput; + try + { + if( xSFI->exists(aSelectedFileURL) ) + xSFI->kill(aSelectedFileURL); + xOutput = xSFI->openFileWrite(aSelectedFileURL); + } + catch(const Exception& ) + {} + + if (!xOutput) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_COULDNTWRITE))); + xBox->run(); + return; + } + + // export dialog model to xml + auto xInput(xmlscript::exportDialogModel(GetDialog(), xContext, GetDocument().getDocumentOrNull())->createInputStream()); + + for (Sequence<sal_Int8> bytes; xInput->readBytes(bytes, xInput->available());) + xOutput->writeBytes(bytes); + + // With resource? + Reference< resource::XStringResourceResolver > xStringResourceResolver; + if (auto xDialogModelPropSet = GetDialog().query<beans::XPropertySet>()) + { + try + { + Any aResourceResolver = xDialogModelPropSet->getPropertyValue( "ResourceResolver" ); + aResourceResolver >>= xStringResourceResolver; + } + catch(const beans::UnknownPropertyException& ) + {} + } + + Sequence<lang::Locale> aLocaleSeq; + if (xStringResourceResolver) + aLocaleSeq = xStringResourceResolver->getLocales(); + if (aLocaleSeq.hasElements()) + { + INetURLObject aURLObj(aSelectedFileURL); + aURLObj.removeExtension(); + OUString aDialogName( aURLObj.getName() ); + aURLObj.removeSegment(); + OUString aURL( aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + OUString aComment = "# " + aDialogName + " strings" ; + Reference< task::XInteractionHandler > xDummyHandler; + + // Remove old properties files in case of overwriting Dialog files + if( xSFI->isFolder( aURL ) ) + { + Sequence< OUString > aContentSeq = xSFI->getFolderContents( aURL, false ); + + OUString aDialogName_ = aDialogName + "_" ; + for( const OUString& rCompleteName : aContentSeq ) + { + OUString aPureName; + OUString aExtension; + sal_Int32 iDot = rCompleteName.lastIndexOf( '.' ); + if( iDot != -1 ) + { + sal_Int32 iSlash = rCompleteName.lastIndexOf( '/' ); + sal_Int32 iCopyFrom = (iSlash != -1) ? iSlash + 1 : 0; + aPureName = rCompleteName.copy( iCopyFrom, iDot-iCopyFrom ); + aExtension = rCompleteName.copy( iDot + 1 ); + } + + if( aExtension == "properties" || aExtension == "default" ) + { + if( aPureName.startsWith( aDialogName_ ) ) + { + try + { + xSFI->kill( rCompleteName ); + } + catch(const uno::Exception& ) + {} + } + } + } + } + + Reference< XStringResourceWithLocation > xStringResourceWithLocation = + StringResourceWithLocation::create( xContext, aURL, false/*bReadOnly*/, + xStringResourceResolver->getDefaultLocale(), aDialogName, aComment, xDummyHandler ); + + // Add locales + for( const lang::Locale& rLocale : aLocaleSeq ) + { + xStringResourceWithLocation->newLocale( rLocale ); + } + + LocalizationMgr::copyResourceForDialog( GetDialog(), + xStringResourceResolver, xStringResourceWithLocation ); + + xStringResourceWithLocation->store(); + } +} + +static std::vector< lang::Locale > implGetLanguagesOnlyContainedInFirstSeq + ( const Sequence< lang::Locale >& aFirstSeq, const Sequence< lang::Locale >& aSecondSeq ) +{ + std::vector< lang::Locale > avRet; + + std::copy_if(aFirstSeq.begin(), aFirstSeq.end(), + std::back_inserter(avRet), + [&aSecondSeq](const lang::Locale& rFirstLocale) { + return std::none_of( + aSecondSeq.begin(), aSecondSeq.end(), + [&rFirstLocale](const lang::Locale& rSecondLocale) { + return localesAreEqual(rFirstLocale, rSecondLocale); + }); + }); + return avRet; +} + +namespace { + +class NameClashQueryBox +{ +private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; +public: + NameClashQueryBox(weld::Window* pParent, const OUString& rTitle, const OUString& rMessage) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, VclButtonsType::NONE, rMessage)) + { + if (!rTitle.isEmpty()) + m_xQueryBox->set_title(rTitle); + m_xQueryBox->add_button(IDEResId(RID_STR_DLGIMP_CLASH_RENAME), RET_YES); + m_xQueryBox->add_button(IDEResId(RID_STR_DLGIMP_CLASH_REPLACE), RET_NO); + m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + m_xQueryBox->set_default_response(RET_YES); + } + short run() { return m_xQueryBox->run(); } +}; + +class LanguageMismatchQueryBox +{ +private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; +public: + LanguageMismatchQueryBox(weld::Window* pParent, const OUString& rTitle, const OUString& rMessage) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, VclButtonsType::NONE, rMessage)) + { + if (!rTitle.isEmpty()) + m_xQueryBox->set_title(rTitle); + m_xQueryBox->add_button(IDEResId(RID_STR_DLGIMP_MISMATCH_ADD), RET_YES); + m_xQueryBox->add_button(IDEResId(RID_STR_DLGIMP_MISMATCH_OMIT), RET_NO); + m_xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + m_xQueryBox->add_button(GetStandardText(StandardButtonType::Help), RET_HELP); + m_xQueryBox->set_default_response(RET_YES); + } + short run() { return m_xQueryBox->run(); } +}; + +} + +bool implImportDialog(weld::Window* pWin, const ScriptDocument& rDocument, const OUString& aLibName) +{ + bool bDone = false; + + Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext()); + sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::NONE, pWin); + aDlg.SetContext(sfx2::FileDialogHelper::BasicImportDialog); + Reference<XFilePicker3> xFP = aDlg.GetFilePicker(); + + OUString aDialogStr(IDEResId(RID_STR_STDDIALOGNAME)); + xFP->appendFilter( aDialogStr, "*.xdl" ); + xFP->appendFilter( IDEResId(RID_STR_FILTER_ALLFILES), FilterMask_All ); + xFP->setCurrentFilter( aDialogStr ); + + if( aDlg.Execute() == ERRCODE_NONE ) + { + Sequence< OUString > aPaths = xFP->getSelectedFiles(); + + OUString aBasePath; + OUString aOUCurPath( aPaths[0] ); + sal_Int32 iSlash = aOUCurPath.lastIndexOf( '/' ); + if( iSlash != -1 ) + aBasePath = aOUCurPath.copy( 0, iSlash + 1 ); + + try + { + // create dialog model + Reference< container::XNameContainer > xDialogModel( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.awt.UnoControlDialogModel", xContext), + UNO_QUERY_THROW ); + + Reference< XSimpleFileAccess3 > xSFI( SimpleFileAccess::create(xContext) ); + + Reference< XInputStream > xInput; + if( xSFI->exists( aOUCurPath ) ) + xInput = xSFI->openFileRead( aOUCurPath ); + + ::xmlscript::importDialogModel( xInput, xDialogModel, xContext, rDocument.isDocument() ? rDocument.getDocument() : Reference< frame::XModel >() ); + + OUString aXmlDlgName; + Reference< beans::XPropertySet > xDialogModelPropSet( xDialogModel, UNO_QUERY ); + assert(xDialogModelPropSet.is()); + try + { + Any aXmlDialogNameAny = xDialogModelPropSet->getPropertyValue( DLGED_PROP_NAME ); + aXmlDialogNameAny >>= aXmlDlgName; + } + catch(const beans::UnknownPropertyException& ) + { + TOOLS_WARN_EXCEPTION("basctl", ""); + } + assert( !aXmlDlgName.isEmpty() ); + + bool bDialogAlreadyExists = rDocument.hasDialog( aLibName, aXmlDlgName ); + + OUString aNewDlgName = aXmlDlgName; + enum NameClashMode + { + NO_CLASH, + CLASH_OVERWRITE_DIALOG, + CLASH_RENAME_DIALOG, + }; + NameClashMode eNameClashMode = NO_CLASH; + if( bDialogAlreadyExists ) + { + OUString aQueryBoxTitle(IDEResId(RID_STR_DLGIMP_CLASH_TITLE)); + OUString aQueryBoxText(IDEResId(RID_STR_DLGIMP_CLASH_TEXT)); + aQueryBoxText = aQueryBoxText.replaceAll("$(ARG1)", aXmlDlgName); + + NameClashQueryBox aQueryBox(pWin, aQueryBoxTitle, aQueryBoxText); + sal_uInt16 nRet = aQueryBox.run(); + if( nRet == RET_YES ) + { + // RET_YES == Rename, see NameClashQueryBox::NameClashQueryBox + eNameClashMode = CLASH_RENAME_DIALOG; + + aNewDlgName = rDocument.createObjectName( E_DIALOGS, aLibName ); + } + else if( nRet == RET_NO ) + { + // RET_NO == Replace, see NameClashQueryBox::NameClashQueryBox + eNameClashMode = CLASH_OVERWRITE_DIALOG; + } + else if( nRet == RET_CANCEL ) + { + return bDone; + } + } + + Shell* pShell = GetShell(); + assert(pShell); + + // Resource? + css::lang::Locale aLocale = Application::GetSettings().GetUILanguageTag().getLocale(); + Reference< task::XInteractionHandler > xDummyHandler; + Reference< XStringResourceWithLocation > xImportStringResource = + StringResourceWithLocation::create( xContext, aBasePath, true/*bReadOnly*/, + aLocale, aXmlDlgName, OUString(), xDummyHandler ); + + Sequence< lang::Locale > aImportLocaleSeq = xImportStringResource->getLocales(); + sal_Int32 nImportLocaleCount = aImportLocaleSeq.getLength(); + + Reference< container::XNameContainer > xDialogLib( rDocument.getLibrary( E_DIALOGS, aLibName, true ) ); + Reference< resource::XStringResourceManager > xLibStringResourceManager = LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + sal_Int32 nLibLocaleCount = 0; + Sequence< lang::Locale > aLibLocaleSeq; + if( xLibStringResourceManager.is() ) + { + aLibLocaleSeq = xLibStringResourceManager->getLocales(); + nLibLocaleCount = aLibLocaleSeq.getLength(); + } + + // Check language matches + std::vector< lang::Locale > aOnlyInImportLanguages = + implGetLanguagesOnlyContainedInFirstSeq( aImportLocaleSeq, aLibLocaleSeq ); + int nOnlyInImportLanguageCount = aOnlyInImportLanguages.size(); + + // For now: Keep languages from lib + bool bLibLocalized = (nLibLocaleCount > 0); + bool bImportLocalized = (nImportLocaleCount > 0); + + bool bAddDialogLanguagesToLib = false; + if( nOnlyInImportLanguageCount > 0 ) + { + OUString aQueryBoxTitle(IDEResId(RID_STR_DLGIMP_MISMATCH_TITLE)); + OUString aQueryBoxText(IDEResId(RID_STR_DLGIMP_MISMATCH_TEXT)); + LanguageMismatchQueryBox aQueryBox(pWin, aQueryBoxTitle, aQueryBoxText); + sal_uInt16 nRet = aQueryBox.run(); + if( nRet == RET_YES ) + { + // RET_YES == Add, see LanguageMismatchQueryBox::LanguageMismatchQueryBox + bAddDialogLanguagesToLib = true; + } + // RET_NO == Omit, see LanguageMismatchQueryBox::LanguageMismatchQueryBox + // -> nothing to do here + //else if( RET_NO == nRet ) + //{ + //} + else if( nRet == RET_CANCEL ) + { + return bDone; + } + } + + if( bImportLocalized ) + { + bool bCopyResourcesForDialog = true; + if( bAddDialogLanguagesToLib ) + { + const std::shared_ptr<LocalizationMgr>& pCurMgr = pShell->GetCurLocalizationMgr(); + + lang::Locale aFirstLocale = aOnlyInImportLanguages[0]; + if( nOnlyInImportLanguageCount > 1 ) + { + // Check if import default belongs to only import languages and use it then + lang::Locale aImportDefaultLocale = xImportStringResource->getDefaultLocale(); + + if (std::any_of(aOnlyInImportLanguages.begin(), aOnlyInImportLanguages.end(), + [&aImportDefaultLocale](const lang::Locale& aTmpLocale) { + return localesAreEqual(aImportDefaultLocale, aTmpLocale); + })) + { + aFirstLocale = aImportDefaultLocale; + } + } + + pCurMgr->handleAddLocales( {aFirstLocale} ); + + if( nOnlyInImportLanguageCount > 1 ) + { + Sequence< lang::Locale > aRemainingLocaleSeq( nOnlyInImportLanguageCount - 1 ); + auto pRemainingLocaleSeq = aRemainingLocaleSeq.getArray(); + int iSeq = 0; + for( const lang::Locale& rLocale : aOnlyInImportLanguages ) + { + if( !localesAreEqual( aFirstLocale, rLocale ) ) + pRemainingLocaleSeq[iSeq++] = rLocale; + } + pCurMgr->handleAddLocales( aRemainingLocaleSeq ); + } + } + else if( !bLibLocalized ) + { + LocalizationMgr::resetResourceForDialog( xDialogModel, xImportStringResource ); + bCopyResourcesForDialog = false; + } + + if( bCopyResourcesForDialog ) + { + LocalizationMgr::copyResourceForDroppedDialog( xDialogModel, aXmlDlgName, + xLibStringResourceManager, xImportStringResource ); + } + } + else if( bLibLocalized ) + { + LocalizationMgr::setResourceIDsForDialog( xDialogModel, xLibStringResourceManager ); + } + + + LocalizationMgr::setStringResourceAtDialog( rDocument, aLibName, aNewDlgName, xDialogModel ); + + if( eNameClashMode == CLASH_OVERWRITE_DIALOG ) + { + if (basctl::RemoveDialog( rDocument, aLibName, aNewDlgName ) ) + { + BaseWindow* pDlgWin = pShell->FindDlgWin( rDocument, aLibName, aNewDlgName, false, true ); + if( pDlgWin != nullptr ) + pShell->RemoveWindow( pDlgWin, false ); + MarkDocumentModified( rDocument ); + } + else + { + // TODO: Assertion? + return bDone; + } + } + + if( eNameClashMode == CLASH_RENAME_DIALOG ) + { + bool bRenamed = false; + if( xDialogModelPropSet.is() ) + { + try + { + xDialogModelPropSet->setPropertyValue( DLGED_PROP_NAME, Any(aNewDlgName) ); + bRenamed = true; + } + catch(const beans::UnknownPropertyException& ) + {} + } + + + if( bRenamed ) + { + LocalizationMgr::renameStringResourceIDs( rDocument, aLibName, aNewDlgName, xDialogModel ); + } + else + { + // TODO: Assertion? + return bDone; + } + } + + Reference< XInputStreamProvider > xISP = ::xmlscript::exportDialogModel( xDialogModel, xContext, rDocument.isDocument() ? rDocument.getDocument() : Reference< frame::XModel >() ); + bool bSuccess = rDocument.insertDialog( aLibName, aNewDlgName, xISP ); + if( bSuccess ) + { + VclPtr<DialogWindow> pNewDlgWin = pShell->CreateDlgWin( rDocument, aLibName, aNewDlgName ); + pShell->SetCurWindow( pNewDlgWin, true ); + } + + bDone = true; + } + catch(const Exception& ) + {} + } + + return bDone; +} + +void DialogWindow::ImportDialog() +{ + const ScriptDocument& rDocument = GetDocument(); + OUString aLibName = GetLibName(); + implImportDialog(GetFrameWeld(), rDocument, aLibName); +} + +DlgEdModel& DialogWindow::GetModel() const +{ + return m_pEditor->GetModel(); +} + +DlgEdPage& DialogWindow::GetPage() const +{ + return m_pEditor->GetPage(); +} + +DlgEdView& DialogWindow::GetView() const +{ + return m_pEditor->GetView(); +} + +bool DialogWindow::IsModified() +{ + return m_pEditor->IsModified(); +} + +SfxUndoManager* DialogWindow::GetUndoManager() +{ + return m_pUndoMgr.get(); +} + +OUString DialogWindow::GetTitle() +{ + return GetName(); +} + +EntryDescriptor DialogWindow::CreateEntryDescriptor() +{ + ScriptDocument aDocument( GetDocument() ); + OUString aLibName( GetLibName() ); + LibraryLocation eLocation = aDocument.getLibraryLocation( aLibName ); + return EntryDescriptor( aDocument, eLocation, aLibName, OUString(), GetName(), OBJ_TYPE_DIALOG ); +} + +void DialogWindow::SetReadOnly (bool bReadOnly) +{ + m_pEditor->SetMode(bReadOnly ? DlgEditor::READONLY : DlgEditor::SELECT); +} + +bool DialogWindow::IsReadOnly () +{ + return m_pEditor->GetMode() == DlgEditor::READONLY; +} + +bool DialogWindow::IsPasteAllowed() +{ + return m_pEditor->IsPasteAllowed(); +} + +void DialogWindow::StoreData() +{ + if ( !IsModified() ) + return; + + try + { + Reference< container::XNameContainer > xLib = GetDocument().getLibrary( E_DIALOGS, GetLibName(), true ); + + if( xLib.is() ) + { + Reference< container::XNameContainer > xDialogModel = m_pEditor->GetDialog(); + + if( xDialogModel.is() ) + { + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XInputStreamProvider > xISP = ::xmlscript::exportDialogModel( xDialogModel, xContext, GetDocument().isDocument() ? GetDocument().getDocument() : Reference< frame::XModel >() ); + xLib->replaceByName( GetName(), Any( xISP ) ); + } + } + } + catch (const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + MarkDocumentModified( GetDocument() ); + m_pEditor->ClearModifyFlag(); +} + +void DialogWindow::Activating () +{ + UpdateBrowser(); + Show(); +} + +void DialogWindow::Deactivating() +{ + Hide(); + if ( IsModified() ) + MarkDocumentModified( GetDocument() ); + DisableBrowser(); +} + +sal_Int32 DialogWindow::countPages( Printer* ) +{ + return 1; +} + +void DialogWindow::printPage( sal_Int32 nPage, Printer* pPrinter ) +{ + DlgEditor::printPage( nPage, pPrinter, CreateQualifiedName() ); +} + +void DialogWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + if( (rDCEvt.GetType()==DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + InitSettings(); + Invalidate(); + } + else + BaseWindow::DataChanged( rDCEvt ); +} + +void DialogWindow::InitSettings() +{ + // FIXME RenderContext + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + vcl::Font aFont = rStyleSettings.GetFieldFont(); + SetPointFont(*GetOutDev(), aFont); + + SetTextColor( rStyleSettings.GetFieldTextColor() ); + SetTextFillColor(); + + SetBackground(rStyleSettings.GetFaceColor()); +} + +css::uno::Reference< css::accessibility::XAccessible > DialogWindow::CreateAccessible() +{ + return new AccessibleDialogWindow(this); +} + +OUString DialogWindow::GetHid () const +{ + return HID_BASICIDE_DIALOGWINDOW; +} + +ItemType DialogWindow::GetType () const +{ + return TYPE_DIALOG; +} + + +// DialogWindowLayout + + +DialogWindowLayout::DialogWindowLayout (vcl::Window* pParent, ObjectCatalog& rObjectCatalog_) : + Layout(pParent), + rObjectCatalog(rObjectCatalog_), + pPropertyBrowser(nullptr) +{ + ShowPropertyBrowser(); +} + +DialogWindowLayout::~DialogWindowLayout() +{ + disposeOnce(); +} + +void DialogWindowLayout::dispose() +{ + if (pPropertyBrowser) + Remove(pPropertyBrowser); + pPropertyBrowser.disposeAndClear(); + Layout::dispose(); +} + +// shows the property browser (and creates if necessary) +void DialogWindowLayout::ShowPropertyBrowser () +{ + // not exists? + if (!pPropertyBrowser) + { + // creating + pPropertyBrowser = VclPtr<PropBrw>::Create(*this); + pPropertyBrowser->Show(); + // after OnFirstSize(): + if (HasSize()) + AddPropertyBrowser(); + // updating if necessary + UpdatePropertyBrowser(); + } + else + pPropertyBrowser->Show(); + // refreshing the button state + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_SHOW_PROPERTYBROWSER); +} + +// disables the property browser +void DialogWindowLayout::DisablePropertyBrowser () +{ + if (pPropertyBrowser) + pPropertyBrowser->Update(nullptr); +} + +// updates the property browser +void DialogWindowLayout::UpdatePropertyBrowser () +{ + if (pPropertyBrowser) + pPropertyBrowser->Update(GetShell()); +} + +void DialogWindowLayout::Activating (BaseWindow& rChild) +{ + assert(dynamic_cast<DialogWindow*>(&rChild)); + rObjectCatalog.SetLayoutWindow(this); + rObjectCatalog.UpdateEntries(); + rObjectCatalog.Show(); + if (pPropertyBrowser) + pPropertyBrowser->Show(); + Layout::Activating(rChild); +} + +void DialogWindowLayout::Deactivating () +{ + Layout::Deactivating(); + rObjectCatalog.Hide(); + if (pPropertyBrowser) + pPropertyBrowser->Hide(); +} + +void DialogWindowLayout::ExecuteGlobal (SfxRequest& rReq) +{ + switch (rReq.GetSlot()) + { + case SID_SHOW_PROPERTYBROWSER: + // toggling property browser + if (pPropertyBrowser && pPropertyBrowser->IsVisible()) + pPropertyBrowser->Hide(); + else + ShowPropertyBrowser(); + ArrangeWindows(); + // refreshing the button state + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_SHOW_PROPERTYBROWSER); + break; + } +} + +void DialogWindowLayout::GetState (SfxItemSet& rSet, unsigned nWhich) +{ + switch (nWhich) + { + case SID_SHOW_PROPERTYBROWSER: + rSet.Put(SfxBoolItem(nWhich, pPropertyBrowser && pPropertyBrowser->IsVisible())); + break; + + case SID_BASICIDE_CHOOSEMACRO: + rSet.Put(SfxVisibilityItem(nWhich, false)); + break; + } +} + +void DialogWindowLayout::OnFirstSize (tools::Long const nWidth, tools::Long const nHeight) +{ + AddToLeft(&rObjectCatalog, Size(nWidth * 0.25, nHeight * 0.35)); + if (pPropertyBrowser) + AddPropertyBrowser(); +} + +void DialogWindowLayout::AddPropertyBrowser () { + Size const aSize = GetOutputSizePixel(); + AddToLeft(pPropertyBrowser, Size(aSize.Width() * 0.25, aSize.Height() * 0.65)); +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basidectrlr.cxx b/basctl/source/basicide/basidectrlr.cxx new file mode 100644 index 0000000000..849bff30cb --- /dev/null +++ b/basctl/source/basicide/basidectrlr.cxx @@ -0,0 +1,113 @@ +/* -*- 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 <basidectrlr.hxx> +#include <basidesh.hxx> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +namespace basctl +{ + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; + +namespace +{ + +int const nPropertyIconId = 1; +constexpr OUStringLiteral sPropertyIconId(u"IconId"); + +} + +Controller::Controller (Shell* pViewShell) + :OPropertyContainer( m_aBHelper ) + ,SfxBaseController( pViewShell ) + ,m_nIconId( ICON_MACROLIBRARY ) +{ + registerProperty( + sPropertyIconId, nPropertyIconId, + PropertyAttribute::READONLY, + &m_nIconId, cppu::UnoType<decltype(m_nIconId)>::get() + ); +} + +Controller::~Controller() +{ } + +// XInterface +Any SAL_CALL Controller::queryInterface( const Type & rType ) +{ + Any aReturn = SfxBaseController::queryInterface( rType ); + if ( !aReturn.hasValue() ) + aReturn = OPropertyContainer::queryInterface( rType ); + + return aReturn; +} + +void SAL_CALL Controller::acquire() noexcept +{ + SfxBaseController::acquire(); +} + +void SAL_CALL Controller::release() noexcept +{ + SfxBaseController::release(); +} + +// XTypeProvider ( ::SfxBaseController ) +Sequence< Type > SAL_CALL Controller::getTypes() +{ + Sequence< Type > aTypes = ::comphelper::concatSequences( + SfxBaseController::getTypes(), + getBaseTypes() + ); + + return aTypes; +} + +Sequence< sal_Int8 > SAL_CALL Controller::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XPropertySet +Reference< beans::XPropertySetInfo > SAL_CALL Controller::getPropertySetInfo() +{ + Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +// OPropertySetHelper +::cppu::IPropertyArrayHelper& Controller::getInfoHelper() +{ + return *getArrayHelper(); +} + +// OPropertyArrayUsageHelper +::cppu::IPropertyArrayHelper* Controller::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basides1.cxx b/basctl/source/basicide/basides1.cxx new file mode 100644 index 0000000000..9ef9f75e3f --- /dev/null +++ b/basctl/source/basicide/basides1.cxx @@ -0,0 +1,1551 @@ +/* -*- 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 <iderid.hxx> +#include <strings.hrc> +#include <helpids.h> + +#include "baside2.hxx" +#include <baside3.hxx> +#include <basidesh.hxx> +#include <basobj.hxx> +#include <docsignature.hxx> +#include <iderdll.hxx> +#include "iderdll2.hxx" +#include <localizationmgr.hxx> +#include <managelang.hxx> + +#include <basic/basmgr.hxx> +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/script/XLibraryContainer2.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <svl/srchdefs.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/childwin.hxx> +#include <sfx2/dinfdlg.hxx> +#include <sfx2/minfitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <svl/eitem.hxx> +#include <svl/intitem.hxx> +#include <svl/visitem.hxx> +#include <svl/whiter.hxx> +#include <vcl/texteng.hxx> +#include <vcl/textview.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svx/zoomsliderctrl.hxx> +#include <svx/zoomslideritem.hxx> +#include <basegfx/utils/zoomtools.hxx> + +constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10; + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; + +static void lcl_InvalidateZoomSlots(SfxBindings* pBindings) +{ + if (!pBindings) + return; + + static sal_uInt16 const aInval[] = { + SID_ZOOM_OUT, SID_ZOOM_IN, SID_ATTR_ZOOMSLIDER, 0 + }; + pBindings->Invalidate(aInval); +} + +void Shell::ExecuteSearch( SfxRequest& rReq ) +{ + if ( !pCurWin ) + return; + + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + + // if searching has not been done before this time + if (nSlot == SID_BASICIDE_REPEAT_SEARCH && !mpSearchItem) + { + rReq.SetReturnValue(SfxBoolItem(nSlot, false)); + nSlot = 0; + } + + switch ( nSlot ) + { + case SID_SEARCH_OPTIONS: + break; + case SID_SEARCH_ITEM: + mpSearchItem.reset(pArgs->Get(SID_SEARCH_ITEM).Clone()); + break; + case FID_SEARCH_ON: + mbJustOpened = true; + GetViewFrame().GetBindings().Invalidate(SID_SEARCH_ITEM); + break; + case SID_BASICIDE_REPEAT_SEARCH: + case FID_SEARCH_NOW: + { + if (!pCurWin->HasActiveEditor()) + break; + + // If it is a repeat searching + if ( nSlot == SID_BASICIDE_REPEAT_SEARCH ) + { + if( !mpSearchItem ) + mpSearchItem.reset( new SvxSearchItem( SID_SEARCH_ITEM )); + } + else + { + // Get SearchItem from request if it is the first searching + if ( pArgs ) + { + mpSearchItem.reset(pArgs->Get(SID_SEARCH_ITEM).Clone()); + } + } + + sal_Int32 nFound = 0; + + if ( mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE_ALL ) + { + sal_uInt16 nActModWindows = 0; + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if (pWin->HasActiveEditor()) + nActModWindows++; + } + + bool bAllModules = nActModWindows <= 1; + if (!bAllModules) + { + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pCurWin ? pCurWin->GetFrameWeld() : nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + IDEResId(RID_STR_SEARCHALLMODULES))); + xQueryBox->set_default_response(RET_YES); + bAllModules = xQueryBox->run() == RET_YES; + } + + if (bAllModules) + { + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + nFound += pWin->StartSearchAndReplace( *mpSearchItem ); + } + } + else + nFound = pCurWin->StartSearchAndReplace( *mpSearchItem ); + + OUString aReplStr(IDEResId(RID_STR_SEARCHREPLACES)); + aReplStr = aReplStr.replaceAll("XX", OUString::number(nFound)); + + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pCurWin->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + aReplStr)); + xInfoBox->run(); + } + else + { + bool bCanceled = false; + nFound = pCurWin->StartSearchAndReplace( *mpSearchItem ); + if ( !nFound && !mpSearchItem->GetSelection() ) + { + // search other modules... + bool bChangeCurWindow = false; + auto it = std::find_if(aWindowTable.cbegin(), aWindowTable.cend(), + [this](const WindowTable::value_type& item) { return item.second == pCurWin; }); + if (it != aWindowTable.cend()) + ++it; + BaseWindow* pWin = it != aWindowTable.cend() ? it->second.get() : nullptr; + + bool bSearchedFromStart = false; + while ( !nFound && !bCanceled && ( pWin || !bSearchedFromStart ) ) + { + if ( !pWin ) + { + SfxViewFrame& rViewFrame = GetViewFrame(); + SfxChildWindow* pChildWin = rViewFrame.GetChildWindow(SID_SEARCH_DLG); + auto xParent = pChildWin ? pChildWin->GetController() : nullptr; + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(xParent ? xParent->getDialog() : nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + IDEResId(RID_STR_SEARCHFROMSTART))); + xQueryBox->set_default_response(RET_YES); + if (xQueryBox->run() == RET_YES) + { + it = aWindowTable.cbegin(); + if ( it != aWindowTable.cend() ) + pWin = it->second; + bSearchedFromStart = true; + } + else + bCanceled = true; + } + + if (pWin && pWin->HasActiveEditor()) + { + if ( pWin != pCurWin ) + { + if ( pCurWin ) + pWin->SetSizePixel( pCurWin->GetSizePixel() ); + nFound = pWin->StartSearchAndReplace( *mpSearchItem, true ); + } + if ( nFound ) + { + bChangeCurWindow = true; + break; + } + } + if ( pWin && ( pWin != pCurWin ) ) + { + if ( it != aWindowTable.cend() ) + ++it; + pWin = it != aWindowTable.cend() ? it->second.get() : nullptr; + } + else + pWin = nullptr; + } + if ( !nFound && bSearchedFromStart ) + nFound = pCurWin->StartSearchAndReplace( *mpSearchItem, true ); + if ( bChangeCurWindow ) + SetCurWindow( pWin, true ); + } + if ( !nFound && !bCanceled ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pCurWin->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + IDEResId(RID_STR_SEARCHNOTFOUND))); + xInfoBox->run(); + } + } + + rReq.Done(); + break; + } + default: + pCurWin->ExecuteCommand( rReq ); + } +} + +void Shell::ExecuteCurrent( SfxRequest& rReq ) +{ + if ( !pCurWin ) + return; + + switch ( rReq.GetSlot() ) + { + case SID_BASICIDE_HIDECURPAGE: + { + pCurWin->StoreData(); + RemoveWindow( pCurWin, false ); + } + break; + case SID_BASICIDE_RENAMECURRENT: + { + pTabBar->StartEditMode( pTabBar->GetCurPageId() ); + } + break; + case SID_UNDO: + case SID_REDO: + if ( GetUndoManager() && pCurWin->AllowUndo() ) + GetViewFrame().ExecuteSlot( rReq ); + break; + default: + pCurWin->ExecuteCommand( rReq ); + } +} + +// no matter who's at the top, influence on the shell: +void Shell::ExecuteGlobal( SfxRequest& rReq ) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + switch ( nSlot ) + { + case SID_NEWDOCDIRECT: + { + // we do not have a new document factory, + // so just forward to a fallback method. + SfxGetpApp()->ExecuteSlot(rReq); + } + break; + + case SID_BASICSTOP: + { + // maybe do not simply stop if on breakpoint! + if (ModulWindow* pMCurWin = dynamic_cast<ModulWindow*>(pCurWin.get())) + pMCurWin->BasicStop(); + StopBasic(); + } + break; + + case SID_SAVEDOC: + { + if ( pCurWin ) + { + // rewrite date into the BASIC + StoreAllWindowData(); + + // document basic + ScriptDocument aDocument( pCurWin->GetDocument() ); + if ( aDocument.isDocument() ) + { + uno::Reference< task::XStatusIndicator > xStatusIndicator; + + const SfxUnoAnyItem* pStatusIndicatorItem = rReq.GetArg<SfxUnoAnyItem>(SID_PROGRESS_STATUSBAR_CONTROL); + if ( pStatusIndicatorItem ) + OSL_VERIFY( pStatusIndicatorItem->GetValue() >>= xStatusIndicator ); + else + { + // get statusindicator + SfxViewFrame *pFrame_ = GetFrame(); + if ( pFrame_ ) + { + uno::Reference< task::XStatusIndicatorFactory > xStatFactory( + pFrame_->GetFrame().GetFrameInterface(), + uno::UNO_QUERY ); + if( xStatFactory.is() ) + xStatusIndicator = xStatFactory->createStatusIndicator(); + } + + if ( xStatusIndicator.is() ) + rReq.AppendItem( SfxUnoAnyItem( SID_PROGRESS_STATUSBAR_CONTROL, uno::Any( xStatusIndicator ) ) ); + } + + aDocument.saveDocument( xStatusIndicator ); + } + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_DOC_MODIFIED ); + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Invalidate( SID_SIGNATURE ); + } + } + } + break; + case SID_BASICIDE_MODULEDLG: + { + if ( rReq.GetArgs() ) + { + const SfxUInt16Item &rTabId = rReq.GetArgs()->Get(SID_BASICIDE_ARG_TABID ); + Organize(rReq.GetFrameWeld(), nullptr, rTabId.GetValue()); + } + else + Organize(rReq.GetFrameWeld(), nullptr, 0); + } + break; + case SID_BASICIDE_CHOOSEMACRO: + { + ChooseMacro(rReq.GetFrameWeld(), nullptr); + } + break; + case SID_BASICIDE_CREATEMACRO: + case SID_BASICIDE_EDITMACRO: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SfxMacroInfoItem& rInfo = rReq.GetArgs()->Get(SID_BASICIDE_ARG_MACROINFO ); + BasicManager* pBasMgr = const_cast<BasicManager*>(rInfo.GetBasicManager()); + DBG_ASSERT( pBasMgr, "Nothing selected in basic tree?" ); + + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + + StartListening(*pBasMgr, DuplicateHandling::Prevent /* log on only once */); + OUString aLibName( rInfo.GetLib() ); + if ( aLibName.isEmpty() ) + aLibName = "Standard" ; + StarBASIC* pBasic = pBasMgr->GetLib( aLibName ); + if ( !pBasic ) + { + // load module and dialog library (if not loaded) + aDocument.loadLibraryIfExists( E_SCRIPTS, aLibName ); + aDocument.loadLibraryIfExists( E_DIALOGS, aLibName ); + + // get Basic + pBasic = pBasMgr->GetLib( aLibName ); + } + DBG_ASSERT( pBasic, "No Basic!" ); + + SetCurLib( aDocument, aLibName ); + + if ( pBasic && rReq.GetSlot() == SID_BASICIDE_CREATEMACRO ) + { + SbModule* pModule = pBasic->FindModule( rInfo.GetModule() ); + if ( !pModule ) + { + if ( !rInfo.GetModule().isEmpty() || pBasic->GetModules().empty() ) + { + const OUString& aModName = rInfo.GetModule(); + + OUString sModuleCode; + if ( aDocument.createModule( aLibName, aModName, false, sModuleCode ) ) + pModule = pBasic->FindModule( aModName ); + } + else + pModule = pBasic->GetModules().front().get(); + } + DBG_ASSERT( pModule, "No Module!" ); + if ( pModule && !pModule->GetMethods()->Find( rInfo.GetMethod(), SbxClassType::Method ) ) + CreateMacro( pModule, rInfo.GetMethod() ); + } + SfxViewFrame& rViewFrame = GetViewFrame(); + rViewFrame.ToTop(); + VclPtr<ModulWindow> pWin = FindBasWin( aDocument, aLibName, rInfo.GetModule(), true ); + DBG_ASSERT( pWin, "Edit/Create Macro: Window was not created/found!" ); + SetCurWindow( pWin, true ); + pWin->EditMacro( rInfo.GetMethod() ); + } + break; + + case SID_BASICIDE_OBJCAT: + // toggling object catalog + aObjectCatalog->Show(!aObjectCatalog->IsVisible()); + if (pLayout) + pLayout->ArrangeWindows(); + // refresh the button state + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_BASICIDE_OBJCAT); + break; + + case SID_BASICIDE_WATCH: + { + // Toggling the watch window can only be done from a ModulWindow + if (!dynamic_cast<ModulWindowLayout*>(pLayout.get())) + return; + + pModulLayout->ShowWatchWindow(!pModulLayout->IsWatchWindowVisible()); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_BASICIDE_WATCH); + } + break; + + case SID_BASICIDE_STACK: + { + // Toggling the stack window can only be done from a ModulWindow + if (!dynamic_cast<ModulWindowLayout*>(pLayout.get())) + return; + + pModulLayout->ShowStackWindow(!pModulLayout->IsStackWindowVisible()); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_BASICIDE_STACK); + } + break; + + case SID_BASICIDE_NAMECHANGEDONTAB: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SfxUInt16Item &rTabId = rReq.GetArgs()->Get(SID_BASICIDE_ARG_TABID ); + const SfxStringItem &rModName = rReq.GetArgs()->Get(SID_BASICIDE_ARG_MODULENAME ); + if ( aWindowTable.find( rTabId.GetValue() ) != aWindowTable.end() ) + { + VclPtr<BaseWindow> pWin = aWindowTable[ rTabId.GetValue() ]; + const OUString& aNewName( rModName.GetValue() ); + OUString aOldName( pWin->GetName() ); + if ( aNewName != aOldName ) + { + bool bRenameOk = false; + if (ModulWindow* pModWin = dynamic_cast<ModulWindow*>(pWin.get())) + { + const OUString& aLibName = pModWin->GetLibName(); + ScriptDocument aDocument( pWin->GetDocument() ); + + if (RenameModule(pModWin->GetFrameWeld(), aDocument, aLibName, aOldName, aNewName)) + { + bRenameOk = true; + // Because we listen for container events for script + // modules, rename will delete the 'old' window + // pWin has been invalidated, restore now + pWin = FindBasWin( aDocument, aLibName, aNewName, true ); + } + + } + else if (DialogWindow* pDlgWin = dynamic_cast<DialogWindow*>(pWin.get())) + { + bRenameOk = pDlgWin->RenameDialog( aNewName ); + } + if ( bRenameOk ) + { + MarkDocumentModified( pWin->GetDocument() ); + } + else + { + // set old name in TabWriter + sal_uInt16 nId = GetWindowId( pWin ); + DBG_ASSERT( nId, "No entry in Tabbar!" ); + if ( nId ) + pTabBar->SetPageText( nId, aOldName ); + } + } + + // set focus to current window + pWin->GrabFocus(); + } + } + break; + case SID_BASICIDE_STOREMODULESOURCE: + case SID_BASICIDE_UPDATEMODULESOURCE: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SfxMacroInfoItem& rInfo = rReq.GetArgs()->Get(SID_BASICIDE_ARG_MACROINFO ); + BasicManager* pBasMgr = const_cast<BasicManager*>(rInfo.GetBasicManager()); + DBG_ASSERT( pBasMgr, "Store source: No BasMgr?" ); + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + VclPtr<ModulWindow> pWin = FindBasWin( aDocument, rInfo.GetLib(), rInfo.GetModule(), false, true ); + if ( pWin ) + { + if ( rReq.GetSlot() == SID_BASICIDE_STOREMODULESOURCE ) + pWin->StoreData(); + else + pWin->UpdateData(); + } + } + break; + case SID_BASICIDE_STOREALLMODULESOURCES: + case SID_BASICIDE_UPDATEALLMODULESOURCES: + { + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if (!pWin->IsSuspended() && dynamic_cast<ModulWindow*>(pWin)) + { + if ( rReq.GetSlot() == SID_BASICIDE_STOREALLMODULESOURCES ) + pWin->StoreData(); + else + pWin->UpdateData(); + } + } + } + break; + case SID_BASICIDE_LIBSELECTED: + case SID_BASICIDE_LIBREMOVED: + case SID_BASICIDE_LIBLOADED: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SfxUnoAnyItem& rShellItem = rReq.GetArgs()->Get( SID_BASICIDE_ARG_DOCUMENT_MODEL ); + uno::Reference< frame::XModel > xModel( rShellItem.GetValue(), UNO_QUERY ); + ScriptDocument aDocument( xModel.is() ? ScriptDocument( xModel ) : ScriptDocument::getApplicationScriptDocument() ); + const SfxStringItem& rLibNameItem = rReq.GetArgs()->Get( SID_BASICIDE_ARG_LIBNAME ); + const OUString& aLibName( rLibNameItem.GetValue() ); + + if ( nSlot == SID_BASICIDE_LIBSELECTED ) + { + // load module and dialog library (if not loaded) + aDocument.loadLibraryIfExists( E_SCRIPTS, aLibName ); + aDocument.loadLibraryIfExists( E_DIALOGS, aLibName ); + + // check password, if library is password protected and not verified + bool bOK = true; + Reference< script::XLibraryContainer > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aLibName ) && !xPasswd->isLibraryPasswordVerified( aLibName ) ) + { + OUString aPassword; + bOK = QueryPassword(rReq.GetFrameWeld(), xModLibContainer, aLibName, aPassword); + } + } + + if ( bOK ) + { + SetCurLib( aDocument, aLibName, true, false ); + } + else + { + // adjust old value... + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate(SID_BASICIDE_LIBSELECTOR, true); + } + } + else if ( nSlot == SID_BASICIDE_LIBREMOVED ) + { + if ( m_aCurLibName.isEmpty() || ( aDocument == m_aCurDocument && aLibName == m_aCurLibName ) ) + { + RemoveWindows( aDocument, aLibName ); + if ( aDocument == m_aCurDocument && aLibName == m_aCurLibName ) + { + m_aCurDocument = ScriptDocument::getApplicationScriptDocument(); + m_aCurLibName.clear(); + // no UpdateWindows! + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_BASICIDE_LIBSELECTOR ); + } + } + } + else // Loaded... + UpdateWindows(); + } + break; + case SID_BASICIDE_NEWMODULE: + { + VclPtr<ModulWindow> pWin = CreateBasWin( m_aCurDocument, m_aCurLibName, OUString() ); + DBG_ASSERT( pWin, "New Module: Could not create window!" ); + SetCurWindow( pWin, true ); + } + break; + case SID_BASICIDE_NEWDIALOG: + { + VclPtr<DialogWindow> pWin = CreateDlgWin( m_aCurDocument, m_aCurLibName, OUString() ); + DBG_ASSERT( pWin, "New Module: Could not create window!" ); + SetCurWindow( pWin, true ); + } + break; + case SID_BASICIDE_SBXRENAMED: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + } + break; + case SID_BASICIDE_SBXINSERTED: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SbxItem& rSbxItem = rReq.GetArgs()->Get(SID_BASICIDE_ARG_SBX ); + const ScriptDocument& aDocument( rSbxItem.GetDocument() ); + const OUString& aLibName( rSbxItem.GetLibName() ); + const OUString& aName( rSbxItem.GetName() ); + if ( m_aCurLibName.isEmpty() || ( aDocument == m_aCurDocument && aLibName == m_aCurLibName ) ) + { + if ( rSbxItem.GetType() == TYPE_MODULE ) + FindBasWin( aDocument, aLibName, aName, true ); + else if ( rSbxItem.GetType() == TYPE_DIALOG ) + FindDlgWin( aDocument, aLibName, aName, true ); + } + } + break; + case SID_BASICIDE_SBXDELETED: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SbxItem& rSbxItem = rReq.GetArgs()->Get(SID_BASICIDE_ARG_SBX ); + const ScriptDocument& aDocument( rSbxItem.GetDocument() ); + VclPtr<BaseWindow> pWin = FindWindow( aDocument, rSbxItem.GetLibName(), rSbxItem.GetName(), rSbxItem.GetType(), true ); + if ( pWin ) + RemoveWindow( pWin, true ); + } + break; + case SID_BASICIDE_SHOWSBX: + { + DBG_ASSERT( rReq.GetArgs(), "arguments expected" ); + const SbxItem& rSbxItem = rReq.GetArgs()->Get(SID_BASICIDE_ARG_SBX ); + const ScriptDocument& aDocument( rSbxItem.GetDocument() ); + const OUString& aLibName( rSbxItem.GetLibName() ); + const OUString& aName( rSbxItem.GetName() ); + SetCurLib( aDocument, aLibName ); + BaseWindow* pWin = nullptr; + if ( rSbxItem.GetType() == TYPE_DIALOG ) + { + pWin = FindDlgWin( aDocument, aLibName, aName, true ); + } + else if ( rSbxItem.GetType() == TYPE_MODULE ) + { + pWin = FindBasWin( aDocument, aLibName, aName, true ); + } + else if ( rSbxItem.GetType() == TYPE_METHOD ) + { + pWin = FindBasWin( aDocument, aLibName, aName, true ); + static_cast<ModulWindow*>(pWin)->EditMacro( rSbxItem.GetMethodName() ); + } + DBG_ASSERT( pWin, "Window was not created!" ); + SetCurWindow( pWin, true ); + pTabBar->MakeVisible( pTabBar->GetCurPageId() ); + } + break; + case SID_BASICIDE_SHOWWINDOW: + { + std::unique_ptr< ScriptDocument > pDocument; + + const SfxStringItem* pDocumentItem = rReq.GetArg<SfxStringItem>(SID_BASICIDE_ARG_DOCUMENT); + if ( pDocumentItem ) + { + const OUString& sDocumentCaption = pDocumentItem->GetValue(); + if ( !sDocumentCaption.isEmpty() ) + pDocument.reset( new ScriptDocument( ScriptDocument::getDocumentWithURLOrCaption( sDocumentCaption ) ) ); + } + + const SfxUnoAnyItem* pDocModelItem = rReq.GetArg<SfxUnoAnyItem>(SID_BASICIDE_ARG_DOCUMENT_MODEL); + if (!pDocument && pDocModelItem) + { + uno::Reference< frame::XModel > xModel( pDocModelItem->GetValue(), UNO_QUERY ); + if ( xModel.is() ) + pDocument.reset( new ScriptDocument( xModel ) ); + } + + if (!pDocument) + break; + + const SfxStringItem* pLibNameItem = rReq.GetArg<SfxStringItem>(SID_BASICIDE_ARG_LIBNAME); + if ( !pLibNameItem ) + break; + + OUString aLibName( pLibNameItem->GetValue() ); + pDocument->loadLibraryIfExists( E_SCRIPTS, aLibName ); + SetCurLib( *pDocument, aLibName ); + const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_BASICIDE_ARG_NAME); + if ( pNameItem ) + { + const OUString& aName( pNameItem->GetValue() ); + OUString aModType( "Module" ); + OUString aType( aModType ); + const SfxStringItem* pTypeItem = rReq.GetArg<SfxStringItem>(SID_BASICIDE_ARG_TYPE); + if ( pTypeItem ) + aType = pTypeItem->GetValue(); + + BaseWindow* pWin = nullptr; + if ( aType == aModType ) + pWin = FindBasWin( *pDocument, aLibName, aName ); + else if ( aType == "Dialog" ) + pWin = FindDlgWin( *pDocument, aLibName, aName ); + + if ( pWin ) + { + SetCurWindow( pWin, true ); + if ( pTabBar ) + pTabBar->MakeVisible( pTabBar->GetCurPageId() ); + + if (ModulWindow* pModWin = dynamic_cast<ModulWindow*>(pWin)) + { + const SfxUInt32Item* pLineItem = rReq.GetArg<SfxUInt32Item>(SID_BASICIDE_ARG_LINE); + if ( pLineItem ) + { + pModWin->AssertValidEditEngine(); + TextView* pTextView = pModWin->GetEditView(); + if ( pTextView ) + { + TextEngine* pTextEngine = pTextView->GetTextEngine(); + if ( pTextEngine ) + { + sal_uInt32 nLine = pLineItem->GetValue(); + sal_uInt32 nLineCount = 0; + for ( sal_uInt32 i = 0, nCount = pTextEngine->GetParagraphCount(); i < nCount; ++i ) + nLineCount += pTextEngine->GetLineCount( i ); + if ( nLine > nLineCount ) + nLine = nLineCount; + if ( nLine > 0 ) + --nLine; + + // scroll window and set selection + tools::Long nVisHeight = pModWin->GetOutputSizePixel().Height(); + tools::Long nTextHeight = pTextEngine->GetTextHeight(); + if ( nTextHeight > nVisHeight ) + { + tools::Long nMaxY = nTextHeight - nVisHeight; + tools::Long nOldY = pTextView->GetStartDocPos().Y(); + tools::Long nNewY = nLine * pTextEngine->GetCharHeight() - nVisHeight / 2; + nNewY = std::min( nNewY, nMaxY ); + pTextView->Scroll( 0, -( nNewY - nOldY ) ); + pTextView->ShowCursor( false ); + pModWin->GetEditVScrollBar().SetThumbPos( pTextView->GetStartDocPos().Y() ); + } + sal_uInt16 nCol1 = 0, nCol2 = 0; + const SfxUInt16Item* pCol1Item = rReq.GetArg<SfxUInt16Item>(SID_BASICIDE_ARG_COLUMN1); + if ( pCol1Item ) + { + nCol1 = pCol1Item->GetValue(); + if ( nCol1 > 0 ) + --nCol1; + nCol2 = nCol1; + } + const SfxUInt16Item* pCol2Item = rReq.GetArg<SfxUInt16Item>(SID_BASICIDE_ARG_COLUMN2); + if ( pCol2Item ) + { + nCol2 = pCol2Item->GetValue(); + if ( nCol2 > 0 ) + --nCol2; + } + TextSelection aSel( TextPaM( nLine, nCol1 ), TextPaM( nLine, nCol2 ) ); + pTextView->SetSelection( aSel ); + pTextView->ShowCursor(); + vcl::Window* pWindow_ = pTextView->GetWindow(); + if ( pWindow_ ) + pWindow_->GrabFocus(); + } + } + } + } + } + } + rReq.Done(); + } + break; + + case SID_BASICIDE_MANAGE_LANG: + { + auto pRequest = std::make_shared<SfxRequest>(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + auto xDlg = std::make_shared<ManageLanguageDialog>(pCurWin ? pCurWin->GetFrameWeld() : nullptr, m_pCurLocalizationMgr); + weld::DialogController::runAsync(xDlg, [=](sal_Int32 /*nResult*/){ + pRequest->Done(); + }); + } + break; + + case SID_ATTR_ZOOMSLIDER: + { + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + + if (pArgs && pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) == SfxItemState::SET) + SetGlobalEditorZoomLevel(static_cast<const SvxZoomSliderItem*>(pItem)->GetValue()); + + lcl_InvalidateZoomSlots(GetBindingsPtr()); + } + break; + + case SID_ZOOM_IN: + case SID_ZOOM_OUT: + { + const sal_uInt16 nOldZoom = GetCurrentZoomSliderValue(); + sal_uInt16 nNewZoom; + if (nSlot == SID_ZOOM_IN) + nNewZoom = std::min<sal_uInt16>(GetMaxZoom(), basegfx::zoomtools::zoomIn(nOldZoom)); + else + nNewZoom = std::max<sal_uInt16>(GetMinZoom(), basegfx::zoomtools::zoomOut(nOldZoom)); + SetGlobalEditorZoomLevel(nNewZoom); + lcl_InvalidateZoomSlots(GetBindingsPtr()); + } + break; + + default: + if (pLayout) + pLayout->ExecuteGlobal(rReq); + if (pCurWin) + pCurWin->ExecuteGlobal(rReq); + break; + } +} + +void Shell::GetState(SfxItemSet &rSet) +{ + SfxWhichIter aIter(rSet); + for ( sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich() ) + { + switch ( nWh ) + { + case SID_NEWDOCDIRECT: + { + // we do not have a new document factory, + // so just forward to a fallback method. + SfxGetpApp()->GetSlotState(nWh, nullptr, &rSet); + } + break; + case SID_DOCINFO: + { + rSet.DisableItem( nWh ); + } + break; + case SID_SAVEDOC: + { + bool bDisable = false; + + if ( pCurWin ) + { + if ( !pCurWin->IsModified() ) + { + ScriptDocument aDocument( pCurWin->GetDocument() ); + bDisable = ( !aDocument.isAlive() ) + || ( aDocument.isDocument() ? !aDocument.isDocumentModified() : !IsAppBasicModified() ); + } + } + else + { + bDisable = true; + } + + if ( bDisable ) + rSet.DisableItem( nWh ); + } + break; + case SID_NEWWINDOW: + case SID_SAVEASDOC: + { + rSet.DisableItem( nWh ); + } + break; + case SID_SIGNATURE: + { + SignatureState nState = SignatureState::NOSIGNATURES; + if ( pCurWin ) + { + DocumentSignature aSignature( pCurWin->GetDocument() ); + nState = aSignature.getScriptingSignatureState(); + } + rSet.Put( SfxUInt16Item( SID_SIGNATURE, static_cast<sal_uInt16>(nState) ) ); + } + break; + case SID_BASICIDE_MODULEDLG: + { + if ( StarBASIC::IsRunning() ) + rSet.DisableItem( nWh ); + } + break; + + case SID_BASICIDE_OBJCAT: + { + if (pLayout) + rSet.Put(SfxBoolItem(nWh, aObjectCatalog->IsVisible())); + else + rSet.Put(SfxVisibilityItem(nWh, false)); + } + break; + + case SID_BASICIDE_WATCH: + { + if (pLayout) + { + rSet.Put(SfxBoolItem(nWh, pModulLayout->IsWatchWindowVisible())); + // Disable command if the visible window is not a ModulWindow + if (!dynamic_cast<ModulWindowLayout*>(pLayout.get())) + rSet.DisableItem(nWh); + } + else + rSet.Put(SfxVisibilityItem(nWh, false)); + } + break; + + case SID_BASICIDE_STACK: + { + if (pLayout) + { + rSet.Put(SfxBoolItem(nWh, pModulLayout->IsStackWindowVisible())); + // Disable command if the visible window is not a ModulWindow + if (!dynamic_cast<ModulWindowLayout*>(pLayout.get())) + rSet.DisableItem(nWh); + } + else + rSet.Put(SfxVisibilityItem(nWh, false)); + } + break; + + case SID_BASICIDE_SHOWSBX: + case SID_BASICIDE_CREATEMACRO: + case SID_BASICIDE_EDITMACRO: + case SID_BASICIDE_NAMECHANGEDONTAB: + { + ; + } + break; + + case SID_BASICIDE_ADDWATCH: + case SID_BASICIDE_REMOVEWATCH: + case SID_BASICLOAD: + case SID_BASICSAVEAS: + case SID_BASICIDE_MATCHGROUP: + { + if (!dynamic_cast<ModulWindow*>(pCurWin.get())) + rSet.DisableItem( nWh ); + else if ( ( nWh == SID_BASICLOAD ) && ( StarBASIC::IsRunning() || ( pCurWin && pCurWin->IsReadOnly() ) ) ) + rSet.DisableItem( nWh ); + } + break; + case SID_BASICRUN: + case SID_BASICSTEPINTO: + case SID_BASICSTEPOVER: + case SID_BASICSTEPOUT: + case SID_BASICIDE_TOGGLEBRKPNT: + case SID_BASICIDE_MANAGEBRKPNTS: + { + if (ModulWindow* pMCurWin = dynamic_cast<ModulWindow*>(pCurWin.get())) + { + if (StarBASIC::IsRunning() && !pMCurWin->GetBasicStatus().bIsInReschedule) + rSet.DisableItem(nWh); + } + else + rSet.DisableItem( nWh ); + } + break; + case SID_BASICCOMPILE: + { + if (StarBASIC::IsRunning() || !dynamic_cast<ModulWindow*>(pCurWin.get())) + rSet.DisableItem( nWh ); + } + break; + case SID_BASICSTOP: + { + // stop is always possible when some Basic is running... + if (!StarBASIC::IsRunning()) + rSet.DisableItem( nWh ); + } + break; + case SID_CHOOSE_CONTROLS: + case SID_DIALOG_TESTMODE: + case SID_INSERT_SELECT: + case SID_INSERT_PUSHBUTTON: + case SID_INSERT_RADIOBUTTON: + case SID_INSERT_CHECKBOX: + case SID_INSERT_LISTBOX: + case SID_INSERT_COMBOBOX: + case SID_INSERT_GROUPBOX: + case SID_INSERT_EDIT: + case SID_INSERT_FIXEDTEXT: + case SID_INSERT_IMAGECONTROL: + case SID_INSERT_PROGRESSBAR: + case SID_INSERT_HSCROLLBAR: + case SID_INSERT_VSCROLLBAR: + case SID_INSERT_HFIXEDLINE: + case SID_INSERT_VFIXEDLINE: + case SID_INSERT_DATEFIELD: + case SID_INSERT_TIMEFIELD: + case SID_INSERT_NUMERICFIELD: + case SID_INSERT_CURRENCYFIELD: + case SID_INSERT_FORMATTEDFIELD: + case SID_INSERT_PATTERNFIELD: + case SID_INSERT_FILECONTROL: + case SID_INSERT_SPINBUTTON: + case SID_INSERT_GRIDCONTROL: + case SID_INSERT_HYPERLINKCONTROL: + case SID_INSERT_TREECONTROL: + case SID_INSERT_FORM_RADIO: + case SID_INSERT_FORM_CHECK: + case SID_INSERT_FORM_LIST: + case SID_INSERT_FORM_COMBO: + case SID_INSERT_FORM_VSCROLL: + case SID_INSERT_FORM_HSCROLL: + case SID_INSERT_FORM_SPIN: + { + if (!dynamic_cast<DialogWindow*>(pCurWin.get())) + rSet.DisableItem( nWh ); + } + break; + case SID_SEARCH_OPTIONS: + { + SearchOptionFlags nOptions = SearchOptionFlags::NONE; + if( pCurWin ) + nOptions = pCurWin->GetSearchOptions(); + rSet.Put( SfxUInt16Item( SID_SEARCH_OPTIONS, static_cast<sal_uInt16>(nOptions) ) ); + } + break; + case SID_BASICIDE_LIBSELECTOR: + { + OUString aName; + if ( !m_aCurLibName.isEmpty() ) + { + LibraryLocation eLocation = m_aCurDocument.getLibraryLocation( m_aCurLibName ); + aName = CreateMgrAndLibStr( m_aCurDocument.getTitle( eLocation ), m_aCurLibName ); + } + SfxStringItem aItem( SID_BASICIDE_LIBSELECTOR, aName ); + rSet.Put( aItem ); + } + break; + case SID_SEARCH_ITEM: + { + if ( !mpSearchItem ) + { + mpSearchItem.reset( new SvxSearchItem( SID_SEARCH_ITEM )); + mpSearchItem->SetSearchString( GetSelectionText( true )); + } + + if ( mbJustOpened && HasSelection() ) + { + OUString aText = GetSelectionText( true ); + + if ( !aText.isEmpty() ) + { + mpSearchItem->SetSearchString( aText ); + mpSearchItem->SetSelection( false ); + } + else + mpSearchItem->SetSelection( true ); + } + + mbJustOpened = false; + rSet.Put( *mpSearchItem ); + } + break; + case SID_BASICIDE_STAT_DATE: + { + SfxStringItem aItem( SID_BASICIDE_STAT_DATE, "Datum?!" ); + rSet.Put( aItem ); + } + break; + case SID_DOC_MODIFIED: + { + bool bModified = false; + + if ( pCurWin ) + { + if ( pCurWin->IsModified() ) + bModified = true; + else + { + ScriptDocument aDocument( pCurWin->GetDocument() ); + bModified = aDocument.isDocument() ? aDocument.isDocumentModified() : IsAppBasicModified(); + } + } + + SfxBoolItem aItem(SID_DOC_MODIFIED, bModified); + rSet.Put( aItem ); + } + break; + case SID_BASICIDE_STAT_TITLE: + { + if ( pCurWin ) + { + OUString aTitle = pCurWin->CreateQualifiedName(); + if (pCurWin->IsReadOnly()) + aTitle += " (" + IDEResId(RID_STR_READONLY) + ")"; + SfxStringItem aItem( SID_BASICIDE_STAT_TITLE, aTitle ); + rSet.Put( aItem ); + } + } + break; + case SID_BASICIDE_CURRENT_ZOOM: + { + // The current zoom value is only visible in a module window + ModulWindow* pModuleWindow = dynamic_cast<ModulWindow*>(pCurWin.get()); + if (pModuleWindow) + { + OUString sZoom; + sZoom = OUString::number(m_nCurrentZoomSliderValue) + "%"; + SfxStringItem aItem( SID_BASICIDE_CURRENT_ZOOM, sZoom ); + rSet.Put( aItem ); + } + } + break; + // are interpreted by the controller: + case SID_ATTR_SIZE: + case SID_ATTR_INSERT: + break; + case SID_UNDO: + case SID_REDO: + { + if( GetUndoManager() ) // recursive GetState else + GetViewFrame().GetSlotState( nWh, nullptr, &rSet ); + } + break; + case SID_BASICIDE_CURRENT_LANG: + { + if( (pCurWin && pCurWin->IsReadOnly()) || GetCurLibName().isEmpty() ) + rSet.DisableItem( nWh ); + else + { + OUString aItemStr; + std::shared_ptr<LocalizationMgr> pCurMgr(GetCurLocalizationMgr()); + if ( pCurMgr->isLibraryLocalized() ) + { + Sequence< lang::Locale > aLocaleSeq = pCurMgr->getStringResourceManager()->getLocales(); + const lang::Locale* pLocale = aLocaleSeq.getConstArray(); + sal_Int32 i, nCount = aLocaleSeq.getLength(); + + // Force different results for any combination of locales and default locale + OUString aLangStr; + for ( i = 0; i <= nCount; ++i ) + { + lang::Locale aLocale; + if( i < nCount ) + aLocale = pLocale[i]; + else + aLocale = pCurMgr->getStringResourceManager()->getDefaultLocale(); + + aLangStr += aLocale.Language + aLocale.Country + aLocale.Variant; + } + aItemStr = aLangStr; + } + rSet.Put( SfxStringItem( nWh, aItemStr ) ); + } + } + break; + + case SID_BASICIDE_MANAGE_LANG: + { + if( (pCurWin && pCurWin->IsReadOnly()) || GetCurLibName().isEmpty() ) + rSet.DisableItem( nWh ); + } + break; + case SID_GOTOLINE: + { + // if this is not a module window hide the + // setting, doesn't make sense for example if the + // dialog editor is open + if (pCurWin && !dynamic_cast<ModulWindow*>(pCurWin.get())) + { + rSet.DisableItem( nWh ); + rSet.Put(SfxVisibilityItem(nWh, false)); + } + break; + } + case SID_BASICIDE_HIDECURPAGE: + { + if (pTabBar->GetPageCount() == 0) + rSet.DisableItem(nWh); + } + break; + case SID_BASICIDE_DELETECURRENT: + case SID_BASICIDE_RENAMECURRENT: + { + if (pTabBar->GetPageCount() == 0 || StarBASIC::IsRunning()) + rSet.DisableItem(nWh); + else if (m_aCurDocument.isInVBAMode()) + { + // disable to delete or rename object modules in IDE + BasicManager* pBasMgr = m_aCurDocument.getBasicManager(); + StarBASIC* pBasic = pBasMgr ? pBasMgr->GetLib(m_aCurLibName) : nullptr; + if (pBasic && dynamic_cast<ModulWindow*>(pCurWin.get())) + { + SbModule* pActiveModule = pBasic->FindModule( pCurWin->GetName() ); + if ( pActiveModule && ( pActiveModule->GetModuleType() == script::ModuleType::DOCUMENT ) ) + rSet.DisableItem(nWh); + } + } + } + [[fallthrough]]; + + case SID_BASICIDE_NEWMODULE: + case SID_BASICIDE_NEWDIALOG: + { + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( m_aCurLibName ) && xModLibContainer->isLibraryReadOnly( m_aCurLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( m_aCurLibName ) && xDlgLibContainer->isLibraryReadOnly( m_aCurLibName ) ) ) + rSet.DisableItem(nWh); + } + break; + + case SID_ZOOM_IN: + case SID_ZOOM_OUT: + { + const sal_uInt16 nCurrentZoom = GetCurrentZoomSliderValue(); + if ((nWh == SID_ZOOM_IN && nCurrentZoom >= GetMaxZoom()) || + (nWh == SID_ZOOM_OUT && nCurrentZoom <= GetMinZoom())) + rSet.DisableItem(nWh); + } + break; + + case SID_ATTR_ZOOMSLIDER: + { + // The zoom slider is only visible in a module window + ModulWindow* pModuleWindow = dynamic_cast<ModulWindow*>(pCurWin.get()); + if (pModuleWindow) + { + SvxZoomSliderItem aZoomSliderItem(GetCurrentZoomSliderValue(), GetMinZoom(), GetMaxZoom()); + aZoomSliderItem.AddSnappingPoint(100); + rSet.Put( aZoomSliderItem ); + } + } + break; + + default: + if (pLayout) + pLayout->GetState(rSet, nWh); + } + } + if ( pCurWin ) + pCurWin->GetState( rSet ); +} + +bool Shell::HasUIFeature(SfxShellFeature nFeature) const +{ + assert((nFeature & ~SfxShellFeature::BasicMask) == SfxShellFeature::NONE); + bool bResult = false; + + if (nFeature & SfxShellFeature::BasicShowBrowser) + { + // fade out (in) property browser in module (dialog) windows + if (dynamic_cast<DialogWindow*>(pCurWin.get()) && !pCurWin->IsReadOnly()) + bResult = true; + } + + return bResult; +} + +void Shell::SetCurWindow( BaseWindow* pNewWin, bool bUpdateTabBar, bool bRememberAsCurrent ) +{ + if ( pNewWin == pCurWin ) + return; + + pCurWin = pNewWin; + if (pLayout) + pLayout->Deactivating(); + if (pCurWin) + { + if (pCurWin->GetType() == TYPE_MODULE) + pLayout = pModulLayout.get(); + else + pLayout = pDialogLayout.get(); + AdjustPosSizePixel(Point(0, 0), GetViewFrame().GetWindow().GetOutputSizePixel()); + pLayout->Activating(*pCurWin); + GetViewFrame().GetWindow().SetHelpId(pCurWin->GetHid()); + if (bRememberAsCurrent) + pCurWin->InsertLibInfo(); + if (GetViewFrame().GetWindow().IsVisible()) // SFX will do it later otherwise + pCurWin->Show(); + pCurWin->Init(); + if (!GetExtraData()->ShellInCriticalSection()) + { + vcl::Window* pFrameWindow = &GetViewFrame().GetWindow(); + vcl::Window* pFocusWindow = Application::GetFocusWindow(); + while ( pFocusWindow && ( pFocusWindow != pFrameWindow ) ) + pFocusWindow = pFocusWindow->GetParent(); + if ( pFocusWindow ) // Focus in BasicIDE + pCurWin->GrabFocus(); + } + } + else + { + SetWindow(pLayout); + pLayout = nullptr; + } + if ( bUpdateTabBar ) + { + sal_uInt16 nKey = GetWindowId( pCurWin ); + if ( pCurWin && ( pTabBar->GetPagePos( nKey ) == TabBar::PAGE_NOT_FOUND ) ) + pTabBar->InsertPage( nKey, pCurWin->GetTitle() ); // has just been faded in + pTabBar->SetCurPageId( nKey ); + } + if ( pCurWin && pCurWin->IsSuspended() ) // if the window is shown in the case of an error... + pCurWin->SetStatus( pCurWin->GetStatus() & ~BASWIN_SUSPENDED ); + if ( pCurWin ) + { + SetWindow( pCurWin ); + if ( pCurWin->GetDocument().isDocument() ) + SfxObjectShell::SetCurrentComponent( pCurWin->GetDocument().getDocument() ); + } + else if (pLayout) + { + SetWindow(pLayout); + GetViewFrame().GetWindow().SetHelpId( HID_BASICIDE_MODULWINDOW ); + SfxObjectShell::SetCurrentComponent(nullptr); + } + aObjectCatalog->SetCurrentEntry(pCurWin); + SetUndoManager( pCurWin ? pCurWin->GetUndoManager() : nullptr ); + InvalidateBasicIDESlots(); + InvalidateControlSlots(); + + if ( m_pCurLocalizationMgr ) + m_pCurLocalizationMgr->handleTranslationbar(); + + ManageToolbars(); + + // fade out (in) property browser in module (dialog) windows + UIFeatureChanged(); +} + +void Shell::ManageToolbars() +{ + static constexpr OUString aMacroBarResName = u"private:resource/toolbar/macrobar"_ustr; + static constexpr OUString aDialogBarResName = u"private:resource/toolbar/dialogbar"_ustr; + static constexpr OUString aInsertControlsBarResName + = u"private:resource/toolbar/insertcontrolsbar"_ustr; + static constexpr OUString aFormControlsBarResName + = u"private:resource/toolbar/formcontrolsbar"_ustr; + + if( !pCurWin ) + return; + + Reference< beans::XPropertySet > xFrameProps + ( GetViewFrame().GetFrame().GetFrameInterface(), uno::UNO_QUERY ); + if ( !xFrameProps.is() ) + return; + + Reference< css::frame::XLayoutManager > xLayoutManager; + uno::Any a = xFrameProps->getPropertyValue( "LayoutManager" ); + a >>= xLayoutManager; + if ( !xLayoutManager.is() ) + return; + + xLayoutManager->lock(); + if (dynamic_cast<DialogWindow*>(pCurWin.get())) + { + xLayoutManager->destroyElement( aMacroBarResName ); + + xLayoutManager->requestElement( aDialogBarResName ); + xLayoutManager->requestElement( aInsertControlsBarResName ); + xLayoutManager->requestElement( aFormControlsBarResName ); + } + else + { + xLayoutManager->destroyElement( aDialogBarResName ); + xLayoutManager->destroyElement( aInsertControlsBarResName ); + xLayoutManager->destroyElement( aFormControlsBarResName ); + + xLayoutManager->requestElement( aMacroBarResName ); + } + xLayoutManager->unlock(); +} + +VclPtr<BaseWindow> Shell::FindApplicationWindow() +{ + return FindWindow( ScriptDocument::getApplicationScriptDocument(), u"", u"", TYPE_UNKNOWN ); +} + +VclPtr<BaseWindow> Shell::FindWindow( + ScriptDocument const& rDocument, + std::u16string_view rLibName, std::u16string_view rName, + ItemType eType, bool bFindSuspended +) +{ + for (auto const& window : aWindowTable) + { + BaseWindow* const pWin = window.second; + if (pWin->Is(rDocument, rLibName, rName, eType, bFindSuspended)) + return pWin; + } + return nullptr; +} + +bool Shell::CallBasicErrorHdl( StarBASIC const * pBasic ) +{ + VclPtr<ModulWindow> pModWin = ShowActiveModuleWindow( pBasic ); + if ( pModWin ) + pModWin->BasicErrorHdl( pBasic ); + return false; +} + +BasicDebugFlags Shell::CallBasicBreakHdl( StarBASIC const * pBasic ) +{ + BasicDebugFlags nRet = BasicDebugFlags::NONE; + VclPtr<ModulWindow> pModWin = ShowActiveModuleWindow( pBasic ); + if ( pModWin ) + { + bool bAppWindowDisabled, bDispatcherLocked; + sal_uInt16 nWaitCount; + SfxUInt16Item *pSWActionCount, *pSWLockViewCount; + BasicStopped( &bAppWindowDisabled, &bDispatcherLocked, + &nWaitCount, &pSWActionCount, &pSWLockViewCount ); + + nRet = pModWin->BasicBreakHdl(); + + if ( StarBASIC::IsRunning() ) // if cancelled... + { + if ( bAppWindowDisabled ) + Application::GetDefDialogParent()->set_sensitive(false); + + if ( nWaitCount ) + { + Shell* pShell = GetShell(); + for ( sal_uInt16 n = 0; n < nWaitCount; n++ ) + pShell->GetViewFrame().GetWindow().EnterWait(); + } + } + } + return nRet; +} + +VclPtr<ModulWindow> Shell::ShowActiveModuleWindow( StarBASIC const * pBasic ) +{ + SetCurLib( ScriptDocument::getApplicationScriptDocument(), OUString(), false ); + + SbModule* pActiveModule = StarBASIC::GetActiveModule(); + if (SbClassModuleObject* pCMO = dynamic_cast<SbClassModuleObject*>(pActiveModule)) + pActiveModule = pCMO->getClassModule(); + + DBG_ASSERT( pActiveModule, "No active module in ErrorHdl!?" ); + if ( pActiveModule ) + { + VclPtr<ModulWindow> pWin; + SbxObject* pParent = pActiveModule->GetParent(); + if (StarBASIC* pLib = dynamic_cast<StarBASIC*>(pParent)) + { + if (BasicManager* pBasMgr = FindBasicManager(pLib)) + { + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + const OUString& aLibName = pLib->GetName(); + pWin = FindBasWin( aDocument, aLibName, pActiveModule->GetName(), true ); + DBG_ASSERT( pWin, "Error/Step-Hdl: Window was not created/found!" ); + SetCurLib( aDocument, aLibName ); + SetCurWindow( pWin, true ); + } + } + else + SAL_WARN( "basctl.basicide", "No BASIC!"); + if (BasicManager* pBasicMgr = FindBasicManager(pBasic)) + StartListening(*pBasicMgr, DuplicateHandling::Prevent /* log on only once */); + return pWin; + } + return nullptr; +} + +void Shell::AdjustPosSizePixel( const Point &rPos, const Size &rSize ) +{ + // not if iconified because the whole text would be displaced then at restore + if ( GetViewFrame().GetWindow().GetOutputSizePixel().Height() == 0 ) + return; + + Size aTabBarSize; + aTabBarSize.setHeight( GetViewFrame().GetWindow().GetFont().GetFontHeight() + TAB_HEIGHT_MARGIN ); + aTabBarSize.setWidth( rSize.Width() ); + + Size aSz( rSize ); + auto nScrollBarSz(Application::GetSettings().GetStyleSettings().GetScrollBarSize()); + aSz.AdjustHeight(-aTabBarSize.Height()); + + Size aOutSz( aSz ); + aSz.AdjustWidth(-nScrollBarSz); + aSz.AdjustHeight(-nScrollBarSz); + aVScrollBar->SetPosSizePixel( Point( rPos.X()+aSz.Width(), rPos.Y() ), Size( nScrollBarSz, aSz.Height() ) ); + aHScrollBar->SetPosSizePixel( Point( rPos.X(), rPos.Y()+aSz.Height() ), Size( aOutSz.Width(), nScrollBarSz ) ); + pTabBar->SetPosSizePixel( Point( rPos.X(), rPos.Y() + nScrollBarSz + aSz.Height()), aTabBarSize ); + + // The size to be applied depends on whether it is a DialogWindow or a ModulWindow + if (pLayout) + { + if (dynamic_cast<DialogWindow*>(pCurWin.get())) + { + pCurWin->ShowShellScrollBars(); + pLayout->SetPosSizePixel(rPos, aSz); + } + else + { + pCurWin->ShowShellScrollBars(false); + pLayout->SetPosSizePixel(rPos, aOutSz); + } + } +} + +Reference< XModel > Shell::GetCurrentDocument() const +{ + Reference< XModel > xDocument; + if ( pCurWin && pCurWin->GetDocument().isDocument() ) + xDocument = pCurWin->GetDocument().getDocument(); + return xDocument; +} + +void Shell::Activate( bool bMDI ) +{ + SfxViewShell::Activate( bMDI ); + + if ( bMDI ) + { + if (DialogWindow* pDCurWin = dynamic_cast<DialogWindow*>(pCurWin.get())) + pDCurWin->UpdateBrowser(); + } +} + +void Shell::Deactivate( bool bMDI ) +{ + // bMDI == true means that another MDI has been activated; in case of a + // deactivate due to a MessageBox bMDI is false + if ( bMDI ) + { + if (DialogWindow* pXDlgWin = dynamic_cast<DialogWindow*>(pCurWin.get())) + { + pXDlgWin->DisableBrowser(); + if( pXDlgWin->IsModified() ) + MarkDocumentModified( pXDlgWin->GetDocument() ); + } + } +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basides2.cxx b/basctl/source/basicide/basides2.cxx new file mode 100644 index 0000000000..5bd69b76f3 --- /dev/null +++ b/basctl/source/basicide/basides2.cxx @@ -0,0 +1,239 @@ +/* -*- 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 <docsignature.hxx> + +#include "basicrenderable.hxx" + +#include <com/sun/star/frame/XTitle.hpp> + +#include <iderid.hxx> +#include <strings.hrc> +#include "baside2.hxx" +#include "basdoc.hxx" +#include <basidesh.hxx> +#include <tools/debug.hxx> +#include <vcl/texteng.hxx> +#include <vcl/textview.hxx> +#include <sfx2/signaturestate.hxx> +#include <sfx2/viewfrm.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +Reference< view::XRenderable > Shell::GetRenderable() +{ + return Reference<view::XRenderable>( new Renderable(pCurWin) ); +} + +bool Shell::HasSelection( bool /* bText */ ) const +{ + if (ModulWindow* pMCurWin = dynamic_cast<ModulWindow*>(pCurWin.get())) + { + TextView* pEditView = pMCurWin->GetEditView(); + if ( pEditView && pEditView->HasSelection() ) + return true; + } + return false; +} + +OUString Shell::GetSelectionText( bool bWholeWord, bool /*bOnlyASample*/ ) +{ + OUString aText; + if (ModulWindow* pMCurWin = dynamic_cast<ModulWindow*>(pCurWin.get())) + { + if (TextView* pEditView = pMCurWin->GetEditView()) + { + if ( bWholeWord && !pEditView->HasSelection() ) + { + aText = pEditView->GetTextEngine()->GetWord( pEditView->GetSelection().GetEnd() ); + } + else + { + TextSelection aSel = pEditView->GetSelection(); + if ( !bWholeWord || ( aSel.GetStart().GetPara() == aSel.GetEnd().GetPara() ) ) + aText = pEditView->GetSelected(); + } + } + } + return aText; +} + +SfxPrinter* Shell::GetPrinter( bool bCreate ) +{ + if ( pCurWin ) + { + DocShell* pDocShell = static_cast<DocShell*>(GetViewFrame().GetObjectShell()); + assert(pDocShell && "DocShell ?!"); + return pDocShell->GetPrinter( bCreate ); + } + return nullptr; +} + +sal_uInt16 Shell::SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags ) +{ + DocShell* pDocShell = static_cast<DocShell*>(GetViewFrame().GetObjectShell()); + assert(pDocShell && "DocShell ?!"); + pDocShell->SetPrinter( pNewPrinter ); + return 0; +} + +void Shell::SetMDITitle() +{ + OUString aTitle; + if ( !m_aCurLibName.isEmpty() ) + { + LibraryLocation eLocation = m_aCurDocument.getLibraryLocation( m_aCurLibName ); + aTitle = m_aCurDocument.getTitle(eLocation) + "." + m_aCurLibName ; + } + else + aTitle = IDEResId(RID_STR_ALL) ; + + DocumentSignature aCurSignature( m_aCurDocument ); + if ( aCurSignature.getScriptingSignatureState() == SignatureState::OK ) + { + aTitle += " " + IDEResId(RID_STR_SIGNED) + " "; + } + + SfxViewFrame& rViewFrame = GetViewFrame(); + SfxObjectShell* pShell = rViewFrame.GetObjectShell(); + if ( pShell && pShell->GetTitle( SFX_TITLE_CAPTION ) != aTitle ) + { + pShell->SetTitle( aTitle ); + pShell->SetModified(false); + } + + css::uno::Reference< css::frame::XController > xController = GetController (); + css::uno::Reference< css::frame::XTitle > xTitle (xController, css::uno::UNO_QUERY); + if (xTitle.is ()) + xTitle->setTitle (aTitle); +} + +VclPtr<ModulWindow> Shell::CreateBasWin( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName ) +{ + bCreatingWindow = true; + + sal_uInt16 nKey = 0; + VclPtr<ModulWindow> pWin; + + OUString aLibName( rLibName ); + OUString aModName( rModName ); + + if ( aLibName.isEmpty() ) + aLibName = "Standard" ; + + uno::Reference< container::XNameContainer > xLib = rDocument.getOrCreateLibrary( E_SCRIPTS, aLibName ); + + if ( aModName.isEmpty() ) + aModName = rDocument.createObjectName( E_SCRIPTS, aLibName ); + + // maybe there's an suspended one? + pWin = FindBasWin( rDocument, aLibName, aModName, false, true ); + + if ( !pWin ) + { + OUString aModule; + bool bSuccess = false; + if ( rDocument.hasModule( aLibName, aModName ) ) + bSuccess = rDocument.getModule( aLibName, aModName, aModule ); + else + bSuccess = rDocument.createModule( aLibName, aModName, true, aModule ); + + if ( bSuccess ) + { + pWin = FindBasWin( rDocument, aLibName, aModName, false, true ); + if( !pWin ) + { + // new module window + if (!pModulLayout) + pModulLayout.reset(VclPtr<ModulWindowLayout>::Create(&GetViewFrame().GetWindow(), *aObjectCatalog)); + pWin = VclPtr<ModulWindow>::Create(pModulLayout.get(), rDocument, aLibName, aModName, aModule); + nKey = InsertWindowInTable( pWin ); + } + else // we've gotten called recursively ( via listener from createModule above ), get outta here + return pWin; + } + } + else + { + pWin->SetStatus( pWin->GetStatus() & ~BASWIN_SUSPENDED ); + nKey = GetWindowId( pWin ); + DBG_ASSERT( nKey, "CreateBasWin: No Key - Window not found!" ); + } + if( nKey && xLib.is() && rDocument.isInVBAMode() ) + { + // display a nice friendly name in the ObjectModule tab, + // combining the objectname and module name, e.g. Sheet1 ( Financials ) + OUString sObjName; + ModuleInfoHelper::getObjectName( xLib, rModName, sObjName ); + if( !sObjName.isEmpty() ) + { + aModName += " (" + sObjName + ")"; + } + } + pTabBar->InsertPage( nKey, aModName ); + pTabBar->Sort(); + if(pWin) + { + pWin->GrabScrollBars( aHScrollBar.get(), aVScrollBar.get() ); + if ( !pCurWin ) + SetCurWindow( pWin, false, false ); + } + bCreatingWindow = false; + return pWin; +} + +VclPtr<ModulWindow> Shell::FindBasWin ( + ScriptDocument const& rDocument, + OUString const& rLibName, OUString const& rName, + bool bCreateIfNotExist, bool bFindSuspended +) +{ + if (VclPtr<BaseWindow> pWin = FindWindow(rDocument, rLibName, rName, TYPE_MODULE, bFindSuspended)) + return VclPtr<ModulWindow>(static_cast<ModulWindow*>(pWin.get())); + return bCreateIfNotExist ? CreateBasWin(rDocument, rLibName, rName) : nullptr; +} + +void Shell::Move() +{ +} + +void Shell::ShowCursor( bool bOn ) +{ + if (ModulWindow* pMCurWin = dynamic_cast<ModulWindow*>(pCurWin.get())) + pMCurWin->ShowCursor(bOn); +} + +// only if basic window above: +void Shell::ExecuteBasic( SfxRequest& rReq ) +{ + if (dynamic_cast<ModulWindow*>(pCurWin.get())) + { + pCurWin->ExecuteCommand( rReq ); + if (nShellCount) + CheckWindows(); + } +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basides3.cxx b/basctl/source/basicide/basides3.cxx new file mode 100644 index 0000000000..44bc54ba62 --- /dev/null +++ b/basctl/source/basicide/basides3.cxx @@ -0,0 +1,150 @@ +/* -*- 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 <baside3.hxx> +#include <basidesh.hxx> +#include <localizationmgr.hxx> +#include <dlgedview.hxx> +#include <xmlscript/xmldlg_imexp.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace basctl +{ + +using namespace comphelper; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; + +VclPtr<DialogWindow> Shell::CreateDlgWin( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rDlgName ) +{ + bCreatingWindow = true; + + sal_uInt16 nKey = 0; + VclPtr<DialogWindow> pWin; + OUString aLibName( rLibName ); + OUString aDlgName( rDlgName ); + + if ( aLibName.isEmpty() ) + aLibName = "Standard" ; + + rDocument.getOrCreateLibrary( E_DIALOGS, aLibName ); + + if ( aDlgName.isEmpty() ) + aDlgName = rDocument.createObjectName( E_DIALOGS, aLibName ); + + // maybe there's a suspended one? + pWin = FindDlgWin( rDocument, aLibName, aDlgName, false, true ); + + if ( !pWin ) + { + try + { + Reference< io::XInputStreamProvider > xISP; + if ( rDocument.hasDialog( aLibName, aDlgName ) ) + rDocument.getDialog( aLibName, aDlgName, xISP ); + else + rDocument.createDialog( aLibName, aDlgName, xISP ); + + if ( xISP.is() ) + { + // create dialog model + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + Reference< container::XNameContainer > xDialogModel( xContext->getServiceManager()->createInstanceWithContext + ( "com.sun.star.awt.UnoControlDialogModel", xContext ), UNO_QUERY ); + Reference< XInputStream > xInput( xISP->createInputStream() ); + ::xmlscript::importDialogModel( xInput, xDialogModel, xContext, rDocument.isDocument() ? rDocument.getDocument() : Reference< frame::XModel >() ); + LocalizationMgr::setStringResourceAtDialog( rDocument, rLibName, aDlgName, xDialogModel ); + + // new dialog window + if (!pDialogLayout) + pDialogLayout.reset(VclPtr<DialogWindowLayout>::Create(&GetViewFrame().GetWindow(), *aObjectCatalog)); + pWin = VclPtr<DialogWindow>::Create(pDialogLayout.get(), rDocument, aLibName, aDlgName, xDialogModel); + nKey = InsertWindowInTable( pWin ); + } + } + catch (const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + else + { + pWin->SetStatus( pWin->GetStatus() & ~BASWIN_SUSPENDED ); + nKey = GetWindowId( pWin ); + DBG_ASSERT( nKey, "CreateDlgWin: No Key - Window not found!" ); + } + + if( pWin ) + { + pWin->GrabScrollBars( aHScrollBar.get(), aVScrollBar.get() ); + pTabBar->InsertPage( nKey, aDlgName ); + pTabBar->Sort(); + if ( !pCurWin ) + SetCurWindow( pWin, false, false ); + } + + bCreatingWindow = false; + return pWin; +} + +VclPtr<DialogWindow> Shell::FindDlgWin ( + ScriptDocument const& rDocument, + OUString const& rLibName, OUString const& rName, + bool bCreateIfNotExist, bool bFindSuspended +) +{ + if (VclPtr<BaseWindow> pWin = FindWindow(rDocument, rLibName, rName, TYPE_DIALOG, bFindSuspended)) + return static_cast<DialogWindow*>(pWin.get()); + return bCreateIfNotExist ? CreateDlgWin(rDocument, rLibName, rName) : nullptr; +} + +sal_uInt16 Shell::GetWindowId(const BaseWindow* pWin) const +{ + for (auto const& window : aWindowTable) + if ( window.second == pWin ) + return window.first; + return 0; +} + +SdrView* Shell::GetCurDlgView() const +{ + if (DialogWindow* pDCurWin = dynamic_cast<DialogWindow*>(pCurWin.get())) + return &pDCurWin->GetView(); + else + return nullptr; +} + +// only if dialogue window above: +void Shell::ExecuteDialog( SfxRequest& rReq ) +{ + if (pCurWin && (dynamic_cast<DialogWindow*>(pCurWin.get()) || rReq.GetSlot() == SID_IMPORT_DIALOG)) + pCurWin->ExecuteCommand(rReq); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basidesh.cxx b/basctl/source/basicide/basidesh.cxx new file mode 100644 index 0000000000..d23da94d26 --- /dev/null +++ b/basctl/source/basicide/basidesh.cxx @@ -0,0 +1,994 @@ +/* -*- 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 <config_options.h> + +#include <comphelper/diagnose_ex.hxx> +#include <basic/basmgr.hxx> +#include <svx/zoomsliderctrl.hxx> +#include <svx/zoomslideritem.hxx> +#include <svx/svxids.hrc> +#include <iderid.hxx> +#include <strings.hrc> +#include "baside2.hxx" +#include <baside3.hxx> +#include "basdoc.hxx" +#include <IDEComboBox.hxx> +#include <editeng/sizeitem.hxx> +#include "iderdll2.hxx" +#include <basidectrlr.hxx> +#include <basidesh.hxx> +#include <basobj.hxx> +#include <localizationmgr.hxx> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dinfdlg.hxx> +#include <sfx2/infobar.hxx> +#include <sfx2/minfitem.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/srchitem.hxx> +#include <tools/debug.hxx> +#include <unotools/viewoptions.hxx> + +#if defined(DISABLE_DYNLOADING) || ENABLE_MERGELIBS +/* Avoid clash with the ones from svx/source/form/typemap.cxx */ +#define aSfxDocumentInfoItem_Impl basctl_source_basicide_basidesh_aSfxDocumentInfoItem_Impl +#define aSfxUnoAnyItem_Impl basctl_source_basicide_basidesh_aSfxUnoAnyItem_Impl +#endif + +#define ShellClass_basctl_Shell +#define SFX_TYPEMAP +#include <basslots.hxx> + +#if defined(DISABLE_DYNLOADING) || ENABLE_MERGELIBS +#undef aSfxDocumentInfoItem_Impl +#undef aSfxUnoAnyItem_Impl +#endif + +#include <iderdll.hxx> +#include <svx/pszctrl.hxx> +#include <svx/insctrl.hxx> +#include <svx/srchdlg.hxx> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <svx/xmlsecctrl.hxx> +#include <sfx2/viewfac.hxx> +#include <vcl/weld.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <cppuhelper/implbase.hxx> + +namespace basctl +{ +constexpr OUString BASIC_IDE_EDITOR_WINDOW = u"BasicIDEEditorWindow"_ustr; +constexpr OUString BASIC_IDE_CURRENT_ZOOM = u"CurrentZoom"_ustr; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +class ContainerListenerImpl : public ::cppu::WeakImplHelper< container::XContainerListener > +{ + Shell* mpShell; +public: + explicit ContainerListenerImpl(Shell* pShell) + : mpShell(pShell) + { + } + + void addContainerListener( const ScriptDocument& rScriptDocument, const OUString& aLibName ) + { + try + { + uno::Reference< container::XContainer > xContainer( rScriptDocument.getLibrary( E_SCRIPTS, aLibName, false ), uno::UNO_QUERY ); + if ( xContainer.is() ) + { + uno::Reference< container::XContainerListener > xContainerListener( this ); + xContainer->addContainerListener( xContainerListener ); + } + } + catch(const uno::Exception& ) {} + } + void removeContainerListener( const ScriptDocument& rScriptDocument, const OUString& aLibName ) + { + try + { + uno::Reference< container::XContainer > xContainer( rScriptDocument.getLibrary( E_SCRIPTS, aLibName, false ), uno::UNO_QUERY ); + if ( xContainer.is() ) + { + uno::Reference< container::XContainerListener > xContainerListener( this ); + xContainer->removeContainerListener( xContainerListener ); + } + } + catch(const uno::Exception& ) {} + } + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& ) override {} + + // XContainerListener + virtual void SAL_CALL elementInserted( const container::ContainerEvent& Event ) override + { + OUString sModuleName; + if( mpShell && ( Event.Accessor >>= sModuleName ) ) + mpShell->FindBasWin( mpShell->m_aCurDocument, mpShell->m_aCurLibName, sModuleName, true ); + } + virtual void SAL_CALL elementReplaced( const container::ContainerEvent& ) override { } + virtual void SAL_CALL elementRemoved( const container::ContainerEvent& Event ) override + { + OUString sModuleName; + if( mpShell && ( Event.Accessor >>= sModuleName ) ) + { + VclPtr<ModulWindow> pWin = mpShell->FindBasWin(mpShell->m_aCurDocument, mpShell->m_aCurLibName, sModuleName, false, true); + if( pWin ) + mpShell->RemoveWindow( pWin, true ); + } + } + +}; + +SFX_IMPL_NAMED_VIEWFACTORY( Shell, "Default" ) +{ + SFX_VIEW_REGISTRATION( DocShell ); +} + +SFX_IMPL_INTERFACE(basctl_Shell, SfxViewShell) + +void basctl_Shell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); + GetStaticInterface()->RegisterChildWindow(SID_SHOW_PROPERTYBROWSER, false, SfxShellFeature::BasicShowBrowser); + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + + GetStaticInterface()->RegisterPopupMenu("dialog"); +} + +unsigned Shell::nShellCount = 0; + +Shell::Shell( SfxViewFrame& rFrame_, SfxViewShell* /* pOldShell */ ) : + SfxViewShell( rFrame_, SfxViewShellFlags::NO_NEWWINDOW ), + m_aCurDocument( ScriptDocument::getApplicationScriptDocument() ), + aHScrollBar( VclPtr<ScrollAdaptor>::Create(&GetViewFrame().GetWindow(), true) ), + aVScrollBar( VclPtr<ScrollAdaptor>::Create(&GetViewFrame().GetWindow(), false) ), + pLayout(nullptr), + aObjectCatalog(VclPtr<ObjectCatalog>::Create(&GetViewFrame().GetWindow())), + m_bAppBasicModified( false ), + m_aNotifier( *this ) +{ + m_xLibListener = new ContainerListenerImpl( this ); + Init(); + nShellCount++; +} + +void Shell::Init() +{ + SvxPosSizeStatusBarControl::RegisterControl(); + SvxInsertStatusBarControl::RegisterControl(); + XmlSecStatusBarControl::RegisterControl( SID_SIGNATURE ); + + SvxSearchDialogWrapper::RegisterChildWindow(); + + GetExtraData()->ShellInCriticalSection() = true; + + SetName( "BasicIDE" ); + + LibBoxControl::RegisterControl( SID_BASICIDE_LIBSELECTOR ); + LanguageBoxControl::RegisterControl( SID_BASICIDE_CURRENT_LANG ); + SvxZoomSliderControl::RegisterControl( SID_ATTR_ZOOMSLIDER ); + + GetViewFrame().GetWindow().SetBackground( + GetViewFrame().GetWindow().GetSettings().GetStyleSettings().GetWindowColor() + ); + + pCurWin = nullptr; + m_aCurDocument = ScriptDocument::getApplicationScriptDocument(); + bCreatingWindow = false; + + pTabBar.reset(VclPtr<TabBar>::Create(&GetViewFrame().GetWindow())); + + nCurKey = 100; + InitScrollBars(); + InitTabBar(); + InitZoomLevel(); + + SetCurLib( ScriptDocument::getApplicationScriptDocument(), "Standard", false, false ); + + ShellCreated(this); + + GetExtraData()->ShellInCriticalSection() = false; + + // It's enough to create the controller ... + // It will be public by using magic :-) + new Controller(this); + + // Force updating the title ! Because it must be set to the controller + // it has to be called directly after creating those controller. + SetMDITitle (); + + UpdateWindows(); +} + +Shell::~Shell() +{ + m_aNotifier.dispose(); + + ShellDestroyed(this); + + // so that on a basic saving error, the shell doesn't pop right up again + GetExtraData()->ShellInCriticalSection() = true; + + SetWindow( nullptr ); + SetCurWindow( nullptr ); + + aObjectCatalog.disposeAndClear(); + aVScrollBar.disposeAndClear(); + aHScrollBar.disposeAndClear(); + + for (auto & window : aWindowTable) + { + // no store; does already happen when the BasicManagers are destroyed + window.second.disposeAndClear(); + } + + // no store; does already happen when the BasicManagers are destroyed + aWindowTable.clear(); + + // Destroy all ContainerListeners for Basic Container. + if (ContainerListenerImpl* pListener = static_cast<ContainerListenerImpl*>(m_xLibListener.get())) + pListener->removeContainerListener(m_aCurDocument, m_aCurLibName); + + GetExtraData()->ShellInCriticalSection() = false; + + nShellCount--; + + pDialogLayout.disposeAndClear(); + pModulLayout.disposeAndClear(); + pTabBar.disposeAndClear(); + + // Remember current zoom level + SvtViewOptions(EViewType::Window, BASIC_IDE_EDITOR_WINDOW).SetUserItem( + BASIC_IDE_CURRENT_ZOOM, Any(m_nCurrentZoomSliderValue)); +} + +void Shell::onDocumentCreated( const ScriptDocument& /*_rDocument*/ ) +{ + if (pCurWin) + pCurWin->OnNewDocument(); + + UpdateWindows(); +} + +void Shell::onDocumentOpened( const ScriptDocument& /*_rDocument*/ ) +{ + if (pCurWin) + pCurWin->OnNewDocument(); + UpdateWindows(); +} + +void Shell::onDocumentSave( const ScriptDocument& /*_rDocument*/ ) +{ + StoreAllWindowData(); +} + +void Shell::onDocumentSaveDone( const ScriptDocument& /*_rDocument*/ ) +{ + // #i115671: Update SID_SAVEDOC after saving is completed + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_SAVEDOC ); +} + +void Shell::onDocumentSaveAs( const ScriptDocument& /*_rDocument*/ ) +{ + StoreAllWindowData(); +} + +void Shell::onDocumentSaveAsDone( const ScriptDocument& /*_rDocument*/ ) +{ + // not interested in +} + +void Shell::onDocumentClosed( const ScriptDocument& _rDocument ) +{ + if ( !_rDocument.isValid() ) + return; + + bool bSetCurWindow = false; + bool bSetCurLib = ( _rDocument == m_aCurDocument ); + std::vector<VclPtr<BaseWindow> > aDeleteVec; + + // remove all windows which belong to this document + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if ( pWin->IsDocument( _rDocument ) ) + { + if ( pWin->GetStatus() & (BASWIN_RUNNINGBASIC|BASWIN_INRESCHEDULE) ) + { + pWin->AddStatus( BASWIN_TOBEKILLED ); + pWin->Hide(); + StarBASIC::Stop(); + // there's no notify + pWin->BasicStopped(); + } + else + aDeleteVec.emplace_back(pWin ); + } + } + // delete windows outside main loop so we don't invalidate the original iterator + for (VclPtr<BaseWindow> const & pWin : aDeleteVec) + { + pWin->StoreData(); + if ( pWin == pCurWin ) + bSetCurWindow = true; + RemoveWindow( pWin, true, false ); + } + + // remove lib info + if (ExtraData* pData = GetExtraData()) + pData->GetLibInfo().RemoveInfoFor( _rDocument ); + + if ( bSetCurLib ) + SetCurLib( ScriptDocument::getApplicationScriptDocument(), "Standard", true, false ); + else if ( bSetCurWindow ) + SetCurWindow( FindApplicationWindow(), true ); +} + +void Shell::onDocumentTitleChanged( const ScriptDocument& /*_rDocument*/ ) +{ + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_BASICIDE_LIBSELECTOR, true ); + SetMDITitle(); +} + +void Shell::onDocumentModeChanged( const ScriptDocument& _rDocument ) +{ + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if ( pWin->IsDocument( _rDocument ) && _rDocument.isDocument() ) + pWin->SetReadOnly( _rDocument.isReadOnly() ); + } +} + +void Shell::InitZoomLevel() +{ + m_nCurrentZoomSliderValue = DEFAULT_ZOOM_LEVEL; + SvtViewOptions aWinOpt(EViewType::Window, BASIC_IDE_EDITOR_WINDOW); + if (aWinOpt.Exists()) + { + try + { + aWinOpt.GetUserItem(BASIC_IDE_CURRENT_ZOOM) >>= m_nCurrentZoomSliderValue; + } + catch(const css::container::NoSuchElementException&) + { TOOLS_WARN_EXCEPTION("basctl.basicide", "Zoom level not defined"); } + } +} + +// Applies the new zoom level to all open editor windows +void Shell::SetGlobalEditorZoomLevel(sal_uInt16 nNewZoomLevel) +{ + for (auto const& window : aWindowTable) + { + ModulWindow* pModuleWindow = dynamic_cast<ModulWindow*>(window.second.get()); + if (pModuleWindow) + { + EditorWindow& pEditorWindow = pModuleWindow->GetEditorWindow(); + pEditorWindow.SetEditorZoomLevel(nNewZoomLevel); + } + } + + // Update the zoom slider value based on the new global zoom level + m_nCurrentZoomSliderValue = nNewZoomLevel; + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM ); + pBindings->Invalidate( SID_ATTR_ZOOMSLIDER ); + } +} + +void Shell::StoreAllWindowData( bool bPersistent ) +{ + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + DBG_ASSERT( pWin, "PrepareClose: NULL-Pointer in Table?" ); + if ( !pWin->IsSuspended() ) + pWin->StoreData(); + } + + if ( bPersistent ) + { + SfxGetpApp()->SaveBasicAndDialogContainer(); + SetAppBasicModified(false); + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Update( SID_SAVEDOC ); + } + } +} + +bool Shell::PrepareClose( bool bUI ) +{ + // reset here because it's modified after printing etc. (DocInfo) + GetViewFrame().GetObjectShell()->SetModified(false); + + if ( StarBASIC::IsRunning() ) + { + if( bUI ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetViewFrame().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + IDEResId(RID_STR_CANNOTCLOSE))); + xInfoBox->run(); + } + return false; + } + else + { + StoreAllWindowData( false ); // don't write on the disk, that will be done later automatically + return true; + } +} + +void Shell::InitScrollBars() +{ + aVScrollBar->SetLineSize( 300 ); + aVScrollBar->SetPageSize( 2000 ); + aHScrollBar->SetLineSize( 300 ); + aHScrollBar->SetPageSize( 2000 ); +} + +void Shell::InitTabBar() +{ + pTabBar->Enable(); + pTabBar->Show(); + pTabBar->SetSelectHdl( LINK( this, Shell, TabBarHdl ) ); +} + +void Shell::OuterResizePixel( const Point &rPos, const Size &rSize ) +{ + AdjustPosSizePixel( rPos, rSize ); +} + +IMPL_LINK( Shell, TabBarHdl, ::TabBar *, pCurTabBar, void ) +{ + sal_uInt16 nCurId = pCurTabBar->GetCurPageId(); + BaseWindow* pWin = aWindowTable[ nCurId ].get(); + DBG_ASSERT( pWin, "Entry in TabBar is not matching a window!" ); + SetCurWindow( pWin ); +} + + +bool Shell::NextPage( bool bPrev ) +{ + bool bRet = false; + sal_uInt16 nPos = pTabBar->GetPagePos( pTabBar->GetCurPageId() ); + + if ( bPrev ) + --nPos; + else + ++nPos; + + if ( nPos < pTabBar->GetPageCount() ) + { + VclPtr<BaseWindow> pWin = aWindowTable[ pTabBar->GetPageId( nPos ) ]; + SetCurWindow( pWin, true ); + bRet = true; + } + + return bRet; +} + +SfxUndoManager* Shell::GetUndoManager() +{ + SfxUndoManager* pMgr = nullptr; + if( pCurWin ) + pMgr = pCurWin->GetUndoManager(); + + return pMgr; +} + + +void Shell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if (!GetShell()) + return; + + if (rHint.GetId() == SfxHintId::Dying) + { + EndListening( rBC, true /* log off all */ ); + aObjectCatalog->UpdateEntries(); + } + + SbxHint const* pSbxHint = dynamic_cast<SbxHint const*>(&rHint); + if (!pSbxHint) + return; + + const SfxHintId nHintId = pSbxHint->GetId(); + if ( ( nHintId != SfxHintId::BasicStart ) && + ( nHintId != SfxHintId::BasicStop ) ) + return; + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICRUN ); + pBindings->Update( SID_BASICRUN ); + pBindings->Invalidate( SID_BASICCOMPILE ); + pBindings->Update( SID_BASICCOMPILE ); + pBindings->Invalidate( SID_BASICSTEPOVER ); + pBindings->Update( SID_BASICSTEPOVER ); + pBindings->Invalidate( SID_BASICSTEPINTO ); + pBindings->Update( SID_BASICSTEPINTO ); + pBindings->Invalidate( SID_BASICSTEPOUT ); + pBindings->Update( SID_BASICSTEPOUT ); + pBindings->Invalidate( SID_BASICSTOP ); + pBindings->Update( SID_BASICSTOP ); + pBindings->Invalidate( SID_BASICIDE_TOGGLEBRKPNT ); + pBindings->Update( SID_BASICIDE_TOGGLEBRKPNT ); + pBindings->Invalidate( SID_BASICIDE_MANAGEBRKPNTS ); + pBindings->Update( SID_BASICIDE_MANAGEBRKPNTS ); + pBindings->Invalidate( SID_BASICIDE_MODULEDLG ); + pBindings->Update( SID_BASICIDE_MODULEDLG ); + pBindings->Invalidate( SID_BASICLOAD ); + pBindings->Update( SID_BASICLOAD ); + } + + if ( nHintId == SfxHintId::BasicStop ) + { + // not only at error/break or explicit stoppage, + // if the update is turned off due to a programming bug + BasicStopped(); + if (pLayout) + pLayout->UpdateDebug(true); // clear... + if( m_pCurLocalizationMgr ) + m_pCurLocalizationMgr->handleBasicStopped(); + } + else if( m_pCurLocalizationMgr ) + { + m_pCurLocalizationMgr->handleBasicStarted(); + } + + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if ( nHintId == SfxHintId::BasicStart ) + pWin->BasicStarted(); + else + pWin->BasicStopped(); + } +} + + +void Shell::CheckWindows() +{ + bool bSetCurWindow = false; + std::vector<VclPtr<BaseWindow> > aDeleteVec; + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if ( pWin->GetStatus() & BASWIN_TOBEKILLED ) + aDeleteVec.emplace_back(pWin ); + } + for ( VclPtr<BaseWindow> const & pWin : aDeleteVec ) + { + pWin->StoreData(); + if ( pWin == pCurWin ) + bSetCurWindow = true; + RemoveWindow( pWin, true, false ); + } + if ( bSetCurWindow ) + SetCurWindow( FindApplicationWindow(), true ); +} + + +void Shell::RemoveWindows( const ScriptDocument& rDocument, std::u16string_view rLibName ) +{ + bool bChangeCurWindow = pCurWin; + std::vector<VclPtr<BaseWindow> > aDeleteVec; + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if ( pWin->IsDocument( rDocument ) && pWin->GetLibName() == rLibName ) + aDeleteVec.emplace_back(pWin ); + } + for ( VclPtr<BaseWindow> const & pWin : aDeleteVec ) + { + if ( pWin == pCurWin ) + bChangeCurWindow = true; + pWin->StoreData(); + RemoveWindow( pWin, true/*bDestroy*/, false ); + } + if ( bChangeCurWindow ) + SetCurWindow( FindApplicationWindow(), true ); +} + + +void Shell::UpdateWindows() +{ + // remove all windows that may not be displayed + bool bChangeCurWindow = pCurWin == nullptr; + // stores the total number of modules and dialogs visible + sal_uInt16 nTotalTabs = 0; + + if ( !m_aCurLibName.isEmpty() ) + { + std::vector<VclPtr<BaseWindow> > aDeleteVec; + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if ( !pWin->IsDocument( m_aCurDocument ) || pWin->GetLibName() != m_aCurLibName ) + { + if ( pWin == pCurWin ) + bChangeCurWindow = true; + pWin->StoreData(); + // The request of RUNNING prevents the crash when in reschedule. + // Window is frozen at first, later the windows should be changed + // anyway to be marked as hidden instead of being deleted. + if ( !(pWin->GetStatus() & ( BASWIN_TOBEKILLED | BASWIN_RUNNINGBASIC | BASWIN_SUSPENDED ) ) ) + aDeleteVec.emplace_back(pWin ); + } + } + for (auto const& elem : aDeleteVec) + { + RemoveWindow( elem, false, false ); + } + } + + if ( bCreatingWindow ) + return; + + BaseWindow* pNextActiveWindow = nullptr; + + // show all windows that are to be shown + ScriptDocuments aDocuments( ScriptDocument::getAllScriptDocuments( ScriptDocument::AllWithApplication ) ); + for (auto const& doc : aDocuments) + { + StartListening(*doc.getBasicManager(), DuplicateHandling::Prevent /* log on only once */); + + // libraries + Sequence< OUString > aLibNames( doc.getLibraryNames() ); + sal_Int32 nLibCount = aLibNames.getLength(); + const OUString* pLibNames = aLibNames.getConstArray(); + + for ( sal_Int32 i = 0 ; i < nLibCount ; i++ ) + { + OUString aLibName = pLibNames[ i ]; + + if ( m_aCurLibName.isEmpty() || ( doc == m_aCurDocument && aLibName == m_aCurLibName ) ) + { + // check, if library is password protected and not verified + bool bProtected = false; + Reference< script::XLibraryContainer > xModLibContainer( doc.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aLibName ) && !xPasswd->isLibraryPasswordVerified( aLibName ) ) + { + bProtected = true; + } + } + + if ( !bProtected ) + { + LibInfo::Item const* pLibInfoItem = nullptr; + if (ExtraData* pData = GetExtraData()) + pLibInfoItem = pData->GetLibInfo().GetInfo(doc, aLibName); + + // modules + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) + { + StarBASIC* pLib = doc.getBasicManager()->GetLib( aLibName ); + if ( pLib ) + StartListening(pLib->GetBroadcaster(), DuplicateHandling::Prevent /* log on only once */); + + try + { + Sequence< OUString > aModNames( doc.getObjectNames( E_SCRIPTS, aLibName ) ); + sal_Int32 nModCount = aModNames.getLength(); + const OUString* pModNames = aModNames.getConstArray(); + nTotalTabs += nModCount; + + for ( sal_Int32 j = 0 ; j < nModCount ; j++ ) + { + OUString aModName = pModNames[ j ]; + VclPtr<ModulWindow> pWin = FindBasWin( doc, aLibName, aModName ); + if ( !pWin ) + pWin = CreateBasWin( doc, aLibName, aModName ); + if ( !pNextActiveWindow && pLibInfoItem && pLibInfoItem->GetCurrentName() == aModName && + pLibInfoItem->GetCurrentType() == TYPE_MODULE ) + { + pNextActiveWindow = pWin; + } + } + } + catch (const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + + // dialogs + Reference< script::XLibraryContainer > xDlgLibContainer( doc.getLibraryContainer( E_DIALOGS ) ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) ) + { + try + { + Sequence< OUString > aDlgNames = doc.getObjectNames( E_DIALOGS, aLibName ); + sal_Int32 nDlgCount = aDlgNames.getLength(); + const OUString* pDlgNames = aDlgNames.getConstArray(); + nTotalTabs += nDlgCount; + + for ( sal_Int32 j = 0 ; j < nDlgCount ; j++ ) + { + OUString aDlgName = pDlgNames[ j ]; + // this find only looks for non-suspended windows; + // suspended windows are handled in CreateDlgWin + VclPtr<DialogWindow> pWin = FindDlgWin( doc, aLibName, aDlgName ); + if ( !pWin ) + pWin = CreateDlgWin( doc, aLibName, aDlgName ); + if ( !pNextActiveWindow && pLibInfoItem && pLibInfoItem->GetCurrentName() == aDlgName && + pLibInfoItem->GetCurrentType() == TYPE_DIALOG ) + { + pNextActiveWindow = pWin; + } + } + } + catch (const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + } + } + } + } + + if ( bChangeCurWindow ) + { + if ( nTotalTabs == 0 ) + { + // If no tabs are opened, create a generic module and make it visible + pNextActiveWindow = CreateBasWin( m_aCurDocument, m_aCurLibName, OUString() ); + } + else if ( !pNextActiveWindow ) + { + pNextActiveWindow = FindApplicationWindow().get(); + } + SetCurWindow( pNextActiveWindow, true ); + } +} + +void Shell::RemoveWindow( BaseWindow* pWindow_, bool bDestroy, bool bAllowChangeCurWindow ) +{ + VclPtr<BaseWindow> pWindowTmp( pWindow_ ); + + DBG_ASSERT( pWindow_, "Cannot delete NULL-Pointer!" ); + sal_uInt16 nKey = GetWindowId( pWindow_ ); + pTabBar->RemovePage( nKey ); + aWindowTable.erase( nKey ); + if ( pWindow_ == pCurWin ) + { + if ( bAllowChangeCurWindow ) + { + SetCurWindow( FindApplicationWindow(), true ); + } + else + { + SetCurWindow( nullptr ); + } + } + if ( bDestroy ) + { + if ( !( pWindow_->GetStatus() & BASWIN_INRESCHEDULE ) ) + { + pWindowTmp.disposeAndClear(); + } + else + { + pWindow_->AddStatus( BASWIN_TOBEKILLED ); + pWindow_->Hide(); + // In normal mode stop basic in windows to be deleted + // In VBA stop basic only if the running script is trying to delete + // its parent module + bool bStop = true; + if ( pWindow_->GetDocument().isInVBAMode() ) + { + SbModule* pMod = StarBASIC::GetActiveModule(); + if ( !pMod || pMod->GetName() != pWindow_->GetName() ) + { + bStop = false; + } + } + if ( bStop ) + { + StarBASIC::Stop(); + // there will be no notify... + pWindow_->BasicStopped(); + } + aWindowTable[ nKey ] = pWindow_; // jump in again + } + } + else + { + pWindow_->AddStatus( BASWIN_SUSPENDED ); + pWindow_->Deactivating(); + aWindowTable[ nKey ] = pWindow_; // jump in again + } + +} + + +sal_uInt16 Shell::InsertWindowInTable( BaseWindow* pNewWin ) +{ + nCurKey++; + aWindowTable[ nCurKey ] = pNewWin; + return nCurKey; +} + + +void Shell::InvalidateBasicIDESlots() +{ + // only those that have an optic effect... + + if (!GetShell()) + return; + + SfxBindings* pBindings = GetBindingsPtr(); + if (!pBindings) + return; + + pBindings->Invalidate( SID_COPY ); + pBindings->Invalidate( SID_CUT ); + pBindings->Invalidate( SID_PASTE ); + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Invalidate( SID_SIGNATURE ); + pBindings->Invalidate( SID_BASICIDE_CHOOSEMACRO ); + pBindings->Invalidate( SID_BASICIDE_MODULEDLG ); + pBindings->Invalidate( SID_BASICIDE_OBJCAT ); + pBindings->Invalidate( SID_BASICSTOP ); + pBindings->Invalidate( SID_BASICRUN ); + pBindings->Invalidate( SID_BASICCOMPILE ); + pBindings->Invalidate( SID_BASICLOAD ); + pBindings->Invalidate( SID_BASICSAVEAS ); + pBindings->Invalidate( SID_BASICIDE_MATCHGROUP ); + pBindings->Invalidate( SID_BASICSTEPINTO ); + pBindings->Invalidate( SID_BASICSTEPOVER ); + pBindings->Invalidate( SID_BASICSTEPOUT ); + pBindings->Invalidate( SID_BASICIDE_TOGGLEBRKPNT ); + pBindings->Invalidate( SID_BASICIDE_MANAGEBRKPNTS ); + pBindings->Invalidate( SID_BASICIDE_ADDWATCH ); + pBindings->Invalidate( SID_BASICIDE_REMOVEWATCH ); + + pBindings->Invalidate( SID_PRINTDOC ); + pBindings->Invalidate( SID_PRINTDOCDIRECT ); + pBindings->Invalidate( SID_SETUPPRINTER ); + pBindings->Invalidate( SID_DIALOG_TESTMODE ); + + pBindings->Invalidate( SID_DOC_MODIFIED ); + pBindings->Invalidate( SID_BASICIDE_STAT_TITLE ); + pBindings->Invalidate( SID_BASICIDE_STAT_POS ); + pBindings->Invalidate( SID_ATTR_INSERT ); + pBindings->Invalidate( SID_ATTR_SIZE ); +} + +void Shell::InvalidateControlSlots() +{ + if (!GetShell()) + return; + + SfxBindings* pBindings = GetBindingsPtr(); + if (!pBindings) + return; + + pBindings->Invalidate( SID_INSERT_FORM_RADIO ); + pBindings->Invalidate( SID_INSERT_FORM_CHECK ); + pBindings->Invalidate( SID_INSERT_FORM_LIST ); + pBindings->Invalidate( SID_INSERT_FORM_COMBO ); + pBindings->Invalidate( SID_INSERT_FORM_VSCROLL ); + pBindings->Invalidate( SID_INSERT_FORM_HSCROLL ); + pBindings->Invalidate( SID_INSERT_FORM_SPIN ); + + pBindings->Invalidate( SID_INSERT_SELECT ); + pBindings->Invalidate( SID_INSERT_PUSHBUTTON ); + pBindings->Invalidate( SID_INSERT_RADIOBUTTON ); + pBindings->Invalidate( SID_INSERT_CHECKBOX ); + pBindings->Invalidate( SID_INSERT_LISTBOX ); + pBindings->Invalidate( SID_INSERT_COMBOBOX ); + pBindings->Invalidate( SID_INSERT_GROUPBOX ); + pBindings->Invalidate( SID_INSERT_EDIT ); + pBindings->Invalidate( SID_INSERT_FIXEDTEXT ); + pBindings->Invalidate( SID_INSERT_IMAGECONTROL ); + pBindings->Invalidate( SID_INSERT_PROGRESSBAR ); + pBindings->Invalidate( SID_INSERT_HSCROLLBAR ); + pBindings->Invalidate( SID_INSERT_VSCROLLBAR ); + pBindings->Invalidate( SID_INSERT_HFIXEDLINE ); + pBindings->Invalidate( SID_INSERT_VFIXEDLINE ); + pBindings->Invalidate( SID_INSERT_DATEFIELD ); + pBindings->Invalidate( SID_INSERT_TIMEFIELD ); + pBindings->Invalidate( SID_INSERT_NUMERICFIELD ); + pBindings->Invalidate( SID_INSERT_CURRENCYFIELD ); + pBindings->Invalidate( SID_INSERT_FORMATTEDFIELD ); + pBindings->Invalidate( SID_INSERT_PATTERNFIELD ); + pBindings->Invalidate( SID_INSERT_FILECONTROL ); + pBindings->Invalidate( SID_INSERT_SPINBUTTON ); + pBindings->Invalidate( SID_INSERT_GRIDCONTROL ); + pBindings->Invalidate( SID_INSERT_HYPERLINKCONTROL ); + pBindings->Invalidate( SID_INSERT_TREECONTROL ); + pBindings->Invalidate( SID_CHOOSE_CONTROLS ); +} + +void Shell::SetCurLib( const ScriptDocument& rDocument, const OUString& aLibName, bool bUpdateWindows, bool bCheck ) +{ + if ( bCheck && rDocument == m_aCurDocument && aLibName == m_aCurLibName ) + return; + + ContainerListenerImpl* pListener = static_cast< ContainerListenerImpl* >( m_xLibListener.get() ); + + if (pListener) + pListener->removeContainerListener(m_aCurDocument, m_aCurLibName); + + m_aCurDocument = rDocument; + m_aCurLibName = aLibName; + + if ( pListener ) + pListener->addContainerListener( m_aCurDocument, aLibName ); + + if ( bUpdateWindows ) + UpdateWindows(); + + SetMDITitle(); + + SetCurLibForLocalization( rDocument, aLibName ); + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_LIBSELECTOR ); + pBindings->Invalidate( SID_BASICIDE_CURRENT_LANG ); + pBindings->Invalidate( SID_BASICIDE_MANAGE_LANG ); + } +} + +void Shell::SetCurLibForLocalization( const ScriptDocument& rDocument, const OUString& aLibName ) +{ + // Create LocalizationMgr + Reference< resource::XStringResourceManager > xStringResourceManager; + try + { + if( !aLibName.isEmpty() ) + { + Reference< container::XNameContainer > xDialogLib( rDocument.getLibrary( E_DIALOGS, aLibName, true ) ); + xStringResourceManager = LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + } + } + catch (const container::NoSuchElementException& ) + {} + + m_pCurLocalizationMgr = std::make_shared<LocalizationMgr>(this, rDocument, aLibName, xStringResourceManager); + m_pCurLocalizationMgr->handleTranslationbar(); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basobj2.cxx b/basctl/source/basicide/basobj2.cxx new file mode 100644 index 0000000000..708b1ce035 --- /dev/null +++ b/basctl/source/basicide/basobj2.cxx @@ -0,0 +1,441 @@ +/* -*- 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 <basidesh.hxx> +#include <iderdll.hxx> +#include "iderdll2.hxx" +#include "macrodlg.hxx" +#include "moduldlg.hxx" +#include <iderid.hxx> +#include <strings.hrc> +#include "baside2.hxx" + +#include <com/sun/star/document/XScriptInvocationContext.hpp> + +#include <basic/sbmeth.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/string.hxx> +#include <comphelper/sequence.hxx> +#include <framework/documentundoguard.hxx> +#include <sal/log.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/moduleoptions.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <memory> +#include <vector> +#include <algorithm> +#include <basic/basmgr.hxx> +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; + +extern "C" { + SAL_DLLPUBLIC_EXPORT rtl_uString* basicide_choose_macro(void* pParent, void* pOnlyInDocument_AsXModel, void* pDocFrame_AsXFrame, sal_Bool bChooseOnly ) + { + Reference< frame::XModel > aDocument( static_cast< frame::XModel* >( pOnlyInDocument_AsXModel ) ); + Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) ); + OUString aScriptURL = basctl::ChooseMacro(static_cast<weld::Window*>(pParent), aDocument, aDocFrame, bChooseOnly); + rtl_uString* pScriptURL = aScriptURL.pData; + rtl_uString_acquire( pScriptURL ); + + return pScriptURL; + } + SAL_DLLPUBLIC_EXPORT void basicide_macro_organizer(void *pParent, void* pDocFrame_AsXFrame, sal_Int16 nTabId) + { + SAL_INFO("basctl.basicide","in basicide_macro_organizer"); + Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) ); + basctl::Organize(static_cast<weld::Window*>(pParent), aDocFrame, nTabId); + } +} + +void Organize(weld::Window* pParent, const css::uno::Reference<css::frame::XFrame>& xDocFrame, sal_Int16 tabId) +{ + EnsureIde(); + + auto xDlg(std::make_shared<OrganizeDialog>(pParent, xDocFrame, tabId)); + weld::DialogController::runAsync(xDlg, [](int) {}); +} + +bool IsValidSbxName( std::u16string_view rName ) +{ + for ( size_t nChar = 0; nChar < rName.size(); nChar++ ) + { + sal_Unicode c = rName[nChar]; + bool bValid = ( + ( c >= 'A' && c <= 'Z' ) || + ( c >= 'a' && c <= 'z' ) || + ( c >= '0' && c <= '9' && nChar ) || + ( c == '_' ) + ); + if ( !bValid ) + return false; + } + return true; +} + +Sequence< OUString > GetMergedLibraryNames( const Reference< script::XLibraryContainer >& xModLibContainer, const Reference< script::XLibraryContainer >& xDlgLibContainer ) +{ + // create a list of module library names + std::vector<OUString> aLibList; + if ( xModLibContainer.is() ) + { + const Sequence< OUString > aModLibNames = xModLibContainer->getElementNames(); + aLibList.insert( aLibList.end(), aModLibNames.begin(), aModLibNames.end() ); + } + + // create a list of dialog library names + if ( xDlgLibContainer.is() ) + { + const Sequence< OUString > aDlgLibNames = xDlgLibContainer->getElementNames(); + aLibList.insert( aLibList.end(), aDlgLibNames.begin(), aDlgLibNames.end() ); + } + + // sort list + auto const sort = comphelper::string::NaturalStringSorter( + comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale()); + std::sort(aLibList.begin(), aLibList.end(), + [&sort](const OUString& rLHS, const OUString& rRHS) { + return sort.compare(rLHS, rRHS) < 0; + }); + // remove duplicates + std::vector<OUString>::iterator aIterEnd = std::unique( aLibList.begin(), aLibList.end() ); + aLibList.erase( aIterEnd, aLibList.end() ); + + return comphelper::containerToSequence(aLibList); +} + +bool RenameModule ( + weld::Widget* pErrorParent, + const ScriptDocument& rDocument, + const OUString& rLibName, + const OUString& rOldName, + const OUString& rNewName +) +{ + if ( !rDocument.hasModule( rLibName, rOldName ) ) + { + SAL_WARN( "basctl.basicide","basctl::RenameModule: old module name is invalid!" ); + return false; + } + + if ( rDocument.hasModule( rLibName, rNewName ) ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2))); + xError->run(); + return false; + } + + // #i74440 + if ( rNewName.isEmpty() ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xError->run(); + return false; + } + + if ( !rDocument.renameModule( rLibName, rOldName, rNewName ) ) + return false; + + Shell* pShell = GetShell(); + if (!pShell) + return true; + VclPtr<ModulWindow> pWin = pShell->FindBasWin(rDocument, rLibName, rNewName, false, true); + if (!pWin) + return true; + + // set new name in window + pWin->SetName( rNewName ); + + // set new module in module window + pWin->SetSbModule( pWin->GetBasic()->FindModule( rNewName ) ); + + // update tabwriter + sal_uInt16 nId = pShell->GetWindowId( pWin ); + SAL_WARN_IF( nId == 0 , "basctl.basicide", "No entry in Tabbar!"); + if ( nId ) + { + TabBar& rTabBar = pShell->GetTabBar(); + rTabBar.SetPageText(nId, rNewName); + rTabBar.Sort(); + rTabBar.MakeVisible(rTabBar.GetCurPageId()); + } + return true; +} + +namespace +{ + struct MacroExecutionData + { + ScriptDocument aDocument; + SbMethodRef xMethod; + + MacroExecutionData() + :aDocument( ScriptDocument::NoDocument ) + { + } + }; + + class MacroExecution + { + public: + DECL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, void ); + }; + + IMPL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, p, void ) + { + MacroExecutionData* i_pData = static_cast<MacroExecutionData*>(p); + ENSURE_OR_RETURN_VOID( i_pData, "wrong MacroExecutionData" ); + // take ownership of the data + std::unique_ptr< MacroExecutionData > pData( i_pData ); + + SAL_WARN_IF( (pData->xMethod->GetParent()->GetFlags() & SbxFlagBits::ExtSearch) == SbxFlagBits::NONE, "basctl.basicide","No EXTSEARCH!" ); + + // in case this is a document-local macro, try to protect the document's Undo Manager from + // flawed scripts + std::optional< ::framework::DocumentUndoGuard > pUndoGuard; + if ( pData->aDocument.isDocument() ) + pUndoGuard.emplace( pData->aDocument.getDocument() ); + + RunMethod( pData->xMethod.get() ); + } +} + +OUString ChooseMacro(weld::Window* pParent, + const uno::Reference< frame::XModel >& rxLimitToDocument, + const uno::Reference< frame::XFrame >& xDocFrame, + bool bChooseOnly) +{ + EnsureIde(); + + GetExtraData()->ChoosingMacro() = true; + + OUString aScriptURL; + SbMethod* pMethod = nullptr; + + MacroChooser aChooser(pParent, xDocFrame); + if ( bChooseOnly || !SvtModuleOptions::IsBasicIDE() ) + aChooser.SetMode(MacroChooser::ChooseOnly); + + if ( !bChooseOnly && rxLimitToDocument.is() ) + { + // Hack! + aChooser.SetMode(MacroChooser::Recording); + } + + short nRetValue = aChooser.run(); + + GetExtraData()->ChoosingMacro() = false; + + switch ( nRetValue ) + { + case Macro_OkRun: + { + bool bError = false; + + pMethod = aChooser.GetMacro(); + if ( !pMethod && aChooser.GetMode() == MacroChooser::Recording ) + pMethod = aChooser.CreateMacro(); + + if ( !pMethod ) + break; + + SbModule* pModule = pMethod->GetModule(); + if ( !pModule ) + { + SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Module found!" ); + break; + } + + StarBASIC* pBasic = dynamic_cast<StarBASIC*>(pModule->GetParent()); + if ( !pBasic ) + { + SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Basic found!" ); + break; + } + + BasicManager* pBasMgr = FindBasicManager( pBasic ); + if ( !pBasMgr ) + { + SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No BasicManager found!" ); + break; + } + + // name + OUString aName = pBasic->GetName() + "." + pModule->GetName() + "." + pMethod->GetName(); + + // location + OUString aLocation; + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + if ( aDocument.isDocument() ) + { + // document basic + aLocation = "document" ; + + if ( rxLimitToDocument.is() ) + { + uno::Reference< frame::XModel > xLimitToDocument( rxLimitToDocument ); + + uno::Reference< document::XEmbeddedScripts > xScripts( rxLimitToDocument, UNO_QUERY ); + if ( !xScripts.is() ) + { // the document itself does not support embedding scripts + uno::Reference< document::XScriptInvocationContext > xContext( rxLimitToDocument, UNO_QUERY ); + if ( xContext.is() ) + xScripts = xContext->getScriptContainer(); + if ( xScripts.is() ) + { // but it is able to refer to a document which actually does support this + xLimitToDocument.set( xScripts, UNO_QUERY ); + if ( !xLimitToDocument.is() ) + { + SAL_WARN_IF(!xLimitToDocument.is(), "basctl.basicide", "basctl::ChooseMacro: a script container which is no document!?" ); + xLimitToDocument = rxLimitToDocument; + } + } + } + + if ( xLimitToDocument != aDocument.getDocument() ) + { + // error + bError = true; + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_ERRORCHOOSEMACRO))); + xError->run(); + } + } + } + else + { + // application basic + aLocation = "application" ; + } + + // script URL + if ( !bError ) + { + aScriptURL = "vnd.sun.star.script:" + aName + "?language=Basic&location=" + aLocation; + } + + if ( !rxLimitToDocument.is() ) + { + MacroExecutionData* pExecData = new MacroExecutionData; + pExecData->aDocument = aDocument; + pExecData->xMethod = pMethod; // keep alive until the event has been processed + Application::PostUserEvent( LINK( nullptr, MacroExecution, ExecuteMacroEvent ), pExecData ); + } + } + break; + } + + return aScriptURL; +} + +Sequence< OUString > GetMethodNames( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName ) +{ + Sequence< OUString > aSeqMethods; + + // get module + OUString aOUSource; + if ( rDocument.getModule( rLibName, rModName, aOUSource ) ) + { + BasicManager* pBasMgr = rDocument.getBasicManager(); + StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr; + SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr; + + SbModuleRef xModule; + // Only reparse modules if ScriptDocument source is out of sync + // with basic's Module + if ( !pMod || pMod->GetSource32() != aOUSource ) + { + xModule = new SbModule( rModName ); + xModule->SetSource32( aOUSource ); + pMod = xModule.get(); + } + + sal_uInt32 nCount = pMod->GetMethods()->Count(); + sal_uInt32 nRealCount = nCount; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get(i)); + if( pMethod->IsHidden() ) + --nRealCount; + } + aSeqMethods.realloc( nRealCount ); + + sal_uInt32 iTarget = 0; + for ( sal_uInt32 i = 0 ; i < nCount; ++i ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get(i)); + if( pMethod->IsHidden() ) + continue; + SAL_WARN_IF( !pMethod, "basctl.basicide","Method not found! (NULL)" ); + aSeqMethods.getArray()[ iTarget++ ] = pMethod->GetName(); + } + } + + return aSeqMethods; +} + +bool HasMethod ( + ScriptDocument const& rDocument, + OUString const& rLibName, + OUString const& rModName, + OUString const& rMethName +) +{ + bool bHasMethod = false; + + OUString aOUSource; + if ( rDocument.hasModule( rLibName, rModName ) && rDocument.getModule( rLibName, rModName, aOUSource ) ) + { + // Check if we really need to scan the source ( again ) + BasicManager* pBasMgr = rDocument.getBasicManager(); + StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr; + SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr; + SbModuleRef xModule; + // Only reparse modules if ScriptDocument source is out of sync + // with basic's Module + if ( !pMod || pMod->GetSource32() != aOUSource ) + { + xModule = new SbModule( rModName ); + xModule->SetSource32( aOUSource ); + pMod = xModule.get(); + } + SbxArray* pMethods = pMod->GetMethods().get(); + if ( pMethods ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Find( rMethName, SbxClassType::Method )); + if ( pMethod && !pMethod->IsHidden() ) + bHasMethod = true; + } + } + + return bHasMethod; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/basobj3.cxx b/basctl/source/basicide/basobj3.cxx new file mode 100644 index 0000000000..4672cdd52c --- /dev/null +++ b/basctl/source/basicide/basobj3.cxx @@ -0,0 +1,466 @@ +/* -*- 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 <vcl/errinf.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <basic/basmgr.hxx> +#include <basic/sbmeth.hxx> +#include <unotools/moduleoptions.hxx> + +#include <iderdll.hxx> +#include "iderdll2.hxx" +#include "basdoc.hxx" +#include <iderid.hxx> +#include <strings.hrc> + +#include <baside3.hxx> +#include <basidesh.hxx> +#include <basobj.hxx> +#include <localizationmgr.hxx> +#include <dlged.hxx> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <tools/debug.hxx> + +namespace basctl +{ + +using namespace comphelper; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; + +extern "C" { + SAL_DLLPUBLIC_EXPORT tools::Long basicide_handle_basic_error( void const * pPtr ) + { + return HandleBasicError( static_cast<StarBASIC const *>(pPtr) ); + } +} + +SbMethod* CreateMacro( SbModule* pModule, const OUString& rMacroName ) +{ + SfxDispatcher* pDispatcher = GetDispatcher(); + if( pDispatcher ) + { + pDispatcher->Execute( SID_BASICIDE_STOREALLMODULESOURCES ); + } + + if ( pModule->FindMethod( rMacroName, SbxClassType::Method ) ) + return nullptr; + + OUString aMacroName( rMacroName ); + if ( aMacroName.isEmpty() ) + { + if (!pModule->GetMethods()->Count()) + aMacroName = "Main" ; + else + { + bool bValid = false; + sal_Int32 nMacro = 1; + while ( !bValid ) + { + aMacroName = "Macro" + OUString::number( nMacro ); + // test whether existing... + bValid = pModule->FindMethod( aMacroName, SbxClassType::Method ) == nullptr; + nMacro++; + } + } + } + + OUString aOUSource( pModule->GetSource32() ); + + // don't produce too many empty lines... + sal_Int32 nSourceLen = aOUSource.getLength(); + if ( nSourceLen > 2 ) + { + const sal_Unicode* pStr = aOUSource.getStr(); + if ( pStr[ nSourceLen - 1 ] != LINE_SEP ) + aOUSource += "\n\n" ; + else if ( pStr[ nSourceLen - 2 ] != LINE_SEP ) + aOUSource += "\n" ; + else if ( pStr[ nSourceLen - 3 ] == LINE_SEP ) + aOUSource = aOUSource.copy( 0, nSourceLen-1 ); + } + + aOUSource += "Sub " + aMacroName + "\n\nEnd Sub"; + + // update module in library + StarBASIC* pBasic = dynamic_cast<StarBASIC*>(pModule->GetParent()); + BasicManager* pBasMgr = pBasic ? FindBasicManager(pBasic) : nullptr; + SAL_WARN_IF(!pBasMgr, "basctl.basicide", "No BasicManager found!"); + ScriptDocument aDocument = pBasMgr + ? ScriptDocument::getDocumentForBasicManager(pBasMgr) + : ScriptDocument(ScriptDocument::NoDocument); + + if (aDocument.isValid()) + { + const OUString& aLibName = pBasic->GetName(); + const OUString& aModName = pModule->GetName(); + OSL_VERIFY( aDocument.updateModule( aLibName, aModName, aOUSource ) ); + } + + SbMethod* pMethod = pModule->FindMethod( aMacroName, SbxClassType::Method ); + + if( pDispatcher ) + { + pDispatcher->Execute( SID_BASICIDE_UPDATEALLMODULESOURCES ); + } + + if (aDocument.isAlive()) + MarkDocumentModified(aDocument); + + return pMethod; +} + +bool RenameDialog ( + weld::Widget* pErrorParent, + ScriptDocument const& rDocument, + OUString const& rLibName, + OUString const& rOldName, + OUString const& rNewName +) +{ + if ( !rDocument.hasDialog( rLibName, rOldName ) ) + { + OSL_FAIL( "basctl::RenameDialog: old module name is invalid!" ); + return false; + } + + if ( rDocument.hasDialog( rLibName, rNewName ) ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2))); + xError->run(); + return false; + } + + // #i74440 + if ( rNewName.isEmpty() ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xError->run(); + return false; + } + + Shell* pShell = GetShell(); + VclPtr<DialogWindow> pWin = pShell ? pShell->FindDlgWin(rDocument, rLibName, rOldName) : nullptr; + Reference< XNameContainer > xExistingDialog; + if ( pWin ) + xExistingDialog = pWin->GetEditor().GetDialog(); + + if ( xExistingDialog.is() ) + LocalizationMgr::renameStringResourceIDs( rDocument, rLibName, rNewName, xExistingDialog ); + + if ( !rDocument.renameDialog( rLibName, rOldName, rNewName, xExistingDialog ) ) + return false; + + if (!pWin || !pShell) + return true; + + // set new name in window + pWin->SetName( rNewName ); + + // update property browser + pWin->UpdateBrowser(); + + // update tabwriter + sal_uInt16 nId = pShell->GetWindowId( pWin ); + DBG_ASSERT( nId, "No entry in Tabbar!" ); + if ( nId ) + { + TabBar& rTabBar = pShell->GetTabBar(); + rTabBar.SetPageText( nId, rNewName ); + rTabBar.Sort(); + rTabBar.MakeVisible( rTabBar.GetCurPageId() ); + } + return true; +} + +bool RemoveDialog( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rDlgName ) +{ + if (Shell* pShell = GetShell()) + { + if (VclPtr<DialogWindow> pDlgWin = pShell->FindDlgWin(rDocument, rLibName, rDlgName)) + { + Reference< container::XNameContainer > xDialogModel = pDlgWin->GetDialog(); + LocalizationMgr::removeResourceForDialog( rDocument, rLibName, rDlgName, xDialogModel ); + } + } + + return rDocument.removeDialog( rLibName, rDlgName ); +} + +StarBASIC* FindBasic( const SbxVariable* pVar ) +{ + SbxVariable const* pSbx = pVar; + while (pSbx && !dynamic_cast<StarBASIC const*>(pSbx)) + pSbx = pSbx->GetParent(); + return const_cast<StarBASIC*>(static_cast<const StarBASIC*>(pSbx)); +} + +BasicManager* FindBasicManager( StarBASIC const * pLib ) +{ + ScriptDocuments aDocuments( ScriptDocument::getAllScriptDocuments( ScriptDocument::AllWithApplication ) ); + for (auto const& doc : aDocuments) + { + BasicManager* pBasicMgr = doc.getBasicManager(); + OSL_ENSURE( pBasicMgr, "basctl::FindBasicManager: no basic manager for the document!" ); + if ( !pBasicMgr ) + continue; + + Sequence< OUString > aLibNames( doc.getLibraryNames() ); + sal_Int32 nLibCount = aLibNames.getLength(); + const OUString* pLibNames = aLibNames.getConstArray(); + + for ( sal_Int32 i = 0 ; i < nLibCount ; i++ ) + { + StarBASIC* pL = pBasicMgr->GetLib( pLibNames[ i ] ); + if ( pL == pLib ) + return pBasicMgr; + } + } + return nullptr; +} + +void MarkDocumentModified( const ScriptDocument& rDocument ) +{ + Shell* pShell = GetShell(); + + // does not have to come from a document... + if ( rDocument.isApplication() ) + { + if (pShell) + pShell->SetAppBasicModified(true); + } + else + { + rDocument.setDocumentModified(); + } + + // tdf#130161 in all cases call UpdateObjectCatalog + if (pShell) + pShell->UpdateObjectCatalog(); + + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_SIGNATURE ); + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Update( SID_SAVEDOC ); + } +} + +void RunMethod( SbMethod const * pMethod ) +{ + SbxValues aRes; + aRes.eType = SbxVOID; + pMethod->Get( aRes ); +} + +void StopBasic() +{ + StarBASIC::Stop(); + if (Shell* pShell = GetShell()) + { + Shell::WindowTable& rWindows = pShell->GetWindowTable(); + for (auto const& window : rWindows) + { + BaseWindow* pWin = window.second; + // call BasicStopped manually because the Stop-Notify + // might not get through otherwise + pWin->BasicStopped(); + } + } + BasicStopped(); +} + +void BasicStopped( + bool* pbAppWindowDisabled, + bool* pbDispatcherLocked, + sal_uInt16* pnWaitCount, + SfxUInt16Item** ppSWActionCount, SfxUInt16Item** ppSWLockViewCount +) +{ + // maybe there are some locks to be removed after an error + // or an explicit cancelling of the basic... + if ( pbAppWindowDisabled ) + *pbAppWindowDisabled = false; + if ( pbDispatcherLocked ) + *pbDispatcherLocked = false; + if ( pnWaitCount ) + *pnWaitCount = 0; + if ( ppSWActionCount ) + *ppSWActionCount = nullptr; + if ( ppSWLockViewCount ) + *ppSWLockViewCount = nullptr; + + // AppWait? + if (Shell* pShell = GetShell()) + { + sal_uInt16 nWait = 0; + while ( pShell->GetViewFrame().GetWindow().IsWait() ) + { + pShell->GetViewFrame().GetWindow().LeaveWait(); + nWait++; + } + if ( pnWaitCount ) + *pnWaitCount = nWait; + } + + weld::Window* pDefParent = Application::GetDefDialogParent(); + if (pDefParent && !pDefParent->get_sensitive()) + { + pDefParent->set_sensitive(true); + if ( pbAppWindowDisabled ) + *pbAppWindowDisabled = true; + } + +} + +void InvalidateDebuggerSlots() +{ + SfxBindings* pBindings = GetBindingsPtr(); + if (!pBindings) + return; + + pBindings->Invalidate( SID_BASICSTOP ); + pBindings->Update( SID_BASICSTOP ); + pBindings->Invalidate( SID_BASICRUN ); + pBindings->Update( SID_BASICRUN ); + pBindings->Invalidate( SID_BASICCOMPILE ); + pBindings->Update( SID_BASICCOMPILE ); + pBindings->Invalidate( SID_BASICSTEPOVER ); + pBindings->Update( SID_BASICSTEPOVER ); + pBindings->Invalidate( SID_BASICSTEPINTO ); + pBindings->Update( SID_BASICSTEPINTO ); + pBindings->Invalidate( SID_BASICSTEPOUT ); + pBindings->Update( SID_BASICSTEPOUT ); + pBindings->Invalidate( SID_BASICIDE_TOGGLEBRKPNT ); + pBindings->Update( SID_BASICIDE_TOGGLEBRKPNT ); + pBindings->Invalidate( SID_BASICIDE_STAT_POS ); + pBindings->Update( SID_BASICIDE_STAT_POS ); + pBindings->Invalidate( SID_BASICIDE_STAT_TITLE ); + pBindings->Update( SID_BASICIDE_STAT_TITLE ); +} + +tools::Long HandleBasicError( StarBASIC const * pBasic ) +{ + EnsureIde(); + BasicStopped(); + + // no error output during macro choosing + if (GetExtraData()->ChoosingMacro()) + return 1; + if (GetExtraData()->ShellInCriticalSection()) + return 2; + + tools::Long nRet = 0; + Shell* pShell = nullptr; + if ( SvtModuleOptions::IsBasicIDE() ) + { + BasicManager* pBasMgr = FindBasicManager( pBasic ); + if ( pBasMgr ) + { + bool bProtected = false; + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + OSL_ENSURE( aDocument.isValid(), "basctl::HandleBasicError: no document for the given BasicManager!" ); + if ( aDocument.isValid() ) + { + const OUString& aOULibName( pBasic->GetName() ); + Reference< script::XLibraryContainer > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aOULibName ) && !xPasswd->isLibraryPasswordVerified( aOULibName ) ) + { + bProtected = true; + } + } + } + + if ( !bProtected ) + { + pShell = GetShell(); + if ( !pShell ) + { + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs ); + SfxGetpApp()->ExecuteSlot( aRequest ); + pShell = GetShell(); + } + } + } + } + + if ( pShell ) + nRet = tools::Long(pShell->CallBasicErrorHdl( pBasic )); + else + ErrorHandler::HandleError( StarBASIC::GetErrorCode() ); + + return nRet; +} + +SfxBindings* GetBindingsPtr() +{ + SfxBindings* pBindings = nullptr; + + SfxViewFrame* pFrame = nullptr; + if (Shell* pShell = GetShell()) + { + pFrame = &pShell->GetViewFrame(); + } + else + { + SfxViewFrame* pView = SfxViewFrame::GetFirst(); + while ( pView ) + { + if (dynamic_cast<DocShell*>(pView->GetObjectShell())) + { + pFrame = pView; + break; + } + pView = SfxViewFrame::GetNext( *pView ); + } + } + if ( pFrame != nullptr ) + pBindings = &pFrame->GetBindings(); + + return pBindings; +} + +SfxDispatcher* GetDispatcher () +{ + if (Shell* pShell = GetShell()) + { + SfxViewFrame& rViewFrame = pShell->GetViewFrame(); + if (SfxDispatcher* pDispatcher = rViewFrame.GetDispatcher()) + return pDispatcher; + } + return nullptr; +} +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/bastype2.cxx b/basctl/source/basicide/bastype2.cxx new file mode 100644 index 0000000000..18ca75e01b --- /dev/null +++ b/basctl/source/basicide/bastype2.cxx @@ -0,0 +1,865 @@ +/* -*- 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 <basobj.hxx> +#include <bastypes.hxx> +#include <bastype2.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <iderid.hxx> +#include <tools/urlobj.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <svtools/imagemgr.hxx> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <comphelper/processfactory.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/itemset.hxx> + +#include <initializer_list> +#include <memory> +#include <string_view> + +#include <com/sun/star/script/ModuleType.hpp> +#include <com/sun/star/script/vba/XVBAModuleInfo.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <utility> + +namespace basctl +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +void ModuleInfoHelper::getObjectName( const uno::Reference< container::XNameContainer >& rLib, const OUString& rModName, OUString& rObjName ) +{ + try + { + uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( rLib, uno::UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( rModName ) ) + { + script::ModuleInfo aModuleInfo = xVBAModuleInfo->getModuleInfo( rModName ); + uno::Any aObject( aModuleInfo.ModuleObject ); + uno::Reference< lang::XServiceInfo > xServiceInfo( aObject, uno::UNO_QUERY ); + if( xServiceInfo.is() && xServiceInfo->supportsService( "ooo.vba.excel.Worksheet" ) ) + { + uno::Reference< container::XNamed > xNamed( aObject, uno::UNO_QUERY ); + if( xNamed.is() ) + rObjName = xNamed->getName(); + } + } + } + catch(const uno::Exception& ) + { + } +} + +sal_Int32 ModuleInfoHelper::getModuleType( const uno::Reference< container::XNameContainer >& rLib, const OUString& rModName ) +{ + sal_Int32 nType = script::ModuleType::NORMAL; + uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( rLib, uno::UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( rModName ) ) + { + script::ModuleInfo aModuleInfo = xVBAModuleInfo->getModuleInfo( rModName ); + nType = aModuleInfo.ModuleType; + } + return nType; +} + +Entry::~Entry() +{ } + +DocumentEntry::DocumentEntry ( + ScriptDocument aDocument, + LibraryLocation eLocation, + EntryType eType +) : + Entry(eType), + m_aDocument(std::move(aDocument)), + m_eLocation(eLocation) +{ + OSL_ENSURE( m_aDocument.isValid(), "DocumentEntry::DocumentEntry: illegal document!" ); +} + +DocumentEntry::~DocumentEntry() +{ } + +LibEntry::LibEntry ( + ScriptDocument const& rDocument, + LibraryLocation eLocation, + OUString aLibName +) : + DocumentEntry(rDocument, eLocation, OBJ_TYPE_LIBRARY), + m_aLibName(std::move(aLibName)) +{ } + +LibEntry::~LibEntry() +{ } + +EntryDescriptor::EntryDescriptor () : + m_aDocument(ScriptDocument::getApplicationScriptDocument()), + m_eLocation(LIBRARY_LOCATION_UNKNOWN), + m_eType(OBJ_TYPE_UNKNOWN) +{ } + +EntryDescriptor::EntryDescriptor ( + ScriptDocument aDocument, + LibraryLocation eLocation, + OUString aLibName, + OUString aLibSubName, + OUString aName, + EntryType eType +) : + m_aDocument(std::move(aDocument)), + m_eLocation(eLocation), + m_aLibName(std::move(aLibName)), + m_aLibSubName(std::move(aLibSubName)), + m_aName(std::move(aName)), + m_eType(eType) +{ + OSL_ENSURE( m_aDocument.isValid(), "EntryDescriptor::EntryDescriptor: invalid document!" ); +} + +EntryDescriptor::EntryDescriptor ( + ScriptDocument aDocument, + LibraryLocation eLocation, + OUString aLibName, + OUString aLibSubName, + OUString aName, + OUString aMethodName, + EntryType eType +) : + m_aDocument(std::move(aDocument)), + m_eLocation(eLocation), + m_aLibName(std::move(aLibName)), + m_aLibSubName(std::move(aLibSubName)), + m_aName(std::move(aName)), + m_aMethodName(std::move(aMethodName)), + m_eType(eType) +{ + OSL_ENSURE( m_aDocument.isValid(), "EntryDescriptor::EntryDescriptor: invalid document!" ); +} + +SbTreeListBox::SbTreeListBox(std::unique_ptr<weld::TreeView> xControl, weld::Window* pTopLevel) + : m_xControl(std::move(xControl)) + , m_xScratchIter(m_xControl->make_iterator()) + , m_pTopLevel(pTopLevel) + , m_bFreezeOnFirstAddRemove(false) + , m_aNotifier(*this) +{ + m_xControl->connect_row_activated(LINK(this, SbTreeListBox, OpenCurrentHdl)); + m_xControl->connect_expanding(LINK(this, SbTreeListBox, RequestingChildrenHdl)); + nMode = BrowseMode::All; // everything +} + +SbTreeListBox::~SbTreeListBox() +{ + m_aNotifier.dispose(); + + bool bValidIter = m_xControl->get_iter_first(*m_xScratchIter); + while (bValidIter) + { + Entry* pBasicEntry = weld::fromId<Entry*>(m_xControl->get_id(*m_xScratchIter)); + delete pBasicEntry; + bValidIter = m_xControl->iter_next(*m_xScratchIter); + } +} + +void SbTreeListBox::ScanEntry( const ScriptDocument& rDocument, LibraryLocation eLocation ) +{ + OSL_ENSURE( rDocument.isAlive(), "TreeListBox::ScanEntry: illegal document!" ); + if ( !rDocument.isAlive() ) + return; + + // can be called multiple times for updating! + + // actually test if basic's in the tree already?! + // level 1: BasicManager (application, document, ...) + bool bDocumentRootEntry = FindRootEntry(rDocument, eLocation, *m_xScratchIter); + if (bDocumentRootEntry && m_xControl->get_row_expanded(*m_xScratchIter)) + ImpCreateLibEntries(*m_xScratchIter, rDocument, eLocation); + if (!bDocumentRootEntry) + { + OUString aRootName(GetRootEntryName(rDocument, eLocation)); + OUString aImage(GetRootEntryBitmaps(rDocument)); + AddEntry(aRootName, aImage, nullptr, true, std::make_unique<DocumentEntry>(rDocument, eLocation)); + } +} + +void SbTreeListBox::ImpCreateLibEntries(const weld::TreeIter& rIter, const ScriptDocument& rDocument, LibraryLocation eLocation) +{ + // get a sorted list of library names + Sequence< OUString > aLibNames( rDocument.getLibraryNames() ); + sal_Int32 nLibCount = aLibNames.getLength(); + const OUString* pLibNames = aLibNames.getConstArray(); + + for ( sal_Int32 i = 0 ; i < nLibCount ; i++ ) + { + OUString aLibName = pLibNames[ i ]; + + if ( eLocation == rDocument.getLibraryLocation( aLibName ) ) + { + // check, if the module library is loaded + bool bModLibLoaded = false; + Reference< script::XLibraryContainer > xModLibContainer( rDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryLoaded( aLibName ) ) + bModLibLoaded = true; + + // check, if the dialog library is loaded + bool bDlgLibLoaded = false; + Reference< script::XLibraryContainer > xDlgLibContainer( rDocument.getLibraryContainer( E_DIALOGS ) ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryLoaded( aLibName ) ) + bDlgLibLoaded = true; + + bool bLoaded = bModLibLoaded || bDlgLibLoaded; + + // if only one of the libraries is loaded, load also the other + if ( bLoaded ) + { + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && !xModLibContainer->isLibraryLoaded( aLibName ) ) + xModLibContainer->loadLibrary( aLibName ); + + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && !xDlgLibContainer->isLibraryLoaded( aLibName ) ) + xDlgLibContainer->loadLibrary( aLibName ); + } + + // create tree list box entry + OUString sId; + if ( ( nMode & BrowseMode::Dialogs ) && !( nMode & BrowseMode::Modules ) ) + sId = bLoaded ? RID_BMP_DLGLIB : RID_BMP_DLGLIBNOTLOADED; + else + sId = bLoaded ? RID_BMP_MODLIB : RID_BMP_MODLIBNOTLOADED; + std::unique_ptr<weld::TreeIter> xLibRootEntry(m_xControl->make_iterator(&rIter)); + bool bLibRootEntry = FindEntry(aLibName, OBJ_TYPE_LIBRARY, *xLibRootEntry); + if (bLibRootEntry) + { + SetEntryBitmaps(*xLibRootEntry, sId); + bool bRowExpanded = m_xControl->get_row_expanded(*xLibRootEntry); + bool bRowExpandAttempted = !m_xControl->get_children_on_demand(*xLibRootEntry); + if (bRowExpanded || bRowExpandAttempted) + ImpCreateLibSubEntries(*xLibRootEntry, rDocument, aLibName); + } + else + { + AddEntry(aLibName, sId, &rIter, true, std::make_unique<Entry>(OBJ_TYPE_LIBRARY)); + } + } + } +} + +void SbTreeListBox::ImpCreateLibSubEntries(const weld::TreeIter& rLibRootEntry, const ScriptDocument& rDocument, const OUString& rLibName) +{ + // modules + if ( nMode & BrowseMode::Modules ) + { + Reference< script::XLibraryContainer > xModLibContainer( rDocument.getLibraryContainer( E_SCRIPTS ) ); + + if ( xModLibContainer.is() && xModLibContainer->hasByName( rLibName ) && xModLibContainer->isLibraryLoaded( rLibName ) ) + { + try + { + if( rDocument.isInVBAMode() ) + { + ImpCreateLibSubEntriesInVBAMode(rLibRootEntry, rDocument, rLibName); + } + else + { + // get a sorted list of module names + Sequence< OUString > aModNames = rDocument.getObjectNames( E_SCRIPTS, rLibName ); + sal_Int32 nModCount = aModNames.getLength(); + const OUString* pModNames = aModNames.getConstArray(); + + auto xTreeIter = m_xControl->make_iterator(); + + for ( sal_Int32 i = 0 ; i < nModCount ; i++ ) + { + OUString aModName = pModNames[ i ]; + m_xControl->copy_iterator(rLibRootEntry, *xTreeIter); + bool bModuleEntry = FindEntry(aModName, OBJ_TYPE_MODULE, *xTreeIter); + if (!bModuleEntry) + { + AddEntry(aModName, RID_BMP_MODULE, &rLibRootEntry, false, std::make_unique<Entry>(OBJ_TYPE_MODULE), xTreeIter.get()); + } + + // methods + if ( nMode & BrowseMode::Subs ) + { + Sequence< OUString > aNames = GetMethodNames( rDocument, rLibName, aModName ); + sal_Int32 nCount = aNames.getLength(); + const OUString* pNames = aNames.getConstArray(); + + auto xSubTreeIter = m_xControl->make_iterator(); + + for ( sal_Int32 j = 0 ; j < nCount ; j++ ) + { + OUString aName = pNames[ j ]; + m_xControl->copy_iterator(*xTreeIter, *xSubTreeIter); + bool bEntry = FindEntry(aName, OBJ_TYPE_METHOD, *xSubTreeIter); + if (!bEntry) + { + AddEntry(aName, RID_BMP_MACRO, xTreeIter.get(), false, std::make_unique<Entry>(OBJ_TYPE_METHOD)); + } + } + } + } + } + } + catch ( const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + } + + // dialogs + if ( !(nMode & BrowseMode::Dialogs) ) + return; + + Reference< script::XLibraryContainer > xDlgLibContainer( rDocument.getLibraryContainer( E_DIALOGS ) ); + + if ( !(xDlgLibContainer.is() && xDlgLibContainer->hasByName( rLibName ) && xDlgLibContainer->isLibraryLoaded( rLibName )) ) + return; + + try + { + // get a sorted list of dialog names + Sequence< OUString > aDlgNames( rDocument.getObjectNames( E_DIALOGS, rLibName ) ); + sal_Int32 nDlgCount = aDlgNames.getLength(); + const OUString* pDlgNames = aDlgNames.getConstArray(); + + auto xTreeIter = m_xControl->make_iterator(); + + for ( sal_Int32 i = 0 ; i < nDlgCount ; i++ ) + { + OUString aDlgName = pDlgNames[ i ]; + m_xControl->copy_iterator(rLibRootEntry, *xTreeIter); + bool bDialogEntry = FindEntry(aDlgName, OBJ_TYPE_DIALOG, *xTreeIter); + if (!bDialogEntry) + { + AddEntry(aDlgName, RID_BMP_DIALOG, &rLibRootEntry, false, std::make_unique<Entry>(OBJ_TYPE_DIALOG)); + } + } + } + catch (const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } +} + +void SbTreeListBox::ImpCreateLibSubEntriesInVBAMode(const weld::TreeIter& rLibRootEntry, const ScriptDocument& rDocument, const OUString& rLibName ) +{ + auto const aEntries = { + std::make_pair( OBJ_TYPE_DOCUMENT_OBJECTS, IDEResId(RID_STR_DOCUMENT_OBJECTS) ), + std::make_pair( OBJ_TYPE_USERFORMS, IDEResId(RID_STR_USERFORMS) ), + std::make_pair( OBJ_TYPE_NORMAL_MODULES, IDEResId(RID_STR_NORMAL_MODULES) ), + std::make_pair( OBJ_TYPE_CLASS_MODULES, IDEResId(RID_STR_CLASS_MODULES) ) }; + for( auto const & iter: aEntries ) + { + EntryType eType = iter.first; + OUString const & aEntryName = iter.second; + std::unique_ptr<weld::TreeIter> xLibSubRootEntry(m_xControl->make_iterator(&rLibRootEntry)); + bool bLibSubRootEntry = FindEntry(aEntryName, eType, *xLibSubRootEntry); + if (bLibSubRootEntry) + { + SetEntryBitmaps(*xLibSubRootEntry, RID_BMP_MODLIB); + if (m_xControl->get_row_expanded(*xLibSubRootEntry)) + ImpCreateLibSubSubEntriesInVBAMode(*xLibSubRootEntry, rDocument, rLibName); + } + else + { + m_xControl->copy_iterator(rLibRootEntry, *xLibSubRootEntry); + AddEntry(aEntryName, RID_BMP_MODLIB, xLibSubRootEntry.get(), true, std::make_unique<Entry>(eType)); + } + } +} + +void SbTreeListBox::ImpCreateLibSubSubEntriesInVBAMode(const weld::TreeIter& rLibSubRootEntry, const ScriptDocument& rDocument, const OUString& rLibName) +{ + uno::Reference< container::XNameContainer > xLib = rDocument.getOrCreateLibrary( E_SCRIPTS, rLibName ); + if( !xLib.is() ) + return; + + try + { + // get a sorted list of module names + Sequence< OUString > aModNames = rDocument.getObjectNames( E_SCRIPTS, rLibName ); + sal_Int32 nModCount = aModNames.getLength(); + const OUString* pModNames = aModNames.getConstArray(); + + EntryDescriptor aDesc(GetEntryDescriptor(&rLibSubRootEntry)); + EntryType eCurrentType(aDesc.GetType()); + + for ( sal_Int32 i = 0 ; i < nModCount ; i++ ) + { + OUString aModName = pModNames[ i ]; + EntryType eType = OBJ_TYPE_UNKNOWN; + switch( ModuleInfoHelper::getModuleType( xLib, aModName ) ) + { + case script::ModuleType::DOCUMENT: + eType = OBJ_TYPE_DOCUMENT_OBJECTS; + break; + case script::ModuleType::FORM: + eType = OBJ_TYPE_USERFORMS; + break; + case script::ModuleType::NORMAL: + eType = OBJ_TYPE_NORMAL_MODULES; + break; + case script::ModuleType::CLASS: + eType = OBJ_TYPE_CLASS_MODULES; + break; + } + if( eType != eCurrentType ) + continue; + + // display a nice friendly name in the ObjectModule tab, + // combining the objectname and module name, e.g. Sheet1 ( Financials ) + OUString aEntryName = aModName; + if( eType == OBJ_TYPE_DOCUMENT_OBJECTS ) + { + OUString sObjName; + ModuleInfoHelper::getObjectName( xLib, aModName, sObjName ); + if( !sObjName.isEmpty() ) + { + aEntryName += " (" + sObjName + ")"; + } + } + std::unique_ptr<weld::TreeIter> xModuleEntry(m_xControl->make_iterator(&rLibSubRootEntry)); + bool bModuleEntry = FindEntry(aEntryName, OBJ_TYPE_MODULE, *xModuleEntry); + if (!bModuleEntry) + { + m_xControl->copy_iterator(rLibSubRootEntry, *xModuleEntry); + AddEntry(aEntryName, RID_BMP_MODULE, xModuleEntry.get(), false, + std::make_unique<Entry>(OBJ_TYPE_MODULE)); + } + + // methods + if ( nMode & BrowseMode::Subs ) + { + Sequence< OUString > aNames = GetMethodNames( rDocument, rLibName, aModName ); + sal_Int32 nCount = aNames.getLength(); + const OUString* pNames = aNames.getConstArray(); + + for ( sal_Int32 j = 0 ; j < nCount ; j++ ) + { + OUString aName = pNames[ j ]; + std::unique_ptr<weld::TreeIter> xEntry(m_xControl->make_iterator(xModuleEntry.get())); + bool bEntry = FindEntry(aName, OBJ_TYPE_METHOD, *xEntry); + if (!bEntry) + { + AddEntry(aName, RID_BMP_MACRO, xModuleEntry.get(), false, std::make_unique<Entry>(OBJ_TYPE_METHOD)); + } + } + } + } + } + catch ( const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } +} + +bool SbTreeListBox::ImpFindEntry(weld::TreeIter& rIter, std::u16string_view rText) +{ + bool bValidIter = m_xControl->iter_children(rIter); + while (bValidIter) + { + if (rText == m_xControl->get_text(rIter)) + return true; + bValidIter = m_xControl->iter_next_sibling(rIter); + } + return false; +} + +void SbTreeListBox::onDocumentCreated( const ScriptDocument& /*_rDocument*/ ) +{ + UpdateEntries(); +} + +void SbTreeListBox::onDocumentOpened( const ScriptDocument& /*_rDocument*/ ) +{ + UpdateEntries(); +} + +void SbTreeListBox::onDocumentSave( const ScriptDocument& /*_rDocument*/ ) +{ + // not interested in +} + +void SbTreeListBox::onDocumentSaveDone( const ScriptDocument& /*_rDocument*/ ) +{ + // not interested in +} + +void SbTreeListBox::onDocumentSaveAs( const ScriptDocument& /*_rDocument*/ ) +{ + // not interested in +} + +void SbTreeListBox::onDocumentSaveAsDone( const ScriptDocument& /*_rDocument*/ ) +{ + UpdateEntries(); +} + +void SbTreeListBox::onDocumentClosed( const ScriptDocument& rDocument ) +{ + UpdateEntries(); + // The document is not yet actually deleted, so we need to remove its entry + // manually. + RemoveEntry(rDocument); +} + +void SbTreeListBox::onDocumentTitleChanged( const ScriptDocument& /*_rDocument*/ ) +{ + // not interested in +} + +void SbTreeListBox::onDocumentModeChanged( const ScriptDocument& /*_rDocument*/ ) +{ + // not interested in +} + +void SbTreeListBox::UpdateEntries() +{ + bool bValidIter = m_xControl->get_selected(m_xScratchIter.get()); + EntryDescriptor aCurDesc(GetEntryDescriptor(bValidIter ? m_xScratchIter.get() : nullptr)); + + // removing the invalid entries + std::unique_ptr<weld::TreeIter> xLastValid(m_xControl->make_iterator(nullptr)); + bool bLastValid = false; + bValidIter = m_xControl->get_iter_first(*m_xScratchIter); + while (bValidIter) + { + if (IsValidEntry(*m_xScratchIter)) + { + m_xControl->copy_iterator(*m_xScratchIter, *xLastValid); + bLastValid = true; + } + else + RemoveEntry(*m_xScratchIter); + if (bLastValid) + { + m_xControl->copy_iterator(*xLastValid, *m_xScratchIter); + bValidIter = m_xControl->iter_next(*m_xScratchIter); + } + else + bValidIter = m_xControl->get_iter_first(*m_xScratchIter); + } + + ScanAllEntries(); + + SetCurrentEntry( aCurDesc ); +} + +// Removes the entry from the tree. +void SbTreeListBox::RemoveEntry(const weld::TreeIter& rIter) +{ + if (m_bFreezeOnFirstAddRemove) + { + m_xControl->freeze(); + m_bFreezeOnFirstAddRemove = false; + } + + // removing the associated user data + Entry* pBasicEntry = weld::fromId<Entry*>(m_xControl->get_id(rIter)); + delete pBasicEntry; + // removing the entry + m_xControl->remove(rIter); +} + +// Removes the entry of rDocument. +void SbTreeListBox::RemoveEntry (ScriptDocument const& rDocument) +{ + // finding the entry of rDocument + bool bValidIter = m_xControl->get_iter_first(*m_xScratchIter); + while (bValidIter) + { + if (rDocument == GetEntryDescriptor(m_xScratchIter.get()).GetDocument()) + { + RemoveEntry(*m_xScratchIter); + break; + } + bValidIter = m_xControl->iter_next(*m_xScratchIter); + } +} + +bool SbTreeListBox::FindEntry(std::u16string_view rText, EntryType eType, weld::TreeIter& rIter) +{ + bool bValidIter = m_xControl->iter_children(rIter); + while (bValidIter) + { + Entry* pBasicEntry = weld::fromId<Entry*>(m_xControl->get_id(rIter)); + assert(pBasicEntry && "FindEntry: no Entry ?!"); + if (pBasicEntry->GetType() == eType && rText == m_xControl->get_text(rIter)) + return true; + bValidIter = m_xControl->iter_next_sibling(rIter); + } + return false; +} + +bool SbTreeListBox::IsEntryProtected(const weld::TreeIter* pEntry) +{ + bool bProtected = false; + if (pEntry && m_xControl->get_iter_depth(*pEntry) == 1) + { + EntryDescriptor aDesc(GetEntryDescriptor(pEntry)); + const ScriptDocument& rDocument( aDesc.GetDocument() ); + OSL_ENSURE( rDocument.isAlive(), "TreeListBox::IsEntryProtected: no document, or document is dead!" ); + if ( rDocument.isAlive() ) + { + const OUString& aOULibName( aDesc.GetLibName() ); + Reference< script::XLibraryContainer > xModLibContainer( rDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aOULibName ) && !xPasswd->isLibraryPasswordVerified( aOULibName ) ) + { + bProtected = true; + } + } + } + } + return bProtected; +} + +void SbTreeListBox::AddEntry( + const OUString& rText, + const OUString& rImage, + const weld::TreeIter* pParent, + bool bChildrenOnDemand, + std::unique_ptr<Entry>&& rUserData, + weld::TreeIter* pRet) +{ + if (m_bFreezeOnFirstAddRemove) + { + m_xControl->freeze(); + m_bFreezeOnFirstAddRemove= false; + } + std::unique_ptr<weld::TreeIter> xScratch = pRet ? nullptr : m_xControl->make_iterator(); + if (!pRet) + pRet = xScratch.get(); + OUString sId(weld::toId(rUserData.release())); + m_xControl->insert(pParent, -1, &rText, &sId, nullptr, nullptr, bChildrenOnDemand, pRet); + m_xControl->set_image(*pRet, rImage); +} + +void SbTreeListBox::SetEntryBitmaps(const weld::TreeIter& rIter, const OUString& rImage) +{ + m_xControl->set_image(rIter, rImage, -1); +} + +LibraryType SbTreeListBox::GetLibraryType() const +{ + LibraryType eType = LibraryType::All; + if ( ( nMode & BrowseMode::Modules ) && !( nMode & BrowseMode::Dialogs ) ) + eType = LibraryType::Module; + else if ( !( nMode & BrowseMode::Modules ) && ( nMode & BrowseMode::Dialogs ) ) + eType = LibraryType::Dialog; + return eType; +} + +OUString SbTreeListBox::GetRootEntryName( const ScriptDocument& rDocument, LibraryLocation eLocation ) const +{ + return rDocument.getTitle( eLocation, GetLibraryType() ); +} + +OUString SbTreeListBox::GetRootEntryBitmaps(const ScriptDocument& rDocument) +{ + OSL_ENSURE( rDocument.isValid(), "TreeListBox::GetRootEntryBitmaps: illegal document!" ); + if (!rDocument.isValid()) + return OUString(); + + if ( rDocument.isDocument() ) + { + OUString sFactoryURL; + Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() ); + Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xContext) ); + try + { + OUString sModule( xModuleManager->identify( rDocument.getDocument() ) ); + Sequence< beans::PropertyValue > aModuleDescr; + xModuleManager->getByName( sModule ) >>= aModuleDescr; + sal_Int32 nCount = aModuleDescr.getLength(); + const beans::PropertyValue* pModuleDescr = aModuleDescr.getConstArray(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + if ( pModuleDescr[ i ].Name == "ooSetupFactoryEmptyDocumentURL" ) + { + pModuleDescr[ i ].Value >>= sFactoryURL; + break; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + if ( !sFactoryURL.isEmpty() ) + { + return SvFileInformationManager::GetFileImageId(INetURLObject(sFactoryURL)); + } + else + { + // default icon + return RID_BMP_DOCUMENT; + } + } + return RID_BMP_INSTALLATION; +} + +void SbTreeListBox::SetCurrentEntry (EntryDescriptor const & rDesc) +{ + bool bCurEntry = false; + auto xCurIter = m_xControl->make_iterator(); + EntryDescriptor aDesc = rDesc; + if ( aDesc.GetType() == OBJ_TYPE_UNKNOWN ) + { + aDesc = EntryDescriptor( + ScriptDocument::getApplicationScriptDocument(), + LIBRARY_LOCATION_USER, "Standard", + OUString(), ".", OBJ_TYPE_UNKNOWN + ); + } + ScriptDocument aDocument = aDesc.GetDocument(); + OSL_ENSURE( aDocument.isValid(), "TreeListBox::SetCurrentEntry: invalid document!" ); + LibraryLocation eLocation = aDesc.GetLocation(); + bool bRootEntry = FindRootEntry(aDocument, eLocation, *m_xScratchIter); + if (bRootEntry) + { + m_xControl->copy_iterator(*m_xScratchIter, *xCurIter); + bCurEntry = true; + const OUString& aLibName( aDesc.GetLibName() ); + if ( !aLibName.isEmpty() ) + { + m_xControl->expand_row(*m_xScratchIter); + auto xLibIter = m_xControl->make_iterator(m_xScratchIter.get()); + bool bLibEntry = FindEntry(aLibName, OBJ_TYPE_LIBRARY, *xLibIter); + if (bLibEntry) + { + m_xControl->copy_iterator(*xLibIter, *xCurIter); + const OUString& aLibSubName( aDesc.GetLibSubName() ); + if( !aLibSubName.isEmpty() ) + { + m_xControl->expand_row(*xLibIter); + auto xSubLibIter = m_xControl->make_iterator(xLibIter.get()); + bool bSubLibEntry = ImpFindEntry(*xSubLibIter, aLibSubName); + if (bSubLibEntry) + { + m_xControl->copy_iterator(*xSubLibIter, *xCurIter); + } + } + const OUString& aName( aDesc.GetName() ); + if ( !aName.isEmpty() ) + { + m_xControl->expand_row(*xCurIter); + EntryType eType = OBJ_TYPE_MODULE; + if ( aDesc.GetType() == OBJ_TYPE_DIALOG ) + eType = OBJ_TYPE_DIALOG; + auto xEntryIter = m_xControl->make_iterator(xCurIter.get()); + bool bEntry = FindEntry(aName, eType, *xEntryIter); + if (bEntry) + { + m_xControl->copy_iterator(*xEntryIter, *xCurIter); + const OUString& aMethodName( aDesc.GetMethodName() ); + if (!aMethodName.isEmpty()) + { + m_xControl->expand_row(*xCurIter); + auto xSubEntryIter = m_xControl->make_iterator(xCurIter.get()); + bool bSubEntry = FindEntry(aMethodName, OBJ_TYPE_METHOD, *xSubEntryIter); + if (bSubEntry) + { + m_xControl->copy_iterator(*xSubEntryIter, *xCurIter); + } + else + { + m_xControl->copy_iterator(*xCurIter, *xSubEntryIter); + if (m_xControl->iter_children(*xSubEntryIter)) + m_xControl->copy_iterator(*xSubEntryIter, *xCurIter); + } + } + } + else + { + auto xSubEntryIter = m_xControl->make_iterator(xCurIter.get()); + if (m_xControl->iter_children(*xSubEntryIter)) + m_xControl->copy_iterator(*xSubEntryIter, *xCurIter); + } + } + } + else + { + auto xSubLibIter = m_xControl->make_iterator(m_xScratchIter.get()); + if (m_xControl->iter_children(*xSubLibIter)) + m_xControl->copy_iterator(*xLibIter, *xCurIter); + } + } + } + else + { + bCurEntry = m_xControl->get_iter_first(*xCurIter); + } + + if (!bCurEntry) + return; + + m_xControl->set_cursor(*xCurIter); +} + +IMPL_LINK_NOARG(SbTreeListBox, OpenCurrentHdl, weld::TreeView&, bool) +{ + bool bValidIter = m_xControl->get_cursor(m_xScratchIter.get()); + if (!bValidIter) + return true; + if (!m_xControl->get_row_expanded(*m_xScratchIter)) + m_xControl->expand_row(*m_xScratchIter); + else + m_xControl->collapse_row(*m_xScratchIter); + + EntryDescriptor aDesc = GetEntryDescriptor(m_xScratchIter.get()); + switch (aDesc.GetType()) + { + case OBJ_TYPE_METHOD: + case OBJ_TYPE_MODULE: + case OBJ_TYPE_DIALOG: + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + SbxItem aSbxItem( + SID_BASICIDE_ARG_SBX, aDesc.GetDocument(), + aDesc.GetLibName(), aDesc.GetName(), aDesc.GetMethodName(), + ConvertType(aDesc.GetType()) + ); + pDispatcher->ExecuteList( + SID_BASICIDE_SHOWSBX, SfxCallMode::SYNCHRON, + { &aSbxItem } + ); + } + break; + + default: + break; + } + return true; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/bastype3.cxx b/basctl/source/basicide/bastype3.cxx new file mode 100644 index 0000000000..d26ae83252 --- /dev/null +++ b/basctl/source/basicide/bastype3.cxx @@ -0,0 +1,440 @@ +/* -*- 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 <basic/basmgr.hxx> +#include <basic/sbmod.hxx> +#include <basobj.hxx> +#include <bastype2.hxx> +#include <bitmaps.hlst> +#include <bastypes.hxx> +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <string_view> +#include <osl/diagnose.h> +#include <tools/debug.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +IMPL_LINK(SbTreeListBox, RequestingChildrenHdl, const weld::TreeIter&, rEntry, bool) +{ + EntryDescriptor aDesc = GetEntryDescriptor(&rEntry); + const ScriptDocument& aDocument = aDesc.GetDocument(); + OSL_ENSURE( aDocument.isAlive(), "basctl::TreeListBox::RequestingChildren: invalid document!" ); + if (!aDocument.isAlive()) + return false; + + LibraryLocation eLocation = aDesc.GetLocation(); + EntryType eType = aDesc.GetType(); + + if ( eType == OBJ_TYPE_DOCUMENT ) + { + ImpCreateLibEntries( rEntry, aDocument, eLocation ); + } + else if ( eType == OBJ_TYPE_LIBRARY ) + { + const OUString& aOULibName( aDesc.GetLibName() ); + + // check password + bool bOK = true; + Reference< script::XLibraryContainer > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aOULibName ) && !xPasswd->isLibraryPasswordVerified( aOULibName ) ) + { + OUString aPassword; + bOK = QueryPassword(m_pTopLevel, xModLibContainer, aOULibName, aPassword); + } + } + + if ( bOK ) + { + // load module library + bool bModLibLoaded = false; + if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) ) + { + if ( !xModLibContainer->isLibraryLoaded( aOULibName ) ) + { + weld::WaitObject aWait(m_pTopLevel); + xModLibContainer->loadLibrary( aOULibName ); + } + bModLibLoaded = xModLibContainer->isLibraryLoaded( aOULibName ); + } + + // load dialog library + bool bDlgLibLoaded = false; + Reference< script::XLibraryContainer > xDlgLibContainer = aDocument.getLibraryContainer( E_DIALOGS ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aOULibName ) ) + { + if ( !xDlgLibContainer->isLibraryLoaded( aOULibName ) ) + { + weld::WaitObject aWait(m_pTopLevel); + xDlgLibContainer->loadLibrary( aOULibName ); + } + bDlgLibLoaded = xDlgLibContainer->isLibraryLoaded( aOULibName ); + } + + if ( bModLibLoaded || bDlgLibLoaded ) + { + // create the sub entries + ImpCreateLibSubEntries( rEntry, aDocument, aOULibName ); + + // exchange image + const bool bDlgMode = (nMode & BrowseMode::Dialogs) && !(nMode & BrowseMode::Modules); + auto const aImage(bDlgMode ? RID_BMP_DLGLIB : RID_BMP_MODLIB); + SetEntryBitmaps(rEntry, aImage); + } + else + { + OSL_FAIL( "basctl::TreeListBox::RequestingChildren: Error loading library!" ); + } + } + } + else if ( eType == OBJ_TYPE_DOCUMENT_OBJECTS + || eType == OBJ_TYPE_USERFORMS + || eType == OBJ_TYPE_NORMAL_MODULES + || eType == OBJ_TYPE_CLASS_MODULES ) + { + const OUString& aLibName( aDesc.GetLibName() ); + ImpCreateLibSubSubEntriesInVBAMode( rEntry, aDocument, aLibName ); + } + + return true; +} + +void SbTreeListBox::ScanAllEntries() +{ + // instead of always freezing, freeze on the first add/remove, which keeps gtk + // from relayouting the tree if it's not necessary + m_bFreezeOnFirstAddRemove = true; + + ScanEntry( ScriptDocument::getApplicationScriptDocument(), LIBRARY_LOCATION_USER ); + ScanEntry( ScriptDocument::getApplicationScriptDocument(), LIBRARY_LOCATION_SHARE ); + + ScriptDocuments aDocuments( ScriptDocument::getAllScriptDocuments( ScriptDocument::DocumentsSorted ) ); + for (auto const& doc : aDocuments) + { + if ( doc.isAlive() ) + ScanEntry(doc, LIBRARY_LOCATION_DOCUMENT); + } + + if (!m_bFreezeOnFirstAddRemove) + m_xControl->thaw(); // m_bFreezeOnFirstAddRemove was changed, so control was frozen + else + m_bFreezeOnFirstAddRemove = false; +} + +SbxVariable* SbTreeListBox::FindVariable(const weld::TreeIter* pEntry) +{ + if ( !pEntry ) + return nullptr; + + ScriptDocument aDocument( ScriptDocument::getApplicationScriptDocument() ); + std::unique_ptr<weld::TreeIter> xIter(m_xControl->make_iterator(pEntry)); + std::vector<std::pair<Entry*, OUString>> aEntries; + bool bValidIter = true; + do + { + sal_uInt16 nDepth = m_xControl->get_iter_depth(*xIter); + Entry* pBE = weld::fromId<Entry*>(m_xControl->get_id(*xIter)); + switch (nDepth) + { + case 4: + case 3: + case 2: + case 1: + { + aEntries.emplace_back(pBE, m_xControl->get_text(*xIter)); + } + break; + case 0: + { + aDocument = static_cast<DocumentEntry*>(pBE)->GetDocument(); + } + break; + } + bValidIter = m_xControl->iter_parent(*xIter); + } while (bValidIter); + + SbxVariable* pVar = nullptr; + if (!aEntries.empty()) + { + std::reverse(aEntries.begin(), aEntries.end()); + bool bDocumentObjects = false; + for (const auto& pair : aEntries) + { + Entry* pBE = pair.first; + assert(pBE && "No data found in entry!"); + OUString aName(pair.second); + + switch ( pBE->GetType() ) + { + case OBJ_TYPE_LIBRARY: + if (BasicManager* pBasMgr = aDocument.getBasicManager()) + pVar = pBasMgr->GetLib( aName ); + break; + case OBJ_TYPE_MODULE: + DBG_ASSERT(dynamic_cast<StarBASIC*>(pVar), "FindVariable: invalid Basic"); + if(!pVar) + { + break; + } + // extract the module name from the string like "Sheet1 (Example1)" + if( bDocumentObjects ) + { + aName = aName.getToken( 0, ' ' ); + } + pVar = static_cast<StarBASIC*>(pVar)->FindModule( aName ); + break; + case OBJ_TYPE_METHOD: + DBG_ASSERT(dynamic_cast<SbxObject*>(pVar), "FindVariable: invalid module/object"); + if(!pVar) + { + break; + } + pVar = static_cast<SbxObject*>(pVar)->GetMethods()->Find(aName, SbxClassType::Method); + break; + case OBJ_TYPE_DIALOG: + // sbx dialogs removed + break; + case OBJ_TYPE_DOCUMENT_OBJECTS: + bDocumentObjects = true; + [[fallthrough]]; + case OBJ_TYPE_USERFORMS: + case OBJ_TYPE_NORMAL_MODULES: + case OBJ_TYPE_CLASS_MODULES: + // skip, to find the child entry. + continue; + default: + OSL_FAIL( "FindVariable: unknown type" ); + pVar = nullptr; + break; + } + if ( !pVar ) + break; + } + } + return pVar; +} + +EntryDescriptor SbTreeListBox::GetEntryDescriptor(const weld::TreeIter* pEntry) +{ + ScriptDocument aDocument( ScriptDocument::getApplicationScriptDocument() ); + LibraryLocation eLocation = LIBRARY_LOCATION_UNKNOWN; + OUString aLibName; + OUString aLibSubName; + OUString aName; + OUString aMethodName; + EntryType eType = OBJ_TYPE_UNKNOWN; + + if ( !pEntry ) + return EntryDescriptor( aDocument, eLocation, aLibName, aLibSubName, aName, aMethodName, eType ); + + std::vector<std::pair<Entry*, OUString>> aEntries; + + std::unique_ptr<weld::TreeIter> xIter(m_xControl->make_iterator(pEntry)); + bool bValidIter = true; + do + { + sal_uInt16 nDepth = m_xControl->get_iter_depth(*xIter); + Entry* pBE = weld::fromId<Entry*>(m_xControl->get_id(*xIter)); + switch (nDepth) + { + case 4: + case 3: + case 2: + case 1: + { + aEntries.emplace_back(pBE, m_xControl->get_text(*xIter)); + } + break; + case 0: + { + if (DocumentEntry* pDocumentEntry = static_cast<DocumentEntry*>(pBE)) + { + aDocument = pDocumentEntry->GetDocument(); + eLocation = pDocumentEntry->GetLocation(); + eType = OBJ_TYPE_DOCUMENT; + } + } + break; + } + bValidIter = m_xControl->iter_parent(*xIter); + } while (bValidIter); + + if ( !aEntries.empty() ) + { + std::reverse(aEntries.begin(), aEntries.end()); + for (const auto& pair : aEntries) + { + Entry* pBE = pair.first; + assert(pBE && "No data found in entry!"); + + switch ( pBE->GetType() ) + { + case OBJ_TYPE_LIBRARY: + { + aLibName = pair.second; + eType = pBE->GetType(); + } + break; + case OBJ_TYPE_MODULE: + { + aName = pair.second; + eType = pBE->GetType(); + } + break; + case OBJ_TYPE_METHOD: + { + aMethodName = pair.second; + eType = pBE->GetType(); + } + break; + case OBJ_TYPE_DIALOG: + { + aName = pair.second; + eType = pBE->GetType(); + } + break; + case OBJ_TYPE_DOCUMENT_OBJECTS: + case OBJ_TYPE_USERFORMS: + case OBJ_TYPE_NORMAL_MODULES: + case OBJ_TYPE_CLASS_MODULES: + { + aLibSubName = pair.second; + eType = pBE->GetType(); + } + break; + default: + { + OSL_FAIL( "GetEntryDescriptor: unknown type" ); + eType = OBJ_TYPE_UNKNOWN; + } + break; + } + + if ( eType == OBJ_TYPE_UNKNOWN ) + break; + } + } + + return EntryDescriptor( aDocument, eLocation, aLibName, aLibSubName, aName, aMethodName, eType ); +} + +ItemType SbTreeListBox::ConvertType (EntryType eType) +{ + switch (eType) + { + case OBJ_TYPE_DOCUMENT: return TYPE_SHELL; + case OBJ_TYPE_LIBRARY: return TYPE_LIBRARY; + case OBJ_TYPE_MODULE: return TYPE_MODULE; + case OBJ_TYPE_DIALOG: return TYPE_DIALOG; + case OBJ_TYPE_METHOD: return TYPE_METHOD; + default: + return static_cast<ItemType>(OBJ_TYPE_UNKNOWN); + } +} + +bool SbTreeListBox::IsValidEntry(const weld::TreeIter& rEntry) +{ + bool bIsValid = false; + + EntryDescriptor aDesc(GetEntryDescriptor(&rEntry)); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + LibraryLocation eLocation( aDesc.GetLocation() ); + const OUString& aLibName( aDesc.GetLibName() ); + const OUString& aName( aDesc.GetName() ); + const OUString& aMethodName( aDesc.GetMethodName() ); + EntryType eType( aDesc.GetType() ); + + switch ( eType ) + { + case OBJ_TYPE_DOCUMENT: + { + bIsValid = aDocument.isAlive() + && (aDocument.isApplication() + || GetRootEntryName(aDocument, eLocation) == m_xControl->get_text(rEntry)); + } + break; + case OBJ_TYPE_LIBRARY: + { + bIsValid = aDocument.hasLibrary( E_SCRIPTS, aLibName ) || aDocument.hasLibrary( E_DIALOGS, aLibName ); + } + break; + case OBJ_TYPE_MODULE: + { + bIsValid = aDocument.hasModule( aLibName, aName ); + } + break; + case OBJ_TYPE_DIALOG: + { + bIsValid = aDocument.hasDialog( aLibName, aName ); + } + break; + case OBJ_TYPE_METHOD: + { + bIsValid = HasMethod( aDocument, aLibName, aName, aMethodName ); + } + break; + case OBJ_TYPE_DOCUMENT_OBJECTS: + case OBJ_TYPE_USERFORMS: + case OBJ_TYPE_NORMAL_MODULES: + case OBJ_TYPE_CLASS_MODULES: + { + bIsValid = true; + } + break; + default: ; + } + + return bIsValid; +} + +SbModule* SbTreeListBox::FindModule(const weld::TreeIter* pEntry) +{ + return dynamic_cast<SbModule*>(FindVariable(pEntry)); +} + +bool SbTreeListBox::FindRootEntry( const ScriptDocument& rDocument, LibraryLocation eLocation, weld::TreeIter& rIter) +{ + OSL_ENSURE( rDocument.isValid(), "basctl::TreeListBox::FindRootEntry: invalid document!" ); + bool bValidIter = m_xControl->get_iter_first(rIter); + while (bValidIter) + { + DocumentEntry* pBDEntry = weld::fromId<DocumentEntry*>(m_xControl->get_id(rIter)); + if (pBDEntry && pBDEntry->GetDocument() == rDocument && pBDEntry->GetLocation() == eLocation) + return true; + bValidIter = m_xControl->iter_next_sibling(rIter); + } + return false; +} + +OUString CreateMgrAndLibStr( std::u16string_view rMgrName, std::u16string_view rLibName ) +{ + return OUString::Concat("[") + rMgrName + "]." + rLibName; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/bastypes.cxx b/basctl/source/basicide/bastypes.cxx new file mode 100644 index 0000000000..e436bc9f7e --- /dev/null +++ b/basctl/source/basicide/bastypes.cxx @@ -0,0 +1,831 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <strings.hrc> +#include <helpids.h> +#include <iderid.hxx> + +#include "baside2.hxx" +#include <baside3.hxx> +#include <basidesh.hxx> +#include <basobj.hxx> +#include <iderdll.hxx> +#include "iderdll2.hxx" + +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <sal/log.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/infobar.hxx> +#include <sfx2/passwd.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <svl/intitem.hxx> +#include <svl/stritem.hxx> +#include <svl/srchdefs.hxx> +#include <svl/itemset.hxx> +#include <utility> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <tools/stream.hxx> +#include <o3tl/hash_combine.hxx> + +namespace basctl +{ + +// ID used for the read-only infobar +constexpr OUString BASIC_IDE_READONLY_INFOBAR = u"readonly"_ustr; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +BaseWindow::BaseWindow( vcl::Window* pParent, ScriptDocument aDocument, OUString aLibName, OUString aName ) + :Window( pParent, WinBits( WB_3DLOOK ) ) + ,pShellHScrollBar( nullptr) + ,pShellVScrollBar( nullptr) + ,nStatus( 0) + ,m_aDocument(std::move( aDocument )) + ,m_aLibName(std::move( aLibName )) + ,m_aName(std::move( aName )) +{ +} + +BaseWindow::~BaseWindow() +{ + disposeOnce(); +} + +void BaseWindow::dispose() +{ + if (pShellVScrollBar && !pShellVScrollBar->isDisposed()) + pShellVScrollBar->SetScrollHdl( Link<weld::Scrollbar&,void>() ); + if (pShellHScrollBar && !pShellHScrollBar->isDisposed()) + pShellHScrollBar->SetScrollHdl( Link<weld::Scrollbar&,void>() ); + pShellVScrollBar.clear(); + pShellHScrollBar.clear(); + vcl::Window::dispose(); +} + +void BaseWindow::Init() +{ + if ( pShellVScrollBar ) + pShellVScrollBar->SetScrollHdl( LINK( this, BaseWindow, VertScrollHdl ) ); + if ( pShellHScrollBar ) + pShellHScrollBar->SetScrollHdl( LINK( this, BaseWindow, HorzScrollHdl ) ); + + // Show the read-only infobar if the module/dialog is read-only + GetShell()->GetViewFrame().RemoveInfoBar(BASIC_IDE_READONLY_INFOBAR); + if (IsReadOnly()) + ShowReadOnlyInfoBar(); + + DoInit(); // virtual... +} + +void BaseWindow::DoInit() +{ +} + +void BaseWindow::GrabScrollBars(ScrollAdaptor* pHScroll, ScrollAdaptor* pVScroll) +{ + pShellHScrollBar = pHScroll; + pShellVScrollBar = pVScroll; +} + +IMPL_LINK_NOARG(BaseWindow, VertScrollHdl, weld::Scrollbar&, void) +{ + DoScroll(pShellVScrollBar); +} + +IMPL_LINK_NOARG(BaseWindow, HorzScrollHdl, weld::Scrollbar&, void) +{ + DoScroll(pShellHScrollBar); +} + +void BaseWindow::ExecuteCommand (SfxRequest&) +{ +} + +void BaseWindow::ExecuteGlobal (SfxRequest&) +{ +} + +bool BaseWindow::EventNotify( NotifyEvent& rNEvt ) +{ + bool bDone = false; + + if ( rNEvt.GetType() == NotifyEventType::KEYINPUT ) + { + KeyEvent aKEvt = *rNEvt.GetKeyEvent(); + vcl::KeyCode aCode = aKEvt.GetKeyCode(); + sal_uInt16 nCode = aCode.GetCode(); + + switch ( nCode ) + { + case KEY_PAGEUP: + case KEY_PAGEDOWN: + { + if ( aCode.IsMod1() ) + { + if (Shell* pShell = GetShell()) + pShell->NextPage( nCode == KEY_PAGEUP ); + bDone = true; + } + } + break; + } + } + + return bDone || Window::EventNotify( rNEvt ); +} + +void BaseWindow::ShowShellScrollBars(bool bVisible) +{ + if (bVisible) + { + if (pShellHScrollBar) + { + pShellHScrollBar->Enable(); + pShellHScrollBar->Show(); + } + if (pShellVScrollBar) + { + pShellVScrollBar->Enable(); + pShellVScrollBar->Show(); + } + } + else + { + if (pShellHScrollBar) + { + pShellHScrollBar->Disable(); + pShellHScrollBar->Hide(); + } + if (pShellVScrollBar) + { + pShellVScrollBar->Disable(); + pShellVScrollBar->Hide(); + } + } +} + +void BaseWindow::DoScroll( Scrollable* ) +{ +} + +void BaseWindow::StoreData() +{ +} + +bool BaseWindow::AllowUndo() +{ + return true; +} + +void BaseWindow::UpdateData() +{ +} + +OUString BaseWindow::GetTitle() +{ + return OUString(); +} + +OUString BaseWindow::CreateQualifiedName() +{ + OUString aName; + if ( !m_aLibName.isEmpty() ) + { + LibraryLocation eLocation = m_aDocument.getLibraryLocation( m_aLibName ); + aName = m_aDocument.getTitle(eLocation) + "." + m_aLibName + "." + + GetTitle(); + } + return aName; +} + +void BaseWindow::SetReadOnly (bool) +{ +} + +bool BaseWindow::IsReadOnly () +{ + return false; +} + +// Show the read-only warning messages for module and dialog windows +void BaseWindow::ShowReadOnlyInfoBar() +{ + OUString aMsg; + if (dynamic_cast<ModulWindow*>(this)) + aMsg = IDEResId(RID_STR_MODULE_READONLY); + else + aMsg = IDEResId(RID_STR_DIALOG_READONLY); + + GetShell()->GetViewFrame().AppendInfoBar(BASIC_IDE_READONLY_INFOBAR, OUString(), + aMsg, InfobarType::INFO, true); +} + +void BaseWindow::BasicStarted() +{ +} + +void BaseWindow::BasicStopped() +{ +} + +bool BaseWindow::IsModified () +{ + return true; +} + +SfxUndoManager* BaseWindow::GetUndoManager() +{ + return nullptr; +} + +SearchOptionFlags BaseWindow::GetSearchOptions() +{ + return SearchOptionFlags::NONE; +} + +sal_uInt16 BaseWindow::StartSearchAndReplace (SvxSearchItem const&, bool) +{ + return 0; +} + +void BaseWindow::OnNewDocument () +{ } + +void BaseWindow::InsertLibInfo () const +{ + if (ExtraData* pData = GetExtraData()) + pData->GetLibInfo().InsertInfo(m_aDocument, m_aLibName, m_aName, GetType()); +} + +bool BaseWindow::Is ( + ScriptDocument const& rDocument, + std::u16string_view rLibName, std::u16string_view rName, + ItemType eType, bool bFindSuspended +) +{ + if (bFindSuspended || !IsSuspended()) + { + // any non-suspended window is ok + if (rLibName.empty() || rName.empty() || eType == TYPE_UNKNOWN) + return true; + // ok if the parameters match + if (m_aDocument == rDocument && m_aLibName == rLibName && m_aName == rName && GetType() == eType) + return true; + } + return false; +} + +bool BaseWindow::HasActiveEditor () const +{ + return false; +} + + +// DockingWindow + + +// style bits for DockingWindow +WinBits const DockingWindow::StyleBits = + WB_BORDER | WB_3DLOOK | WB_CLIPCHILDREN | + WB_MOVEABLE | WB_SIZEABLE | WB_DOCKABLE; + +DockingWindow::DockingWindow(vcl::Window* pParent, const OUString& rUIXMLDescription, const OUString& rID) + : ResizableDockingWindow(pParent) + , m_xBuilder(Application::CreateInterimBuilder(m_xBox.get(), rUIXMLDescription, true)) + , pLayout(nullptr) + , nShowCount(0) +{ + m_xContainer = m_xBuilder->weld_container(rID); +} + +DockingWindow::DockingWindow (Layout* pParent) + : ResizableDockingWindow(pParent, StyleBits) + , pLayout(pParent) + , nShowCount(0) +{ } + +DockingWindow::~DockingWindow() +{ + disposeOnce(); +} + +void DockingWindow::dispose() +{ + m_xContainer.reset(); + m_xBuilder.reset(); + pLayout.clear(); + ResizableDockingWindow::dispose(); +} + +// Sets the position and the size of the docking window. This property is saved +// when the window is floating. Called by Layout. +void DockingWindow::ResizeIfDocking (Point const& rPos, Size const& rSize) +{ + tools::Rectangle const rRect(rPos, rSize); + if (rRect != aDockingRect) + { + // saving the position and the size + aDockingRect = rRect; + // resizing if actually docking + if (!IsFloatingMode()) + SetPosSizePixel(rPos, rSize); + } +} +void DockingWindow::ResizeIfDocking (Size const& rSize) +{ + ResizeIfDocking(aDockingRect.TopLeft(), rSize); +} + +// Sets the parent Layout window. +// The physical parent is set only when the window is docking. +void DockingWindow::SetLayoutWindow (Layout* pLayout_) +{ + pLayout = pLayout_; + if (!IsFloatingMode()) + SetParent(pLayout); + +} + +// Increases the "show" reference count. +// The window is shown when the reference count is positive. +void DockingWindow::Show (bool bShow) // = true +{ + if (bShow) + { + if (++nShowCount == 1) + ResizableDockingWindow::Show(); + } + else + { + if (--nShowCount == 0) + ResizableDockingWindow::Hide(); + } +} + +// Decreases the "show" reference count. +// The window is hidden when the reference count reaches zero. +void DockingWindow::Hide () +{ + Show(false); +} + +bool DockingWindow::Docking( const Point& rPos, tools::Rectangle& rRect ) +{ + if (aDockingRect.Contains(rPos)) + { + rRect.SetSize(aDockingRect.GetSize()); + return false; // dock + } + else // adjust old size + { + if (!aFloatingRect.IsEmpty()) + rRect.SetSize(aFloatingRect.GetSize()); + return true; // float + } +} + +void DockingWindow::EndDocking( const tools::Rectangle& rRect, bool bFloatMode ) +{ + if ( bFloatMode ) + ResizableDockingWindow::EndDocking( rRect, bFloatMode ); + else + { + SetFloatingMode(false); + DockThis(); + } +} + +void DockingWindow::ToggleFloatingMode() +{ + if (IsFloatingMode()) + { + if (!aFloatingRect.IsEmpty()) + SetPosSizePixel( + GetParent()->ScreenToOutputPixel(aFloatingRect.TopLeft()), + aFloatingRect.GetSize() + ); + } + DockThis(); +} + +bool DockingWindow::PrepareToggleFloatingMode() +{ + if (IsFloatingMode()) + { + // memorize position and size on the desktop... + aFloatingRect = tools::Rectangle( + GetParent()->OutputToScreenPixel(GetPosPixel()), + GetSizePixel() + ); + } + return true; +} + +void DockingWindow::StartDocking() +{ + if (IsFloatingMode()) + { + aFloatingRect = tools::Rectangle( + GetParent()->OutputToScreenPixel(GetPosPixel()), + GetSizePixel() + ); + } +} + +void DockingWindow::DockThis () +{ + // resizing when floating -> docking + if (!IsFloatingMode()) + { + Point const aPos = aDockingRect.TopLeft(); + Size const aSize = aDockingRect.GetSize(); + if (aSize != GetSizePixel() || aPos != GetPosPixel()) + SetPosSizePixel(aPos, aSize); + } + + if (pLayout) + { + if (!IsFloatingMode() && GetParent() != pLayout) + SetParent(pLayout); + pLayout->ArrangeWindows(); + } +} + +TabBar::TabBar( vcl::Window* pParent ) : + ::TabBar( pParent, WinBits( WB_3DLOOK | WB_SCROLL | WB_BORDER | WB_DRAG ) ) +{ + EnableEditMode(); + + SetHelpId( HID_BASICIDE_TABBAR ); +} + +void TabBar::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if ( rMEvt.IsLeft() && ( rMEvt.GetClicks() == 2 ) && !IsInEditMode() ) + { + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute( SID_BASICIDE_MODULEDLG ); + } + else + { + ::TabBar::MouseButtonDown( rMEvt ); // base class version + } +} + +void TabBar::Command( const CommandEvent& rCEvt ) +{ + if ( ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) && !IsInEditMode() ) + { + Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) ); + if ( rCEvt.IsMouseEvent() ) // select right tab + { + Point aP = PixelToLogic( aPos ); + MouseEvent aMouseEvent( aP, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT ); + ::TabBar::MouseButtonDown( aMouseEvent ); // base class + } + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->ExecutePopup("tabbar", this, &aPos); + } +} + +TabBarAllowRenamingReturnCode TabBar::AllowRenaming() +{ + bool const bValid = IsValidSbxName(GetEditText()); + + if ( !bValid ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xError->run(); + } + + return bValid ? TABBAR_RENAMING_YES : TABBAR_RENAMING_NO; +} + + +void TabBar::EndRenaming() +{ + if ( !IsEditModeCanceled() ) + { + SfxUInt16Item aID( SID_BASICIDE_ARG_TABID, GetEditPageId() ); + SfxStringItem aNewName( SID_BASICIDE_ARG_MODULENAME, GetEditText() ); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->ExecuteList( SID_BASICIDE_NAMECHANGEDONTAB, + SfxCallMode::SYNCHRON, { &aID, &aNewName }); + } +} + + +namespace +{ + +// helper class for sorting TabBar +struct TabBarSortHelper +{ + sal_uInt16 nPageId; + OUString aPageText; + + bool operator < (TabBarSortHelper const& rComp) const + { + return aPageText.compareToIgnoreAsciiCase(rComp.aPageText) < 0; + } +}; + +} // namespace + +void TabBar::Sort() +{ + Shell* pShell = GetShell(); + if (!pShell) + return; + + Shell::WindowTable& aWindowTable = pShell->GetWindowTable(); + TabBarSortHelper aTabBarSortHelper; + std::vector<TabBarSortHelper> aModuleList; + std::vector<TabBarSortHelper> aDialogList; + sal_uInt16 nPageCount = GetPageCount(); + sal_uInt16 i; + + // create module and dialog lists for sorting + for ( i = 0; i < nPageCount; i++) + { + sal_uInt16 nId = GetPageId( i ); + aTabBarSortHelper.nPageId = nId; + aTabBarSortHelper.aPageText = GetPageText( nId ); + BaseWindow* pWin = aWindowTable[ nId ].get(); + + if (dynamic_cast<ModulWindow*>(pWin)) + { + aModuleList.push_back( aTabBarSortHelper ); + } + else if (dynamic_cast<DialogWindow*>(pWin)) + { + aDialogList.push_back( aTabBarSortHelper ); + } + } + + // sort module and dialog lists by page text + std::sort( aModuleList.begin() , aModuleList.end() ); + std::sort( aDialogList.begin() , aDialogList.end() ); + + + sal_uInt16 nModules = sal::static_int_cast<sal_uInt16>( aModuleList.size() ); + sal_uInt16 nDialogs = sal::static_int_cast<sal_uInt16>( aDialogList.size() ); + + // move module pages to new positions + for (i = 0; i < nModules; i++) + { + MovePage( aModuleList[i].nPageId , i ); + } + + // move dialog pages to new positions + for (i = 0; i < nDialogs; i++) + { + MovePage( aDialogList[i].nPageId , nModules + i ); + } +} + +void CutLines( OUString& rStr, sal_Int32 nStartLine, sal_Int32 nLines ) +{ + sal_Int32 nStartPos = 0; + sal_Int32 nLine = 0; + while ( nLine < nStartLine ) + { + nStartPos = searchEOL( rStr, nStartPos ); + if( nStartPos == -1 ) + break; + nStartPos++; // not the \n. + nLine++; + } + + SAL_WARN_IF( nStartPos == -1, "basctl.basicide", "CutLines: Start line not found!" ); + + if ( nStartPos == -1 ) + return; + + sal_Int32 nEndPos = nStartPos; + + for ( sal_Int32 i = 0; i < nLines; i++ ) + nEndPos = searchEOL( rStr, nEndPos+1 ); + + if ( nEndPos == -1 ) // might happen at the last line + nEndPos = rStr.getLength(); + else + nEndPos++; + + rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( nEndPos ); + + // erase trailing empty lines + { + sal_Int32 n = nStartPos; + sal_Int32 nLen = rStr.getLength(); + while ( ( n < nLen ) && ( rStr[ n ] == LINE_SEP || + rStr[ n ] == LINE_SEP_CR ) ) + { + n++; + } + + if ( n > nStartPos ) + { + rStr = OUString::Concat(rStr.subView( 0, nStartPos )) + rStr.subView( n ); + } + } +} + +sal_uInt32 CalcLineCount( SvStream& rStream ) +{ + sal_uInt32 nLFs = 0; + sal_uInt32 nCRs = 0; + char c; + + rStream.Seek( 0 ); + rStream.ReadChar( c ); + while ( !rStream.eof() ) + { + if ( c == '\n' ) + nLFs++; + else if ( c == '\r' ) + nCRs++; + rStream.ReadChar( c ); + } + + rStream.Seek( 0 ); + if ( nLFs > nCRs ) + return nLFs; + return nCRs; +} + + +// LibInfo + + +LibInfo::LibInfo () +{ } + +LibInfo::~LibInfo () +{ } + +void LibInfo::InsertInfo ( + ScriptDocument const& rDocument, + OUString const& rLibName, + OUString const& rCurrentName, + ItemType eCurrentType +) +{ + Key aKey(rDocument, rLibName); + m_aMap.erase(aKey); + m_aMap.emplace(aKey, Item(rCurrentName, eCurrentType)); +} + +void LibInfo::RemoveInfoFor (ScriptDocument const& rDocument) +{ + Map::iterator it = std::find_if(m_aMap.begin(), m_aMap.end(), + [&rDocument](Map::reference rEntry) { return rEntry.first.GetDocument() == rDocument; }); + if (it != m_aMap.end()) + m_aMap.erase(it); +} + +LibInfo::Item const* LibInfo::GetInfo ( + ScriptDocument const& rDocument, OUString const& rLibName +) +{ + Map::iterator it = m_aMap.find(Key(rDocument, rLibName)); + return it != m_aMap.end() ? &it->second : nullptr; +} + +LibInfo::Key::Key (ScriptDocument aDocument, OUString aLibName) : + m_aDocument(std::move(aDocument)), m_aLibName(std::move(aLibName)) +{ } + +bool LibInfo::Key::operator == (Key const& rKey) const +{ + return m_aDocument == rKey.m_aDocument && m_aLibName == rKey.m_aLibName; +} + +size_t LibInfo::Key::Hash::operator () (Key const& rKey) const +{ + std::size_t seed = 0; + o3tl::hash_combine(seed, rKey.m_aDocument.hashCode()); + o3tl::hash_combine(seed, rKey.m_aLibName.hashCode()); + return seed; +} + +LibInfo::Item::Item ( + OUString aCurrentName, + ItemType eCurrentType +) : + m_aCurrentName(std::move(aCurrentName)), + m_eCurrentType(eCurrentType) +{ } + +static bool QueryDel(std::u16string_view rName, const OUString &rStr, weld::Widget* pParent) +{ + OUString aName = OUString::Concat("\'") + rName + "\'"; + OUString aQuery = rStr.replaceAll("XX", aName); + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pParent, + VclMessageType::Question, VclButtonsType::YesNo, aQuery)); + return (xQueryBox->run() == RET_YES); +} + +bool QueryDelMacro( std::u16string_view rName, weld::Widget* pParent ) +{ + return QueryDel( rName, IDEResId( RID_STR_QUERYDELMACRO ), pParent ); +} + +bool QueryReplaceMacro( std::u16string_view rName, weld::Widget* pParent ) +{ + return QueryDel( rName, IDEResId( RID_STR_QUERYREPLACEMACRO ), pParent ); +} + +bool QueryDelDialog( std::u16string_view rName, weld::Widget* pParent ) +{ + return QueryDel( rName, IDEResId( RID_STR_QUERYDELDIALOG ), pParent ); +} + +bool QueryDelLib( std::u16string_view rName, bool bRef, weld::Widget* pParent ) +{ + return QueryDel( rName, IDEResId( bRef ? RID_STR_QUERYDELLIBREF : RID_STR_QUERYDELLIB ), pParent ); +} + +bool QueryDelModule( std::u16string_view rName, weld::Widget* pParent ) +{ + return QueryDel( rName, IDEResId( RID_STR_QUERYDELMODULE ), pParent ); +} + +bool QueryPassword(weld::Widget* pDialogParent, const Reference< script::XLibraryContainer >& xLibContainer, const OUString& rLibName, OUString& rPassword, bool bRepeat, bool bNewTitle) +{ + bool bOK = false; + sal_uInt16 nRet = 0; + + do + { + // password dialog + SfxPasswordDialog aDlg(pDialogParent); + aDlg.SetMinLen(1); + + // set new title + if ( bNewTitle ) + { + OUString aTitle(IDEResId(RID_STR_ENTERPASSWORD)); + aTitle = aTitle.replaceAll("XX", rLibName); + aDlg.set_title(aTitle); + } + + // execute dialog + nRet = aDlg.run(); + + // verify password + if ( nRet == RET_OK ) + { + if ( xLibContainer.is() && xLibContainer->hasByName( rLibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( rLibName ) && !xPasswd->isLibraryPasswordVerified( rLibName ) ) + { + rPassword = aDlg.GetPassword(); + bOK = xPasswd->verifyLibraryPassword( rLibName, rPassword ); + + if ( !bOK ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pDialogParent, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_WRONGPASSWORD))); + xErrorBox->run(); + } + } + } + } + } + while ( bRepeat && !bOK && nRet == RET_OK ); + + return bOK; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/breakpoint.cxx b/basctl/source/basicide/breakpoint.cxx new file mode 100644 index 0000000000..0d0347ace2 --- /dev/null +++ b/basctl/source/basicide/breakpoint.cxx @@ -0,0 +1,158 @@ +/* -*- 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 "breakpoint.hxx" + +#include <basic/sbmod.hxx> +#include <tools/debug.hxx> + + +namespace basctl +{ + +BreakPointList::BreakPointList() +{} + +BreakPointList::BreakPointList(BreakPointList const & rList) +{ + for (size_t i = 0; i < rList.size(); ++i) + maBreakPoints.push_back( rList.at( i ) ); +} + +BreakPointList::~BreakPointList() +{ +} + +void BreakPointList::reset() +{ + maBreakPoints.clear(); +} + +void BreakPointList::transfer(BreakPointList & rList) +{ + maBreakPoints = std::move(rList.maBreakPoints); +} + +void BreakPointList::InsertSorted(BreakPoint aNewBrk) +{ + auto it = std::find_if(maBreakPoints.begin(), maBreakPoints.end(), + [&aNewBrk](const BreakPoint& rBreakPoint) { return aNewBrk.nLine <= rBreakPoint.nLine; }); + if (it != maBreakPoints.end()) + { + DBG_ASSERT( it->nLine != aNewBrk.nLine, "BreakPoint exists already!" ); + maBreakPoints.insert( it, aNewBrk ); + return; + } + // no insert position found => LIST_APPEND + maBreakPoints.push_back( aNewBrk ); +} + +void BreakPointList::SetBreakPointsInBasic(SbModule* pModule) +{ + pModule->ClearAllBP(); + + for (const BreakPoint& rBrk : maBreakPoints) + { + if ( rBrk.bEnabled ) + pModule->SetBP( rBrk.nLine ); + } +} + +BreakPoint* BreakPointList::FindBreakPoint(sal_uInt16 nLine) +{ + for (BreakPoint& rBrk : maBreakPoints) + { + if ( rBrk.nLine == nLine ) + return &rBrk; + } + return nullptr; +} + +void BreakPointList::AdjustBreakPoints(sal_uInt16 nLine, bool bInserted) +{ + for ( size_t i = 0; i < maBreakPoints.size(); ) + { + BreakPoint& rBrk = maBreakPoints[ i ]; + bool bDelBrk = false; + if ( rBrk.nLine == nLine ) + { + if ( bInserted ) + rBrk.nLine++; + else + bDelBrk = true; + } + else if ( rBrk.nLine > nLine ) + { + if ( bInserted ) + rBrk.nLine++; + else + rBrk.nLine--; + } + + if ( bDelBrk ) + { + maBreakPoints.erase(maBreakPoints.begin() + i); + } + else + { + ++i; + } + } +} + +void BreakPointList::ResetHitCount() +{ + for (BreakPoint& rBrk : maBreakPoints) + { + rBrk.nHitCount = 0; + } +} + +void BreakPointList::remove(const BreakPoint* ptr) +{ + auto i = std::find_if(maBreakPoints.begin(), maBreakPoints.end(), + [&ptr](const BreakPoint& rBreakPoint) { return ptr == &rBreakPoint; }); + if (i != maBreakPoints.end()) + maBreakPoints.erase( i ); + return; +} + +void BreakPointList::remove(size_t idx) +{ + maBreakPoints.erase( maBreakPoints.begin() + idx ); +} + +size_t BreakPointList::size() const +{ + return maBreakPoints.size(); +} + +BreakPoint& BreakPointList::at(size_t i) +{ + return maBreakPoints[ i ]; +} + +const BreakPoint& BreakPointList::at(size_t i) const +{ + return maBreakPoints[ i ]; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/breakpoint.hxx b/basctl/source/basicide/breakpoint.hxx new file mode 100644 index 0000000000..324564aaa7 --- /dev/null +++ b/basctl/source/basicide/breakpoint.hxx @@ -0,0 +1,79 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cstddef> +#include <vector> + +#include <sal/types.h> + +class SbModule; + +namespace basctl +{ +struct BreakPoint +{ + bool bEnabled; + sal_uInt16 nLine; + size_t nStopAfter; + size_t nHitCount; + + explicit BreakPoint(sal_uInt16 nL) + : bEnabled(true) + , nLine(nL) + , nStopAfter(0) + , nHitCount(0) + { + } +}; + +class BreakPointList +{ +private: + BreakPointList& operator=(BreakPointList const&) = delete; + std::vector<BreakPoint> maBreakPoints; + +public: + BreakPointList(); + + BreakPointList(BreakPointList const& rList); + + ~BreakPointList(); + + void reset(); + + void transfer(BreakPointList& rList); + + void InsertSorted(BreakPoint pBrk); + BreakPoint* FindBreakPoint(sal_uInt16 nLine); + void AdjustBreakPoints(sal_uInt16 nLine, bool bInserted); + void SetBreakPointsInBasic(SbModule* pModule); + void ResetHitCount(); + + size_t size() const; + BreakPoint& at(size_t i); + const BreakPoint& at(size_t i) const; + void remove(const BreakPoint* ptr); + void remove(size_t i); +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/brkdlg.cxx b/basctl/source/basicide/brkdlg.cxx new file mode 100644 index 0000000000..f39255371b --- /dev/null +++ b/basctl/source/basicide/brkdlg.cxx @@ -0,0 +1,231 @@ +/* -*- 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 <sal/config.h> + +#include "breakpoint.hxx" +#include "brkdlg.hxx" +#include <basobj.hxx> + +#include <sfx2/dispatch.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/itemset.hxx> + +namespace basctl +{ +namespace +{ +bool lcl_ParseText(OUString const& rText, size_t& rLineNr) +{ + // aText should look like "# n" where n > 0 + // All spaces are ignored, so there can even be spaces within the + // number n. (Maybe it would be better to ignore all whitespace instead + // of just spaces.) + OUString aText(rText.replaceAll(" ", "")); + if (aText.isEmpty()) + return false; + sal_Unicode cFirst = aText[0]; + if (cFirst != '#' && (cFirst < '0' || cFirst > '9')) + return false; + if (cFirst == '#') + aText = aText.copy(1); + sal_Int32 n = aText.toInt32(); + if (n <= 0) + return false; + rLineNr = static_cast<size_t>(n); + return true; +} + +} // namespace + +BreakPointDialog::BreakPointDialog(weld::Window* pParent, BreakPointList& rBrkPntList) + : GenericDialogController(pParent, "modules/BasicIDE/ui/managebreakpoints.ui", + "ManageBreakpointsDialog") + , m_rOriginalBreakPointList(rBrkPntList) + , m_aModifiedBreakPointList(rBrkPntList) + , m_xComboBox(m_xBuilder->weld_entry_tree_view("entriesgrid", "entries", "entrieslist")) + , m_xOKButton(m_xBuilder->weld_button("ok")) + , m_xNewButton(m_xBuilder->weld_button("new")) + , m_xDelButton(m_xBuilder->weld_button("delete")) + , m_xCheckBox(m_xBuilder->weld_check_button("active")) + , m_xNumericField(m_xBuilder->weld_spin_button("pass")) +{ + m_xComboBox->set_size_request(m_xComboBox->get_approximate_digit_width() * 20, -1); + m_xComboBox->set_height_request_by_rows(12); + + m_xComboBox->freeze(); + for (size_t i = 0, n = m_aModifiedBreakPointList.size(); i < n; ++i) + { + BreakPoint& rBrk = m_aModifiedBreakPointList.at(i); + OUString aEntryStr("# " + OUString::number(rBrk.nLine)); + m_xComboBox->append_text(aEntryStr); + } + m_xComboBox->thaw(); + + m_xOKButton->connect_clicked(LINK(this, BreakPointDialog, ButtonHdl)); + m_xNewButton->connect_clicked(LINK(this, BreakPointDialog, ButtonHdl)); + m_xDelButton->connect_clicked(LINK(this, BreakPointDialog, ButtonHdl)); + + m_xCheckBox->connect_toggled(LINK(this, BreakPointDialog, CheckBoxHdl)); + m_xComboBox->connect_changed(LINK(this, BreakPointDialog, EditModifyHdl)); + m_xComboBox->connect_row_activated(LINK(this, BreakPointDialog, TreeModifyHdl)); + m_xComboBox->grab_focus(); + + m_xNumericField->set_range(0, 0x7FFFFFFF); + m_xNumericField->set_increments(1, 10); + m_xNumericField->connect_value_changed(LINK(this, BreakPointDialog, FieldModifyHdl)); + + if (m_xComboBox->get_count()) + m_xComboBox->set_active(0); + + if (m_aModifiedBreakPointList.size()) + UpdateFields(m_aModifiedBreakPointList.at(0)); + + CheckButtons(); +} + +BreakPointDialog::~BreakPointDialog() {} + +void BreakPointDialog::SetCurrentBreakPoint(BreakPoint const& rBrk) +{ + OUString aStr("# " + OUString::number(rBrk.nLine)); + m_xComboBox->set_entry_text(aStr); + UpdateFields(rBrk); +} + +void BreakPointDialog::CheckButtons() +{ + // "New" button is enabled if the combo box edit contains a valid line + // number that is not already present in the combo box list; otherwise + // "OK" and "Delete" buttons are enabled: + size_t nLine; + if (lcl_ParseText(m_xComboBox->get_active_text(), nLine) + && m_aModifiedBreakPointList.FindBreakPoint(nLine) == nullptr) + { + m_xNewButton->set_sensitive(true); + m_xOKButton->set_sensitive(false); + m_xDelButton->set_sensitive(false); + m_xDialog->change_default_widget(m_xDelButton.get(), m_xNewButton.get()); + } + else + { + m_xNewButton->set_sensitive(false); + m_xOKButton->set_sensitive(true); + m_xDelButton->set_sensitive(true); + m_xDialog->change_default_widget(m_xNewButton.get(), m_xDelButton.get()); + } +} + +IMPL_LINK(BreakPointDialog, CheckBoxHdl, weld::Toggleable&, rButton, void) +{ + BreakPoint* pBrk = GetSelectedBreakPoint(); + if (pBrk) + pBrk->bEnabled = rButton.get_active(); +} + +IMPL_LINK(BreakPointDialog, EditModifyHdl, weld::ComboBox&, rBox, void) +{ + CheckButtons(); + + int nEntry = rBox.find_text(rBox.get_active_text()); + if (nEntry == -1) + return; + BreakPoint& rBrk = m_aModifiedBreakPointList.at(nEntry); + UpdateFields(rBrk); +} + +IMPL_LINK(BreakPointDialog, FieldModifyHdl, weld::SpinButton&, rEdit, void) +{ + BreakPoint* pBrk = GetSelectedBreakPoint(); + if (pBrk) + pBrk->nStopAfter = rEdit.get_value(); +} + +IMPL_LINK_NOARG(BreakPointDialog, TreeModifyHdl, weld::TreeView&, bool) +{ + if (m_xDelButton->get_sensitive()) + ButtonHdl(*m_xDelButton); + return true; +} + +IMPL_LINK(BreakPointDialog, ButtonHdl, weld::Button&, rButton, void) +{ + if (&rButton == m_xOKButton.get()) + { + m_rOriginalBreakPointList.transfer(m_aModifiedBreakPointList); + m_xDialog->response(RET_OK); + } + else if (&rButton == m_xNewButton.get()) + { + // keep checkbox in mind! + OUString aText(m_xComboBox->get_active_text()); + size_t nLine; + bool bValid = lcl_ParseText(aText, nLine); + if (bValid) + { + BreakPoint aBrk(nLine); + aBrk.bEnabled = m_xCheckBox->get_active(); + aBrk.nStopAfter = static_cast<size_t>(m_xNumericField->get_value()); + m_aModifiedBreakPointList.InsertSorted(aBrk); + OUString aEntryStr("# " + OUString::number(aBrk.nLine)); + m_xComboBox->append_text(aEntryStr); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute(SID_BASICIDE_BRKPNTSCHANGED); + } + else + { + m_xComboBox->set_active_text(aText); + m_xComboBox->grab_focus(); + } + CheckButtons(); + } + else if (&rButton == m_xDelButton.get()) + { + int nEntry = m_xComboBox->find_text(m_xComboBox->get_active_text()); + if (nEntry != -1) + { + m_aModifiedBreakPointList.remove(nEntry); + m_xComboBox->remove(nEntry); + if (nEntry && nEntry >= m_xComboBox->get_count()) + nEntry--; + m_xComboBox->set_active_text(m_xComboBox->get_text(nEntry)); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute(SID_BASICIDE_BRKPNTSCHANGED); + CheckButtons(); + } + } +} + +void BreakPointDialog::UpdateFields(BreakPoint const& rBrk) +{ + m_xCheckBox->set_active(rBrk.bEnabled); + m_xNumericField->set_value(rBrk.nStopAfter); +} + +BreakPoint* BreakPointDialog::GetSelectedBreakPoint() +{ + int nEntry = m_xComboBox->find_text(m_xComboBox->get_active_text()); + if (nEntry == -1) + return nullptr; + return &m_aModifiedBreakPointList.at(nEntry); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/brkdlg.hxx b/basctl/source/basicide/brkdlg.hxx new file mode 100644 index 0000000000..89406d2c0c --- /dev/null +++ b/basctl/source/basicide/brkdlg.hxx @@ -0,0 +1,57 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/weld.hxx> +#include "breakpoint.hxx" + +namespace basctl +{ +class BreakPointDialog final : public weld::GenericDialogController +{ + BreakPointList& m_rOriginalBreakPointList; + BreakPointList m_aModifiedBreakPointList; + + std::unique_ptr<weld::EntryTreeView> m_xComboBox; + std::unique_ptr<weld::Button> m_xOKButton; + std::unique_ptr<weld::Button> m_xNewButton; + std::unique_ptr<weld::Button> m_xDelButton; + std::unique_ptr<weld::CheckButton> m_xCheckBox; + std::unique_ptr<weld::SpinButton> m_xNumericField; + + void CheckButtons(); + DECL_LINK(CheckBoxHdl, weld::Toggleable&, void); + DECL_LINK(EditModifyHdl, weld::ComboBox&, void); + DECL_LINK(FieldModifyHdl, weld::SpinButton&, void); + DECL_LINK(ButtonHdl, weld::Button&, void); + DECL_LINK(TreeModifyHdl, weld::TreeView&, bool); + void UpdateFields(BreakPoint const& rBrk); + BreakPoint* GetSelectedBreakPoint(); + +public: + BreakPointDialog(weld::Window* pParent, BreakPointList& rBrkList); + virtual ~BreakPointDialog() override; + + void SetCurrentBreakPoint(BreakPoint const& rBrk); +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/doceventnotifier.cxx b/basctl/source/basicide/doceventnotifier.cxx new file mode 100644 index 0000000000..254c719bb5 --- /dev/null +++ b/basctl/source/basicide/doceventnotifier.cxx @@ -0,0 +1,250 @@ +/* -*- 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 <doceventnotifier.hxx> +#include <scriptdocument.hxx> + +#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp> + +#include <vcl/svapp.hxx> + +#include <comphelper/compbase.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <comphelper/processfactory.hxx> + +namespace basctl +{ + + using ::com::sun::star::document::XDocumentEventBroadcaster; + using ::com::sun::star::document::XDocumentEventListener; + using ::com::sun::star::document::DocumentEvent; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::frame::theGlobalEventBroadcaster; + using ::com::sun::star::uno::UNO_QUERY; + + // DocumentEventNotifier::Impl + + typedef ::comphelper::WeakComponentImplHelper< XDocumentEventListener + > DocumentEventNotifier_Impl_Base; + + namespace { + + enum ListenerAction + { + RegisterListener, + RemoveListener + }; + + } + + /** impl class for DocumentEventNotifier + */ + class DocumentEventNotifier::Impl : public DocumentEventNotifier_Impl_Base + { + public: + // noncopyable + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + Impl (DocumentEventListener&, Reference<XModel> const& rxDocument); + virtual ~Impl () override; + + // XDocumentEventListener + virtual void SAL_CALL documentEventOccured( const DocumentEvent& Event ) override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& Event ) override; + + // WeakComponentImplHelper + virtual void disposing(std::unique_lock<std::mutex>&) override; + + private: + /// determines whether the instance is already disposed + bool impl_isDisposed_nothrow(std::unique_lock<std::mutex>& /*rGuard*/) const { return m_pListener == nullptr; } + + /// disposes the instance + void impl_dispose_nothrow(std::unique_lock<std::mutex>& rGuard); + + /// registers or revokes the instance as listener at the global event broadcaster + void impl_listenerAction_nothrow( std::unique_lock<std::mutex>& rGuard, ListenerAction _eAction ); + + private: + DocumentEventListener* m_pListener; + Reference< XModel > m_xModel; + }; + + DocumentEventNotifier::Impl::Impl (DocumentEventListener& rListener, Reference<XModel> const& rxDocument) : + m_pListener(&rListener), + m_xModel(rxDocument) + { + std::unique_lock aGuard(m_aMutex); + osl_atomic_increment( &m_refCount ); + impl_listenerAction_nothrow( aGuard, RegisterListener ); + osl_atomic_decrement( &m_refCount ); + } + + DocumentEventNotifier::Impl::~Impl () + { + std::unique_lock aGuard(m_aMutex); + if ( !impl_isDisposed_nothrow(aGuard) ) + { + acquire(); + dispose(); + } + } + + void SAL_CALL DocumentEventNotifier::Impl::documentEventOccured( const DocumentEvent& _rEvent ) + { + std::unique_lock aGuard( m_aMutex ); + + OSL_PRECOND( !impl_isDisposed_nothrow(aGuard), "DocumentEventNotifier::Impl::notifyEvent: disposed, but still getting events?" ); + if ( impl_isDisposed_nothrow(aGuard) ) + return; + + Reference< XModel > xDocument( _rEvent.Source, UNO_QUERY ); + OSL_ENSURE( xDocument.is(), "DocumentEventNotifier::Impl::notifyEvent: illegal source document!" ); + if ( !xDocument.is() ) + return; + + struct EventEntry + { + const char* pEventName; + void (DocumentEventListener::*listenerMethod)( const ScriptDocument& _rDocument ); + }; + static EventEntry const aEvents[] = { + { "OnNew", &DocumentEventListener::onDocumentCreated }, + { "OnLoad", &DocumentEventListener::onDocumentOpened }, + { "OnSave", &DocumentEventListener::onDocumentSave }, + { "OnSaveDone", &DocumentEventListener::onDocumentSaveDone }, + { "OnSaveAs", &DocumentEventListener::onDocumentSaveAs }, + { "OnSaveAsDone", &DocumentEventListener::onDocumentSaveAsDone }, + { "OnUnload", &DocumentEventListener::onDocumentClosed }, + { "OnTitleChanged", &DocumentEventListener::onDocumentTitleChanged }, + { "OnModeChanged", &DocumentEventListener::onDocumentModeChanged } + }; + + for (EventEntry const & aEvent : aEvents) + { + if ( !_rEvent.EventName.equalsAscii( aEvent.pEventName ) ) + continue; + + // Listener implementations require that we hold the mutex, but to avoid lock ordering issues, + // we need to take the solar mutex before we take our own mutex. + aGuard.unlock(); + + // Listener implements require that we hold the solar mutex. + SolarMutexGuard aSolarGuard; + + // Take the lock again, so we can check our local fields. + aGuard.lock(); + if ( impl_isDisposed_nothrow(aGuard) ) + // somebody took the chance to dispose us -> bail out + return; + DocumentEventListener* pListener = m_pListener; + ScriptDocument aDocument( xDocument ); + // We cannot call the listener while holding our mutex because the listener + // call might trigger an event which call back into us. + aGuard.unlock(); + + (pListener->*aEvent.listenerMethod)( aDocument ); + + break; + } + } + + void SAL_CALL DocumentEventNotifier::Impl::disposing( const css::lang::EventObject& /*Event*/ ) + { + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( m_aMutex ); + + if ( !impl_isDisposed_nothrow(aGuard) ) + impl_dispose_nothrow(aGuard); + } + + void DocumentEventNotifier::Impl::disposing(std::unique_lock<std::mutex>& rGuard) + { + impl_listenerAction_nothrow( rGuard, RemoveListener ); + impl_dispose_nothrow(rGuard); + } + + void DocumentEventNotifier::Impl::impl_dispose_nothrow(std::unique_lock<std::mutex>& /*rGuard*/) + { + m_pListener = nullptr; + m_xModel.clear(); + } + + void DocumentEventNotifier::Impl::impl_listenerAction_nothrow( std::unique_lock<std::mutex>& rGuard, ListenerAction _eAction ) + { + try + { + Reference< XDocumentEventBroadcaster > xBroadcaster; + if ( m_xModel.is() ) + xBroadcaster.set( m_xModel, UNO_QUERY_THROW ); + else + { + Reference< css::uno::XComponentContext > aContext( + comphelper::getProcessComponentContext() ); + xBroadcaster = theGlobalEventBroadcaster::get(aContext); + } + + void ( SAL_CALL XDocumentEventBroadcaster::*listenerAction )( const Reference< XDocumentEventListener >& ) = + ( _eAction == RegisterListener ) ? &XDocumentEventBroadcaster::addDocumentEventListener : &XDocumentEventBroadcaster::removeDocumentEventListener; + + rGuard.unlock(); + (xBroadcaster.get()->*listenerAction)( this ); + rGuard.lock(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + + // DocumentEventNotifier + + DocumentEventNotifier::DocumentEventNotifier (DocumentEventListener& rListener, Reference<XModel> const& rxDocument) : + m_pImpl(new Impl(rListener, rxDocument)) + { } + + DocumentEventNotifier::DocumentEventNotifier (DocumentEventListener& rListener) : + m_pImpl(new Impl(rListener, Reference<XModel>())) + { } + + DocumentEventNotifier::~DocumentEventNotifier() + { + } + + void DocumentEventNotifier::dispose() + { + m_pImpl->dispose(); + } + + // DocumentEventListener + + DocumentEventListener::~DocumentEventListener() + { + } + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/docsignature.cxx b/basctl/source/basicide/docsignature.cxx new file mode 100644 index 0000000000..08d7a1ab9c --- /dev/null +++ b/basctl/source/basicide/docsignature.cxx @@ -0,0 +1,75 @@ +/* -*- 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 <docsignature.hxx> +#include <scriptdocument.hxx> + +#include <sfx2/objsh.hxx> +#include <sfx2/signaturestate.hxx> + +#include <osl/diagnose.h> + + +namespace basctl +{ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::frame::XModel; + + // DocumentSignature + + DocumentSignature::DocumentSignature (ScriptDocument const& rDocument) : + m_pShell(nullptr) + { + if (!rDocument.isDocument()) + return; + + Reference<XModel> xDocument(rDocument.getDocument()); + // find object shell for document + SfxObjectShell* pShell = SfxObjectShell::GetFirst(); + while ( pShell ) + { + if ( pShell->GetModel() == xDocument ) + break; + pShell = SfxObjectShell::GetNext( *pShell ); + } + m_pShell = pShell; + } + + bool DocumentSignature::supportsSignatures() const + { + return ( m_pShell != nullptr ); + } + + void DocumentSignature::signScriptingContent(weld::Window* pDialogParent) const + { + OSL_PRECOND( supportsSignatures(), "DocumentSignature::signScriptingContent: signatures not supported by this document!" ); + if ( m_pShell ) + m_pShell->SignScriptingContent(pDialogParent); + } + + SignatureState DocumentSignature::getScriptingSignatureState() const + { + if ( m_pShell ) + return m_pShell->GetScriptingSignatureState(); + return SignatureState::NOSIGNATURES; + } + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/documentenumeration.cxx b/basctl/source/basicide/documentenumeration.cxx new file mode 100644 index 0000000000..d71e02139e --- /dev/null +++ b/basctl/source/basicide/documentenumeration.cxx @@ -0,0 +1,157 @@ +/* -*- 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 <sal/config.h> + +#include <set> + +#include "documentenumeration.hxx" + +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> + +#include <comphelper/diagnose_ex.hxx> + +namespace basctl::docs { + + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_SET_THROW; + using ::com::sun::star::frame::Desktop; + using ::com::sun::star::frame::XDesktop2; + using ::com::sun::star::container::XEnumeration; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::frame::XFrames; + using ::com::sun::star::frame::XController; + using ::com::sun::star::frame::XModel2; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::frame::XFrame; + + namespace FrameSearchFlag = ::com::sun::star::frame::FrameSearchFlag; + + // DocumentEnumeration + DocumentEnumeration::DocumentEnumeration( Reference< css::uno::XComponentContext > const & _rContext, const IDocumentDescriptorFilter* _pFilter ) + : m_xContext( _rContext ) + , m_pFilter( _pFilter ) + { + } + + DocumentEnumeration::~DocumentEnumeration() + { + } + + namespace + { + void lcl_getDocumentControllers_nothrow( DocumentDescriptor& _io_rDocDesc ) + { + OSL_PRECOND( _io_rDocDesc.xModel.is(), "lcl_getDocumentControllers_nothrow: illegal model!" ); + + _io_rDocDesc.aControllers.clear(); + try + { + Reference< XModel2 > xModel2( _io_rDocDesc.xModel, UNO_QUERY ); + if ( xModel2.is() ) + { + Reference< XEnumeration > xEnum( xModel2->getControllers(), UNO_SET_THROW ); + while ( xEnum->hasMoreElements() ) + { + Reference< XController > xController( xEnum->nextElement(), UNO_QUERY_THROW ); + _io_rDocDesc.aControllers.push_back( xController ); + } + } + else if ( _io_rDocDesc.xModel.is() ) + _io_rDocDesc.aControllers.push_back( _io_rDocDesc.xModel->getCurrentController() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + + void lcl_getDocuments_nothrow( const Sequence< Reference< XFrame > >& _rFrames, Documents& _out_rDocuments, + const IDocumentDescriptorFilter* _pFilter ) + { + // ensure we don't encounter some models multiple times + std::set< Reference< XModel > > aEncounteredModels; + + for ( auto const & rFrame : _rFrames ) + { + try + { + OSL_ENSURE( rFrame.is(), "lcl_getDocuments_nothrow: illegal frame!" ); + if ( !rFrame.is() ) + continue; + Reference< XController > xController( rFrame->getController() ); + if ( !xController.is() ) + continue; + + Reference< XModel > xModel( xController->getModel() ); + if ( !xModel.is() ) + // though it's legal for a controller to not have a model, we're not interested in + // those + continue; + + if ( !aEncounteredModels.insert( xModel ).second ) + // there might be multiple frames for the same model + // handle it only once + continue; + + // create a DocumentDescriptor + DocumentDescriptor aDescriptor; + aDescriptor.xModel = xModel; + lcl_getDocumentControllers_nothrow( aDescriptor ); + + // consult filter, if there is one + if ( _pFilter && !_pFilter->includeDocument( aDescriptor ) ) + continue; + + _out_rDocuments.push_back( aDescriptor ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + } + } + + void DocumentEnumeration::getDocuments( Documents& _out_rDocuments ) const + { + _out_rDocuments.clear(); + + try + { + const Reference< XDesktop2 > xDesktop = Desktop::create( m_xContext ); + const Reference< XFrames > xFrames( xDesktop->getFrames(), UNO_SET_THROW ); + const Sequence< Reference< XFrame > > aFrames( xFrames->queryFrames( FrameSearchFlag::ALL ) ); + + lcl_getDocuments_nothrow( aFrames, _out_rDocuments, m_pFilter ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + +} // namespace basctl::docs + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/documentenumeration.hxx b/basctl/source/basicide/documentenumeration.hxx new file mode 100644 index 0000000000..dfd4d2e817 --- /dev/null +++ b/basctl/source/basicide/documentenumeration.hxx @@ -0,0 +1,90 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/frame/XController.hpp> +#include <memory> +#include <vector> + +namespace com::sun::star::uno { class XComponentContext; } + + +namespace basctl::docs { + + + typedef std::vector< css::uno::Reference< css::frame::XController > > Controllers; + + struct DocumentDescriptor + { + css::uno::Reference< css::frame::XModel > xModel; + Controllers aControllers; + }; + + typedef std::vector< DocumentDescriptor > Documents; + + + /// allows pre-filtering when enumerating document descriptors + class SAL_NO_VTABLE IDocumentDescriptorFilter + { + public: + virtual bool includeDocument( const DocumentDescriptor& _rDocument ) const = 0; + + protected: + ~IDocumentDescriptorFilter() {} + }; + + + /** is a helper class for enumerating documents in OOo + + If you need a list of all open documents in OOo, this is little bit of + a hassle: You need to iterate though all components at the desktop, which + might or might not be documents. + + Additionally, you need to examine the existing documents' frames + for sub frames, which might contain sub documents (e.g. embedded objects + edited out-place). + + DocumentEnumeration relieves you from this hassle. + */ + class DocumentEnumeration + { + public: + DocumentEnumeration( css::uno::Reference< css::uno::XComponentContext > const & _rContext, const IDocumentDescriptorFilter* _pFilter ); + ~DocumentEnumeration(); + + /** retrieves a list of all currently known documents in the application + + @param _out_rDocuments + output parameter taking the collected document information + @ + */ + void getDocuments( + Documents& _out_rDocuments + ) const; + + private: + css::uno::Reference< css::uno::XComponentContext > m_xContext; + const IDocumentDescriptorFilter* m_pFilter; + }; + + +} // namespace basctl::docs + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/iderdll.cxx b/basctl/source/basicide/iderdll.cxx new file mode 100644 index 0000000000..022045050e --- /dev/null +++ b/basctl/source/basicide/iderdll.cxx @@ -0,0 +1,205 @@ +/* -*- 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 <comphelper/unique_disposing_ptr.hxx> +#include <comphelper/processfactory.hxx> + +#include <iderdll.hxx> +#include "iderdll2.hxx" +#include <iderid.hxx> +#include <basidesh.hxx> +#include <basobj.hxx> +#include "basdoc.hxx" +#include "basicmod.hxx" + +#include <basic/sbstar.hxx> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <unotools/resmgr.hxx> +#include <sfx2/app.hxx> +#include <osl/diagnose.h> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace +{ + +class Dll +{ + Shell* m_pShell; + std::unique_ptr<ExtraData> m_xExtraData; + +public: + Dll (); + + Shell* GetShell() const { return m_pShell; } + void SetShell (Shell* pShell) { m_pShell = pShell; } + ExtraData* GetExtraData (); +}; + +// Holds a basctl::Dll and release it on exit, or dispose of the +//default XComponent, whichever comes first +class DllInstance : public comphelper::unique_disposing_solar_mutex_reset_ptr<Dll> +{ +public: + DllInstance() : comphelper::unique_disposing_solar_mutex_reset_ptr<Dll>(Reference<lang::XComponent>( frame::Desktop::create(comphelper::getProcessComponentContext()), UNO_QUERY_THROW), new Dll, true) + { } +}; + +struct theDllInstance : public rtl::Static<DllInstance, theDllInstance> { }; + +} // namespace + +void EnsureIde () +{ + // coverity[side_effect_free : FALSE] - not actually side-effect-free + theDllInstance::get(); +} + +Shell* GetShell () +{ + if (Dll* pDll = theDllInstance::get().get()) + return pDll->GetShell(); + return nullptr; +} + +void ShellCreated (Shell* pShell) +{ + Dll* pDll = theDllInstance::get().get(); + if (pDll && !pDll->GetShell()) + pDll->SetShell(pShell); +} + +void ShellDestroyed (Shell const * pShell) +{ + Dll* pDll = theDllInstance::get().get(); + if (pDll && pDll->GetShell() == pShell) + pDll->SetShell(nullptr); +} + +ExtraData* GetExtraData() +{ + if (Dll* pDll = theDllInstance::get().get()) + return pDll->GetExtraData(); + return nullptr; +} + +OUString IDEResId(TranslateId aId) +{ + return Translate::get(aId, SfxApplication::GetModule(SfxToolsModule::Basic)->GetResLocale()); +} + +namespace +{ + +Dll::Dll () : + m_pShell(nullptr) +{ + SfxObjectFactory& rFactory = DocShell::Factory(); + + auto pModule = std::make_unique<Module>("basctl", &rFactory); + SfxModule* pMod = pModule.get(); + SfxApplication::SetModule(SfxToolsModule::Basic, std::move(pModule)); + + GetExtraData(); // to cause GlobalErrorHdl to be set + + rFactory.SetDocumentServiceName( "com.sun.star.script.BasicIDE" ); + + DocShell::RegisterInterface( pMod ); + Shell::RegisterFactory( SVX_INTERFACE_BASIDE_VIEWSH ); + Shell::RegisterInterface( pMod ); +} + +ExtraData* Dll::GetExtraData () +{ + if (!m_xExtraData) + m_xExtraData.reset(new ExtraData); + return m_xExtraData.get(); +} + +} // namespace + + +// basctl::ExtraData + + +ExtraData::ExtraData () : + bChoosingMacro(false), + bShellInCriticalSection(false) +{ + StarBASIC::SetGlobalBreakHdl(LINK(this, ExtraData, GlobalBasicBreakHdl)); +} + +ExtraData::~ExtraData () +{ + // Resetting ErrorHdl is cleaner indeed but this instance is destroyed + // pretty late, after the last Basic, anyway. + // Due to the call there is AppData created then though and not + // destroyed anymore => MLK's at Purify +// StarBASIC::SetGlobalErrorHdl( Link() ); +// StarBASIC::SetGlobalBreakHdl( Link() ); +// StarBASIC::setGlobalStarScriptListener( XEngineListenerRef() ); +} + +IMPL_STATIC_LINK(ExtraData, GlobalBasicBreakHdl, StarBASIC *, pBasic, BasicDebugFlags) +{ + BasicDebugFlags nRet = BasicDebugFlags::NONE; + if (Shell* pShell = GetShell()) + { + if (BasicManager* pBasMgr = FindBasicManager(pBasic)) + { + // I do get here twice if Step into protected Basic + // => bad, if password query twice, also you don't see + // the lib in the PasswordDlg... + // => start no password query at this point + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + OSL_ENSURE( aDocument.isValid(), "basctl::ExtraData::GlobalBasicBreakHdl: no document for the basic manager!" ); + if ( aDocument.isValid() ) + { + OUString aOULibName( pBasic->GetName() ); + Reference< script::XLibraryContainer > xModLibContainer = aDocument.getLibraryContainer( E_SCRIPTS ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aOULibName ) && !xPasswd->isLibraryPasswordVerified( aOULibName ) ) + { + // a step-out should get me out of the protected area... + nRet = BasicDebugFlags::StepOut; + } + else + { + nRet = pShell->CallBasicBreakHdl( pBasic ); + } + } + } + } + } + + return nRet; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/iderdll2.hxx b/basctl/source/basicide/iderdll2.hxx new file mode 100644 index 0000000000..99534119f8 --- /dev/null +++ b/basctl/source/basicide/iderdll2.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ + +#pragma once + +class StarBASIC; +class SvxSearchItem; + + +#include <bastypes.hxx> +#include <bastype2.hxx> + +namespace basctl +{ + +class ExtraData final +{ + LibInfo aLibInfo; + + EntryDescriptor m_aLastEntryDesc; + + OUString aAddLibPath; + OUString aAddLibFilter; + + bool bChoosingMacro; + bool bShellInCriticalSection; + + DECL_STATIC_LINK( ExtraData, GlobalBasicBreakHdl, StarBASIC *, BasicDebugFlags ); + +public: + ExtraData(); + ~ExtraData(); + + LibInfo& GetLibInfo () { return aLibInfo; } + + EntryDescriptor& GetLastEntryDescriptor () { return m_aLastEntryDesc; } + void SetLastEntryDescriptor (EntryDescriptor const & rDesc) { m_aLastEntryDesc = rDesc; } + + bool& ChoosingMacro() { return bChoosingMacro; } + bool& ShellInCriticalSection() { return bShellInCriticalSection; } + + const OUString& GetAddLibPath() const { return aAddLibPath; } + void SetAddLibPath( const OUString& rPath ) { aAddLibPath = rPath; } + + const OUString& GetAddLibFilter() const { return aAddLibFilter; } + void SetAddLibFilter( const OUString& rFilter ) { aAddLibFilter = rFilter; } +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/layout.cxx b/basctl/source/basicide/layout.cxx new file mode 100644 index 0000000000..e6b6676ed6 --- /dev/null +++ b/basctl/source/basicide/layout.cxx @@ -0,0 +1,430 @@ +/* -*- 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 <layout.hxx> + +#include <bastypes.hxx> +#include <vcl/settings.hxx> +#include <vcl/event.hxx> + +namespace basctl +{ + +namespace +{ +// the thickness of the splitting lines +tools::Long const nSplitThickness = 3; +} // namespace + +// ctor for derived classes +// pParent: the parent window (Shell) +Layout::Layout (vcl::Window* pParent) : + Window(pParent, WB_CLIPCHILDREN), + pChild(nullptr), + bFirstSize(true), + aLeftSide(this, SplittedSide::Side::Left), + aBottomSide(this, SplittedSide::Side::Bottom) +{ + SetBackground(GetSettings().GetStyleSettings().GetWindowColor()); + + vcl::Font aFont = GetFont(); + Size aSz = aFont.GetFontSize(); + aSz.setHeight( aSz.Height() * 1.5 ); + aFont.SetFontSize(aSz); + aFont.SetWeight(WEIGHT_BOLD); + aFont.SetColor(GetSettings().GetStyleSettings().GetWindowTextColor()); + SetFont(aFont); +} + +Layout::~Layout() +{ + disposeOnce(); +} + +void Layout::dispose() +{ + aLeftSide.dispose(); + aBottomSide.dispose(); + pChild.clear(); + Window::dispose(); +} + +// removes a docking window +void Layout::Remove (DockingWindow* pWin) +{ + aLeftSide.Remove(pWin); + aBottomSide.Remove(pWin); +} + +// called by Window when resized +void Layout::Resize() +{ + if (IsVisible()) + ArrangeWindows(); +} + +// ArrangeWindows() -- arranges the child windows +void Layout::ArrangeWindows () +{ + // prevent recursion via OnFirstSize() -> Add() -> ArrangeWindows() + static bool bInArrangeWindows = false; + if (bInArrangeWindows) + return; + bInArrangeWindows = true; + + Size const aSize = GetOutputSizePixel(); + tools::Long const nWidth = aSize.Width(), nHeight = aSize.Height(); + if (nWidth && nHeight) // non-empty size + { + // On first call the derived classes initializes the sizes of the + // docking windows. This cannot be done at construction because + // the Layout has empty size at that point. + if (bFirstSize) + { + bFirstSize = false; + OnFirstSize(nWidth, nHeight); // virtual + } + + // sides + aBottomSide.ArrangeIn(tools::Rectangle(Point(0, 0), aSize)); + aLeftSide.ArrangeIn(tools::Rectangle(Point(0, 0), Size(nWidth, nHeight - aBottomSide.GetSize()))); + // child in the middle + pChild->SetPosSizePixel( + Point(aLeftSide.GetSize(), 0), + Size(nWidth - aLeftSide.GetSize(), nHeight - aBottomSide.GetSize()) + ); + } + + bInArrangeWindows = false; +} + +void Layout::Activating (BaseWindow& rWindow) +{ + // first activation + pChild = &rWindow; + ArrangeWindows(); + Show(); + pChild->Activating(); +} + +void Layout::Deactivating () +{ + if (pChild) + pChild->Deactivating(); + Hide(); + pChild = nullptr; +} + +// virtual +void Layout::DataChanged (DataChangedEvent const& rDCEvt) +{ + Window::DataChanged(rDCEvt); + if (!(rDCEvt.GetType() == DataChangedEventType::SETTINGS && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) + return; + + bool bInvalidate = false; + Color aColor = GetSettings().GetStyleSettings().GetWindowColor(); + const AllSettings* pOldSettings = rDCEvt.GetOldSettings(); + if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetWindowColor()) + { + SetBackground(Wallpaper(aColor)); + bInvalidate = true; + } + aColor = GetSettings().GetStyleSettings().GetWindowTextColor(); + if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetWindowTextColor()) + { + vcl::Font aFont(GetFont()); + aFont.SetColor(aColor); + SetFont(aFont); + bInvalidate = true; + } + if (bInvalidate) + Invalidate(); +} + + +// SplittedSide + + +// ctor +Layout::SplittedSide::SplittedSide (Layout* pParent, Side eSide) : + rLayout(*pParent), + bVertical(eSide == Side::Left), + bLower(eSide == Side::Left), + nSize(0), + aSplitter(VclPtr<Splitter>::Create(pParent, bVertical ? WB_HSCROLL : WB_VSCROLL)) +{ + InitSplitter(*aSplitter); +} + +void Layout::SplittedSide::dispose() +{ + aSplitter.disposeAndClear(); + for (auto & item : vItems) + { + item.pSplit.disposeAndClear(); + item.pWin.clear(); + } +} + +// Add() -- adds a new window to the side (after construction) +void Layout::SplittedSide::Add (DockingWindow* pWin, Size const& rSize) +{ + tools::Long const nSize1 = (bVertical ? rSize.Width() : rSize.Height()) + nSplitThickness; + tools::Long const nSize2 = bVertical ? rSize.Height() : rSize.Width(); + // nSize + if (nSize1 > nSize) + nSize = nSize1; + // window + Item aItem; + aItem.pWin = pWin; + aItem.nStartPos = vItems.empty() ? 0 : vItems.back().nEndPos + nSplitThickness; + aItem.nEndPos = aItem.nStartPos + nSize2; + // splitter + if (!vItems.empty()) + { + aItem.pSplit = VclPtr<Splitter>::Create(&rLayout, bVertical ? WB_VSCROLL : WB_HSCROLL); + aItem.pSplit->SetSplitPosPixel(aItem.nStartPos - nSplitThickness); + InitSplitter(*aItem.pSplit); + } + vItems.push_back(aItem); + // refresh + rLayout.ArrangeWindows(); +} + +// Remove() -- removes a window from the side (if contains) +void Layout::SplittedSide::Remove (DockingWindow* pWin) +{ + // contains? + std::vector<Item>::size_type iWin; + for (iWin = 0; iWin != vItems.size(); ++iWin) + if (vItems[iWin].pWin == pWin) + break; + if (iWin == vItems.size()) + return; + // remove + vItems[iWin].pSplit.disposeAndClear(); + vItems[iWin].pWin.clear(); + vItems.erase(vItems.begin() + iWin); + // if that was the first one, remove the first splitter line + if (iWin == 0 && !vItems.empty()) + vItems.front().pSplit.reset(); +} + +// creating a Point or a Size object +// The coordinate order depends on bVertical (reversed if true). +inline Size Layout::SplittedSide::MakeSize (tools::Long A, tools::Long B) const +{ + return bVertical ? Size(B, A) : Size(A, B); +} +inline Point Layout::SplittedSide::MakePoint (tools::Long A, tools::Long B) const +{ + return bVertical ? Point(B, A) : Point(A, B); +} + +// IsDocking() -- is this window currently docking in the strip? +bool Layout::SplittedSide::IsDocking (DockingWindow const& rWin) +{ + return rWin.IsVisible() && !rWin.IsFloatingMode(); +} + +// IsEmpty() -- are there no windows docked in this strip? +bool Layout::SplittedSide::IsEmpty () const +{ + for (auto const & i: vItems) + if (IsDocking(*i.pWin)) + return false; + return true; +} + +// GetSize() -- returns the width or height of the strip (depending on the direction) +tools::Long Layout::SplittedSide::GetSize () const +{ + return IsEmpty() ? 0 : nSize; +} + +// Arrange() -- arranges the docking windows +// rRect: the available space +void Layout::SplittedSide::ArrangeIn (tools::Rectangle const& rRect) +{ + // saving the rectangle + aRect = rRect; + + // the length of the side + tools::Long const nLength = bVertical ? aRect.GetSize().Height() : aRect.GetSize().Width(); + tools::Long const nOtherSize = bVertical ? aRect.GetSize().Width() : aRect.GetSize().Height(); + // bVertical ? horizontal position : vertical position + tools::Long const nPos1 = (bVertical ? aRect.Left() : aRect.Top()) + + (bLower ? 0 : nOtherSize - (nSize - nSplitThickness)); + // bVertical ? vertical position : horizontal position + tools::Long const nPos2 = bVertical ? aRect.Top() : aRect.Left(); + + // main line + bool const bEmpty = IsEmpty(); + // shown if any of the windows is docked + if (!bEmpty) + { + aSplitter->Show(); + // split position + aSplitter->SetSplitPosPixel((bLower ? nSize : nPos1) - nSplitThickness); + // the actual position and size + aSplitter->SetPosSizePixel( + MakePoint(nPos2, aSplitter->GetSplitPosPixel()), + MakeSize(nLength, nSplitThickness) + ); + // dragging rectangle + aSplitter->SetDragRectPixel(aRect); + } + else + aSplitter->Hide(); + + // positioning separator lines and windows + bool bPrevDocking = false; // is the previous window docked? + tools::Long nStartPos = 0; // window position in the strip + std::vector<Item>::size_type iLastWin = vItems.size(); // index of last docking window in the strip + + for (std::vector<Item>::size_type i = 0; i != vItems.size(); ++i) + { + // window + DockingWindow& rWin = *vItems[i].pWin; + bool const bDocking = IsDocking(rWin); + if (bDocking) + iLastWin = i; + // sizing window + rWin.ResizeIfDocking( + MakePoint(nPos2 + nStartPos, nPos1), + MakeSize(vItems[i].nEndPos - nStartPos, nSize - nSplitThickness) + ); + // splitting line before the window + if (i > 0) + { + Splitter& rSplit = *vItems[i].pSplit; + // If neither of two adjacent windows are docked, + // the splitting line is hidden. + // If this window is docking but the previous isn't, + // then the splitting line is also hidden, because this window + // will occupy the space of the previous. + if (bPrevDocking) + { + rSplit.Show(); + // the actual position and size of the line + rSplit.SetPosSizePixel( + MakePoint(nPos2 + nStartPos - nSplitThickness, nPos1), + MakeSize(nSplitThickness, nSize - nSplitThickness) + ); + // the dragging rectangle + rSplit.SetDragRectPixel(tools::Rectangle( + MakePoint(nPos2, nPos1), + MakeSize(nLength, nSize - nSplitThickness) + )); + } + else + rSplit.Hide(); + } + // next + bPrevDocking = bDocking; + if (bDocking) + nStartPos = vItems[i].nEndPos + nSplitThickness; + // We only set nStartPos if this window is docking, because otherwise + // the next window will occupy also the space of this window. + } + + // filling the remaining space with the last docking window + if (bEmpty || vItems[iLastWin].nEndPos == nLength) + return; + + Item& rItem = vItems[iLastWin]; + Size aSize = rItem.pWin->GetDockingSize(); + if (bVertical) + aSize.AdjustHeight( nLength - rItem.nEndPos ); + else + aSize.AdjustWidth( nLength - rItem.nEndPos ); + rItem.pWin->ResizeIfDocking(aSize); + // and hiding the split line after the window + if (iLastWin < vItems.size() - 1) + vItems[iLastWin + 1].pSplit->Hide(); +} + +IMPL_LINK(Layout::SplittedSide, SplitHdl, Splitter*, pSplitter, void) +{ + // checking margins + CheckMarginsFor(pSplitter); + // changing stored sizes + if (pSplitter == aSplitter.get()) + { + // nSize + if (bLower) + nSize = pSplitter->GetSplitPosPixel(); + else + nSize = (bVertical ? aRect.Right() : aRect.Bottom()) + 1 - pSplitter->GetSplitPosPixel(); + } + else + { + // Item::nStartPos, Item::nLength + for (size_t i = 1; i < vItems.size(); ++i) + { + if (vItems[i].pSplit.get() == pSplitter) + { + // before the line + vItems[i - 1].nEndPos = pSplitter->GetSplitPosPixel(); + // after the line + vItems[i].nStartPos = pSplitter->GetSplitPosPixel() + nSplitThickness; + } + } + } + // arranging windows + rLayout.ArrangeWindows(); +} + +void Layout::SplittedSide::CheckMarginsFor (Splitter* pSplitter) +{ + // The splitter line cannot be closer to the edges than nMargin pixels. + static tools::Long const nMargin = 16; + // Checking margins: + tools::Long const nLength = pSplitter->IsHorizontal() ? + aRect.GetWidth() : aRect.GetHeight(); + if (!nLength) + return; + + // bounds + tools::Long const nLower = (pSplitter->IsHorizontal() ? aRect.Left() : aRect.Top()) + nMargin; + tools::Long const nUpper = nLower + nLength - 2*nMargin; + // split position + tools::Long const nPos = pSplitter->GetSplitPosPixel(); + // checking bounds + if (nPos < nLower) + pSplitter->SetSplitPosPixel(nLower); + if (nPos > nUpper) + pSplitter->SetSplitPosPixel(nUpper); +} + +void Layout::SplittedSide::InitSplitter (Splitter& rSplitter) +{ + // link + rSplitter.SetSplitHdl(LINK(this, SplittedSide, SplitHdl)); + // color + Color aColor = rLayout.GetSettings().GetStyleSettings().GetShadowColor(); + rSplitter.GetOutDev()->SetLineColor(aColor); + rSplitter.GetOutDev()->SetFillColor(aColor); +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/linenumberwindow.cxx b/basctl/source/basicide/linenumberwindow.cxx new file mode 100644 index 0000000000..18420199e2 --- /dev/null +++ b/basctl/source/basicide/linenumberwindow.cxx @@ -0,0 +1,135 @@ +/* -*- 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/. + */ + +#include "baside2.hxx" + +#include <vcl/event.hxx> +#include <vcl/textview.hxx> +#include <vcl/xtextedt.hxx> +#include <vcl/settings.hxx> + +namespace basctl +{ +LineNumberWindow::LineNumberWindow(vcl::Window* pParent, ModulWindow* pModulWindow) + : Window(pParent, WB_BORDER) + , m_pModulWindow(pModulWindow) + , m_nCurYOffset(0) +{ + const Wallpaper aBackground(GetSettings().GetStyleSettings().GetWindowColor()); + SetBackground(aBackground); + GetWindow(GetWindowType::Border)->SetBackground(aBackground); + m_FontColor = GetSettings().GetStyleSettings().GetWindowTextColor(); + m_nBaseWidth = GetTextWidth("8"); + m_nWidth = m_nBaseWidth * 3 + m_nBaseWidth / 2; +} + +LineNumberWindow::~LineNumberWindow() { disposeOnce(); } + +void LineNumberWindow::dispose() +{ + m_pModulWindow.clear(); + Window::dispose(); +} + +void LineNumberWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) +{ + if (SyncYOffset()) + return; + + ExtTextEngine* txtEngine = m_pModulWindow->GetEditEngine(); + if (!txtEngine) + return; + + TextView* txtView = m_pModulWindow->GetEditView(); + if (!txtView) + return; + + int windowHeight = rRenderContext.GetOutputSize().Height(); + int nLineHeight = rRenderContext.GetTextHeight(); + if (!nLineHeight) + { + return; + } + + int startY = txtView->GetStartDocPos().Y(); + const sal_uInt32 nStartLine = startY / nLineHeight + 1; + sal_uInt32 nEndLine = (startY + windowHeight) / nLineHeight + 1; + + if (txtEngine->GetParagraphCount() + 1 < nEndLine) + nEndLine = txtEngine->GetParagraphCount() + 1; + + // FIXME: it would be best if we could get notified of a font change + // rather than doing that re-calculation at each Paint event + m_nBaseWidth = GetTextWidth("8"); + + // reserve enough for 3 digit minimum, with a bit to spare for comfort + m_nWidth = m_nBaseWidth * 3 + m_nBaseWidth / 2; + auto nMaxLineNumber = std::max(nEndLine, txtEngine->GetParagraphCount() + 1); + sal_uInt32 i = (nMaxLineNumber + 1) / 1000; + while (i) + { + i /= 10; + m_nWidth += m_nBaseWidth; + } + + sal_Int64 y = (nStartLine - 1) * static_cast<sal_Int64>(nLineHeight); + rRenderContext.SetTextColor(m_FontColor); + for (sal_uInt32 n = nStartLine; n <= nEndLine; ++n, y += nLineHeight) + { + const OUString aLineNumber = OUString::number(n); + // tdf#153798 - align line numbers to the right + rRenderContext.DrawText( + Point(m_nWidth - GetTextWidth(aLineNumber) - m_nBaseWidth / 2, y - m_nCurYOffset), + aLineNumber); + } + + // Resize the parent after calculating the new width and height values + GetParent()->Resize(); +} + +void LineNumberWindow::DataChanged(DataChangedEvent const& rDCEvt) +{ + Window::DataChanged(rDCEvt); + if (rDCEvt.GetType() == DataChangedEventType::SETTINGS + && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) + { + Color aColor(GetSettings().GetStyleSettings().GetFieldColor()); + const AllSettings* pOldSettings = rDCEvt.GetOldSettings(); + if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFieldColor()) + { + SetBackground(Wallpaper(aColor)); + Invalidate(); + } + } +} + +void LineNumberWindow::DoScroll(tools::Long nVertScroll) +{ + m_nCurYOffset -= nVertScroll; + Window::Scroll(0, nVertScroll); +} + +bool LineNumberWindow::SyncYOffset() +{ + TextView* pView = m_pModulWindow->GetEditView(); + if (!pView) + return false; + + tools::Long nViewYOffset = pView->GetStartDocPos().Y(); + if (m_nCurYOffset == nViewYOffset) + return false; + + m_nCurYOffset = nViewYOffset; + Invalidate(); + return true; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/linenumberwindow.hxx b/basctl/source/basicide/linenumberwindow.hxx new file mode 100644 index 0000000000..a2e457f711 --- /dev/null +++ b/basctl/source/basicide/linenumberwindow.hxx @@ -0,0 +1,46 @@ +/* -*- 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/. + */ + +#pragma once + +#include <vcl/window.hxx> + +namespace basctl +{ +class ModulWindow; + +class LineNumberWindow : public vcl::Window +{ +private: + VclPtr<ModulWindow> m_pModulWindow; + int m_nWidth; + tools::Long m_nCurYOffset; + int m_nBaseWidth; + Color m_FontColor; + virtual void DataChanged(DataChangedEvent const& rDCEvt) override; + +protected: + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + +public: + LineNumberWindow(vcl::Window* pParent, ModulWindow* pModulWin); + virtual ~LineNumberWindow() override; + virtual void dispose() override; + + void DoScroll(tools::Long nVertScroll); + + bool SyncYOffset(); + tools::Long& GetCurYOffset() { return m_nCurYOffset; } + + int GetWidth() const { return m_nWidth; } +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/localizationmgr.cxx b/basctl/source/basicide/localizationmgr.cxx new file mode 100644 index 0000000000..68d2f63622 --- /dev/null +++ b/basctl/source/basicide/localizationmgr.cxx @@ -0,0 +1,1100 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <localizationmgr.hxx> + +#include <basidesh.hxx> +#include <baside3.hxx> +#include <basobj.hxx> +#include <iderdll.hxx> +#include <dlged.hxx> +#include <managelang.hxx> + +#include <com/sun/star/frame/XLayoutManager.hpp> +#include <com/sun/star/resource/MissingResourceException.hpp> +#include <com/sun/star/resource/XStringResourceSupplier.hpp> +#include <sfx2/bindings.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <tools/debug.hxx> +#include <utility> +#include <osl/diagnose.h> +#include <o3tl/string_view.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::resource; + +namespace +{ + +constexpr OUString aDot(u"."_ustr); +constexpr OUString aEsc(u"&"_ustr); +constexpr OUString aSemi(u";"_ustr); + +} // namespace + +LocalizationMgr::LocalizationMgr( + Shell* pShell, + ScriptDocument aDocument, + OUString aLibName, + Reference<XStringResourceManager> const& xStringResourceManager +) : + m_xStringResourceManager(xStringResourceManager), + m_pShell(pShell), + m_aDocument(std::move(aDocument)), + m_aLibName(std::move(aLibName)) +{ } + +bool LocalizationMgr::isLibraryLocalized () +{ + if (m_xStringResourceManager.is()) + return m_xStringResourceManager->getLocales().hasElements(); + return false; +} + +void LocalizationMgr::handleTranslationbar () +{ + static constexpr OUString aToolBarResName = u"private:resource/toolbar/translationbar"_ustr; + + Reference< beans::XPropertySet > xFrameProps + ( m_pShell->GetViewFrame().GetFrame().GetFrameInterface(), uno::UNO_QUERY ); + if ( !xFrameProps.is() ) + return; + + Reference< css::frame::XLayoutManager > xLayoutManager; + uno::Any a = xFrameProps->getPropertyValue( "LayoutManager" ); + a >>= xLayoutManager; + if ( xLayoutManager.is() ) + { + if ( !isLibraryLocalized() ) + { + xLayoutManager->destroyElement( aToolBarResName ); + } + else + { + xLayoutManager->createElement( aToolBarResName ); + xLayoutManager->requestElement( aToolBarResName ); + } + } +} + + +// TODO: -> export from toolkit + + +static bool isLanguageDependentProperty( std::u16string_view aName ) +{ + static struct Prop + { + const char* sName; + sal_Int32 nNameLength; + } + const vProp[] = + { + { "Text", 4 }, + { "Label", 5 }, + { "Title", 5 }, + { "HelpText", 8 }, + { "CurrencySymbol", 14 }, + { "StringItemList", 14 }, + { nullptr, 0 } + }; + + for (Prop const* pProp = vProp; pProp->sName; ++pProp) + if (o3tl::equalsAscii(aName, std::string_view(pProp->sName, pProp->nNameLength))) + return true; + return false; +} + + +void LocalizationMgr::implEnableDisableResourceForAllLibraryDialogs( HandleResourceMode eMode ) +{ + Sequence< OUString > aDlgNames = m_aDocument.getObjectNames( E_DIALOGS, m_aLibName ); + sal_Int32 nDlgCount = aDlgNames.getLength(); + const OUString* pDlgNames = aDlgNames.getConstArray(); + + Reference< XStringResourceResolver > xDummyStringResolver; + for( sal_Int32 i = 0 ; i < nDlgCount ; i++ ) + { + OUString aDlgName = pDlgNames[ i ]; + if (VclPtr<DialogWindow> pWin = m_pShell->FindDlgWin(m_aDocument, m_aLibName, aDlgName)) + { + Reference< container::XNameContainer > xDialog = pWin->GetDialog(); + if( xDialog.is() ) + { + // Handle dialog itself as control + Any aDialogCtrl; + aDialogCtrl <<= xDialog; + implHandleControlResourceProperties( aDialogCtrl, aDlgName, + std::u16string_view(), m_xStringResourceManager, xDummyStringResolver, eMode ); + + // Handle all controls + Sequence< OUString > aNames = xDialog->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nCtrls = aNames.getLength(); + for( sal_Int32 j = 0 ; j < nCtrls ; ++j ) + { + OUString aCtrlName( pNames[j] ); + Any aCtrl = xDialog->getByName( aCtrlName ); + implHandleControlResourceProperties( aCtrl, aDlgName, + aCtrlName, m_xStringResourceManager, xDummyStringResolver, eMode ); + } + } + } + } +} + + +static OUString implCreatePureResourceId + ( std::u16string_view aDialogName, std::u16string_view aCtrlName, + std::u16string_view aPropName, + const Reference< XStringResourceManager >& xStringResourceManager ) +{ + sal_Int32 nUniqueId = xStringResourceManager->getUniqueNumericId(); + OUString aPureIdStr = OUString::number( nUniqueId ) + + aDot + + aDialogName + + aDot; + if( !aCtrlName.empty() ) + { + aPureIdStr += aCtrlName + aDot; + } + aPureIdStr += aPropName; + return aPureIdStr; +} + +// Works on xStringResourceManager's current language for SET_IDS/RESET_IDS, +// anyway only one language should exist when calling this method then, +// either the first one for mode SET_IDS or the last one for mode RESET_IDS +sal_Int32 LocalizationMgr::implHandleControlResourceProperties + (const Any& rControlAny, std::u16string_view aDialogName, std::u16string_view aCtrlName, + const Reference< XStringResourceManager >& xStringResourceManager, + const Reference< XStringResourceResolver >& xSourceStringResolver, HandleResourceMode eMode ) +{ + sal_Int32 nChangedCount = 0; + + Reference< XPropertySet > xPropertySet; + rControlAny >>= xPropertySet; + if( xPropertySet.is() && xStringResourceManager.is()) + { + Sequence< Locale > aLocaleSeq = xStringResourceManager->getLocales(); + sal_Int32 nLocaleCount = aLocaleSeq.getLength(); + if( nLocaleCount == 0 ) + return 0; + + Reference< XPropertySetInfo > xPropertySetInfo = xPropertySet->getPropertySetInfo(); + if( xPropertySetInfo.is() ) + { + // get sequence of control properties + Sequence< Property > aPropSeq = xPropertySetInfo->getProperties(); + const Property* pProps = aPropSeq.getConstArray(); + sal_Int32 nCtrlProps = aPropSeq.getLength(); + + // create a map of tab indices and control names, sorted by tab index + for( sal_Int32 j = 0 ; j < nCtrlProps ; ++j ) + { + const Property& rProp = pProps[j]; + OUString aPropName = rProp.Name; + TypeClass eType = rProp.Type.getTypeClass(); + bool bLanguageDependentProperty = + (eType == TypeClass_STRING || eType == TypeClass_SEQUENCE) + && isLanguageDependentProperty( aPropName ); + if( !bLanguageDependentProperty ) + continue; + + if( eType == TypeClass_STRING ) + { + Any aPropAny = xPropertySet->getPropertyValue( aPropName ); + OUString aPropStr; + aPropAny >>= aPropStr; + + // Replace string by id, add id+string to StringResource + if( eMode == SET_IDS ) + { + bool bEscAlreadyExisting = aPropStr.startsWith("&"); + if( bEscAlreadyExisting ) + continue; + + OUString aPureIdStr = implCreatePureResourceId + ( aDialogName, aCtrlName, aPropName, xStringResourceManager ); + + // Set Id for all locales + const Locale* pLocales = aLocaleSeq.getConstArray(); + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + xStringResourceManager->setStringForLocale( aPureIdStr, aPropStr, rLocale ); + } + + OUString aPropIdStr = aEsc + aPureIdStr; + // TODO?: Change here and in toolkit + (void)aSemi; + xPropertySet->setPropertyValue( aPropName, Any(aPropIdStr) ); + } + // Replace id by string from StringResource + else if( eMode == RESET_IDS ) + { + if( aPropStr.getLength() > 1 ) + { + OUString aPureIdStr = aPropStr.copy( 1 ); + OUString aNewPropStr = aPropStr; + try + { + aNewPropStr = xStringResourceManager->resolveString( aPureIdStr ); + } + catch(const MissingResourceException&) + { + } + xPropertySet->setPropertyValue( aPropName, Any(aNewPropStr) ); + } + } + // Remove Id for all locales + else if( eMode == REMOVE_IDS_FROM_RESOURCE ) + { + if( aPropStr.getLength() > 1 ) + { + OUString aPureIdStr = aPropStr.copy( 1 ); + + const Locale* pLocales = aLocaleSeq.getConstArray(); + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + try + { + xStringResourceManager->removeIdForLocale( aPureIdStr, rLocale ); + } + catch(const MissingResourceException&) + { + } + } + } + } + // Rename resource id + else if( eMode == RENAME_DIALOG_IDS || eMode == RENAME_CONTROL_IDS ) + { + OUString aPureSourceIdStr = aPropStr.copy( 1 ); + + OUString aPureIdStr = implCreatePureResourceId + ( aDialogName, aCtrlName, aPropName, xStringResourceManager ); + + // Set new Id and remove old one for all locales + const Locale* pLocales = aLocaleSeq.getConstArray(); + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + try + { + OUString aResStr = xStringResourceManager->resolveStringForLocale + ( aPureSourceIdStr, rLocale ); + xStringResourceManager->removeIdForLocale( aPureSourceIdStr, rLocale ); + xStringResourceManager->setStringForLocale( aPureIdStr, aResStr, rLocale ); + } + catch(const MissingResourceException&) + {} + } + + OUString aPropIdStr = aEsc + aPureIdStr; + // TODO?: Change here and in toolkit + (void)aSemi; + xPropertySet->setPropertyValue( aPropName, Any(aPropIdStr) ); + } + // Replace string by string from source StringResourceResolver + else if( eMode == MOVE_RESOURCES && xSourceStringResolver.is() ) + { + OUString aPureSourceIdStr = aPropStr.copy( 1 ); + + OUString aPureIdStr = implCreatePureResourceId + ( aDialogName, aCtrlName, aPropName, xStringResourceManager ); + + const Locale& rDefaultLocale = xSourceStringResolver->getDefaultLocale(); + + // Set Id for all locales + const Locale* pLocales = aLocaleSeq.getConstArray(); + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + OUString aResStr; + try + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rLocale ); + } + catch(const MissingResourceException&) + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rDefaultLocale ); + } + xStringResourceManager->setStringForLocale( aPureIdStr, aResStr, rLocale ); + } + + OUString aPropIdStr = aEsc + aPureIdStr; + // TODO?: Change here and in toolkit + (void)aSemi; + xPropertySet->setPropertyValue( aPropName, Any(aPropIdStr) ); + } + // Copy string from source to target resource + else if( eMode == COPY_RESOURCES && xSourceStringResolver.is() ) + { + OUString aPureSourceIdStr = aPropStr.copy( 1 ); + + const Locale& rDefaultLocale = xSourceStringResolver->getDefaultLocale(); + + // Copy Id for all locales + const Locale* pLocales = aLocaleSeq.getConstArray(); + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + OUString aResStr; + try + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rLocale ); + } + catch(const MissingResourceException&) + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rDefaultLocale ); + } + xStringResourceManager->setStringForLocale( aPureSourceIdStr, aResStr, rLocale ); + } + } + nChangedCount++; + } + + // Listbox / Combobox + else if( eType == TypeClass_SEQUENCE ) + { + Any aPropAny = xPropertySet->getPropertyValue( aPropName ); + Sequence< OUString > aPropStrings; + aPropAny >>= aPropStrings; + + const OUString* pPropStrings = aPropStrings.getConstArray(); + sal_Int32 nPropStringCount = aPropStrings.getLength(); + if( nPropStringCount == 0 ) + continue; + + // Replace string by id, add id+string to StringResource + if( eMode == SET_IDS ) + { + Sequence< OUString > aIdStrings; + aIdStrings.realloc( nPropStringCount ); + OUString* pIdStrings = aIdStrings.getArray(); + + OUString aIdStrBase = aDot + + aCtrlName + + aDot + + aPropName; + + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 i; + for ( i = 0; i < nPropStringCount; ++i ) + { + OUString aPropStr = pPropStrings[i]; + bool bEscAlreadyExisting = aPropStr.startsWith("&"); + if( bEscAlreadyExisting ) + { + pIdStrings[i] = aPropStr; + continue; + } + + sal_Int32 nUniqueId = xStringResourceManager->getUniqueNumericId(); + OUString aPureIdStr = OUString::number( nUniqueId ) + + aIdStrBase; + + // Set Id for all locales + for( sal_Int32 iLocale = 0 ; iLocale < nLocaleCount ; iLocale++ ) + { + const Locale& rLocale = pLocales[ iLocale ]; + xStringResourceManager->setStringForLocale( aPureIdStr, aPropStr, rLocale ); + } + + OUString aPropIdStr = aEsc + aPureIdStr; + pIdStrings[i] = aPropIdStr; + } + xPropertySet->setPropertyValue( aPropName, Any(aIdStrings) ); + } + // Replace id by string from StringResource + else if( eMode == RESET_IDS ) + { + Sequence< OUString > aNewPropStrings; + aNewPropStrings.realloc( nPropStringCount ); + OUString* pNewPropStrings = aNewPropStrings.getArray(); + + sal_Int32 i; + for ( i = 0; i < nPropStringCount; ++i ) + { + OUString aIdStr = pPropStrings[i]; + OUString aNewPropStr = aIdStr; + if( aIdStr.getLength() > 1 ) + { + OUString aPureIdStr = aIdStr.copy( 1 ); + try + { + aNewPropStr = xStringResourceManager->resolveString( aPureIdStr ); + } + catch(const MissingResourceException&) + { + } + } + pNewPropStrings[i] = aNewPropStr; + } + xPropertySet->setPropertyValue( aPropName, Any(aNewPropStrings) ); + } + // Remove Id for all locales + else if( eMode == REMOVE_IDS_FROM_RESOURCE ) + { + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 i; + for ( i = 0; i < nPropStringCount; ++i ) + { + OUString aIdStr = pPropStrings[i]; + if( aIdStr.getLength() > 1 ) + { + OUString aPureIdStr = aIdStr.copy( 1 ); + + for( sal_Int32 iLocale = 0 ; iLocale < nLocaleCount ; iLocale++ ) + { + const Locale& rLocale = pLocales[iLocale]; + try + { + xStringResourceManager->removeIdForLocale( aPureIdStr, rLocale ); + } + catch(const MissingResourceException&) + { + } + } + } + } + } + // Rename resource id + else if( eMode == RENAME_CONTROL_IDS ) + { + Sequence< OUString > aIdStrings; + aIdStrings.realloc( nPropStringCount ); + OUString* pIdStrings = aIdStrings.getArray(); + + OUString aIdStrBase = aDot + + aCtrlName + + aDot + + aPropName; + + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 i; + for ( i = 0; i < nPropStringCount; ++i ) + { + OUString aSourceIdStr = pPropStrings[i]; + OUString aPureSourceIdStr = aSourceIdStr.copy( 1 ); + + sal_Int32 nUniqueId = xStringResourceManager->getUniqueNumericId(); + OUString aPureIdStr = OUString::number( nUniqueId ) + + aIdStrBase; + + // Set Id for all locales + for( sal_Int32 iLocale = 0 ; iLocale < nLocaleCount ; iLocale++ ) + { + const Locale& rLocale = pLocales[ iLocale ]; + + try + { + OUString aResStr = xStringResourceManager->resolveStringForLocale + ( aPureSourceIdStr, rLocale ); + xStringResourceManager->removeIdForLocale( aPureSourceIdStr, rLocale ); + xStringResourceManager->setStringForLocale( aPureIdStr, aResStr, rLocale ); + } + catch(const MissingResourceException&) + {} + } + + OUString aPropIdStr = aEsc + aPureIdStr; + pIdStrings[i] = aPropIdStr; + } + xPropertySet->setPropertyValue( aPropName, Any(aIdStrings) ); + } + // Replace string by string from source StringResourceResolver + else if( eMode == MOVE_RESOURCES && xSourceStringResolver.is() ) + { + Sequence< OUString > aIdStrings; + aIdStrings.realloc( nPropStringCount ); + OUString* pIdStrings = aIdStrings.getArray(); + + OUString aIdStrBase = aDot + + aCtrlName + + aDot + + aPropName; + + const Locale& rDefaultLocale = xSourceStringResolver->getDefaultLocale(); + + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 i; + for ( i = 0; i < nPropStringCount; ++i ) + { + OUString aSourceIdStr = pPropStrings[i]; + OUString aPureSourceIdStr = aSourceIdStr.copy( 1 ); + + sal_Int32 nUniqueId = xStringResourceManager->getUniqueNumericId(); + OUString aPureIdStr = OUString::number( nUniqueId ) + + aIdStrBase; + + // Set Id for all locales + for( sal_Int32 iLocale = 0 ; iLocale < nLocaleCount ; iLocale++ ) + { + const Locale& rLocale = pLocales[ iLocale ]; + + OUString aResStr; + try + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rLocale ); + } + catch(const MissingResourceException&) + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rDefaultLocale ); + } + xStringResourceManager->setStringForLocale( aPureIdStr, aResStr, rLocale ); + } + + OUString aPropIdStr = aEsc + aPureIdStr; + pIdStrings[i] = aPropIdStr; + } + xPropertySet->setPropertyValue( aPropName, Any(aIdStrings) ); + } + // Copy string from source to target resource + else if( eMode == COPY_RESOURCES && xSourceStringResolver.is() ) + { + const Locale& rDefaultLocale = xSourceStringResolver->getDefaultLocale(); + + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 i; + for ( i = 0; i < nPropStringCount; ++i ) + { + OUString aSourceIdStr = pPropStrings[i]; + OUString aPureSourceIdStr = aSourceIdStr.copy( 1 ); + + // Set Id for all locales + for( sal_Int32 iLocale = 0 ; iLocale < nLocaleCount ; iLocale++ ) + { + const Locale& rLocale = pLocales[ iLocale ]; + + OUString aResStr; + try + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rLocale ); + } + catch(const MissingResourceException&) + { + aResStr = xSourceStringResolver->resolveStringForLocale + ( aPureSourceIdStr, rDefaultLocale ); + } + xStringResourceManager->setStringForLocale( aPureSourceIdStr, aResStr, rLocale ); + } + } + } + nChangedCount++; + } + } + } + } + return nChangedCount; +} + + +void LocalizationMgr::handleAddLocales( const Sequence< Locale >& aLocaleSeq ) +{ + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 nLocaleCount = aLocaleSeq.getLength(); + + if( isLibraryLocalized() ) + { + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + m_xStringResourceManager->newLocale( rLocale ); + } + } + else + { + DBG_ASSERT( nLocaleCount==1, "LocalizationMgr::handleAddLocales(): Only one first locale allowed" ); + + const Locale& rLocale = pLocales[ 0 ]; + m_xStringResourceManager->newLocale( rLocale ); + enableResourceForAllLibraryDialogs(); + } + + MarkDocumentModified( m_aDocument ); + + // update locale toolbar + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_BASICIDE_CURRENT_LANG ); + + handleTranslationbar(); +} + + +void LocalizationMgr::handleRemoveLocales( const Sequence< Locale >& aLocaleSeq ) +{ + const Locale* pLocales = aLocaleSeq.getConstArray(); + sal_Int32 nLocaleCount = aLocaleSeq.getLength(); + bool bConsistent = true; + bool bModified = false; + + for( sal_Int32 i = 0 ; i < nLocaleCount ; i++ ) + { + const Locale& rLocale = pLocales[ i ]; + bool bRemove = true; + + // Check if last locale + Sequence< Locale > aResLocaleSeq = m_xStringResourceManager->getLocales(); + if( aResLocaleSeq.getLength() == 1 ) + { + const Locale& rLastResLocale = aResLocaleSeq.getConstArray()[ 0 ]; + if( localesAreEqual( rLocale, rLastResLocale ) ) + { + disableResourceForAllLibraryDialogs(); + } + else + { + // Inconsistency, keep last locale + bConsistent = false; + bRemove = false; + } + } + + if( bRemove ) + { + try + { + m_xStringResourceManager->removeLocale( rLocale ); + bModified = true; + } + catch(const IllegalArgumentException&) + { + bConsistent = false; + } + } + } + if( bModified ) + { + MarkDocumentModified( m_aDocument ); + + // update slots + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_CURRENT_LANG ); + pBindings->Invalidate( SID_BASICIDE_MANAGE_LANG ); + } + + handleTranslationbar(); + } + + DBG_ASSERT( bConsistent, + "LocalizationMgr::handleRemoveLocales(): sequence contains unsupported locales" ); +} + +void LocalizationMgr::handleSetDefaultLocale(const Locale& rLocale) +{ + if( !m_xStringResourceManager.is() ) + return; + + try + { + m_xStringResourceManager->setDefaultLocale(rLocale); + } + catch(const IllegalArgumentException&) + { + OSL_FAIL( "LocalizationMgr::handleSetDefaultLocale: Invalid locale" ); + } + + // update locale toolbar + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_BASICIDE_CURRENT_LANG ); +} + +void LocalizationMgr::handleSetCurrentLocale(const css::lang::Locale& rLocale) +{ + if( !m_xStringResourceManager.is() ) + return; + + try + { + m_xStringResourceManager->setCurrentLocale(rLocale, false); + } + catch(const IllegalArgumentException&) + { + OSL_FAIL( "LocalizationMgr::handleSetCurrentLocale: Invalid locale" ); + } + + // update locale toolbar + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_BASICIDE_CURRENT_LANG ); + + if (DialogWindow* pDlgWin = dynamic_cast<DialogWindow*>(m_pShell->GetCurWindow())) + if (!pDlgWin->IsSuspended()) + pDlgWin->GetEditor().UpdatePropertyBrowserDelayed(); +} + +void LocalizationMgr::handleBasicStarted() +{ + if( m_xStringResourceManager.is() ) + m_aLocaleBeforeBasicStart = m_xStringResourceManager->getCurrentLocale(); +} + +void LocalizationMgr::handleBasicStopped() +{ + try + { + if( m_xStringResourceManager.is() ) + m_xStringResourceManager->setCurrentLocale( m_aLocaleBeforeBasicStart, true ); + } + catch(const IllegalArgumentException&) + { + } +} + + +static DialogWindow* FindDialogWindowForEditor( DlgEditor const * pEditor ) +{ + Shell::WindowTable const& aWindowTable = GetShell()->GetWindowTable(); + for (auto const& window : aWindowTable) + { + BaseWindow* pWin = window.second; + if (!pWin->IsSuspended()) + if (DialogWindow* pDlgWin = dynamic_cast<DialogWindow*>(pWin)) + { + if (&pDlgWin->GetEditor() == pEditor) + return pDlgWin; + } + } + return nullptr; +} + + +void LocalizationMgr::setControlResourceIDsForNewEditorObject( DlgEditor const * pEditor, + const Any& rControlAny, std::u16string_view aCtrlName ) +{ + // Get library for DlgEditor + DialogWindow* pDlgWin = FindDialogWindowForEditor( pEditor ); + if( !pDlgWin ) + return; + ScriptDocument aDocument( pDlgWin->GetDocument() ); + DBG_ASSERT( aDocument.isValid(), "LocalizationMgr::setControlResourceIDsForNewEditorObject: invalid document!" ); + if ( !aDocument.isValid() ) + return; + const OUString& rLibName = pDlgWin->GetLibName(); + Reference< container::XNameContainer > xDialogLib( aDocument.getLibrary( E_DIALOGS, rLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + + // Set resource property + if( !xStringResourceManager.is() || !xStringResourceManager->getLocales().hasElements() ) + return; + + OUString aDialogName = pDlgWin->GetName(); + Reference< XStringResourceResolver > xDummyStringResolver; + sal_Int32 nChangedCount = implHandleControlResourceProperties + ( rControlAny, aDialogName, aCtrlName, xStringResourceManager, + xDummyStringResolver, SET_IDS ); + + if( nChangedCount ) + MarkDocumentModified( aDocument ); +} + +void LocalizationMgr::renameControlResourceIDsForEditorObject( DlgEditor const * pEditor, + const css::uno::Any& rControlAny, std::u16string_view aNewCtrlName ) +{ + // Get library for DlgEditor + DialogWindow* pDlgWin = FindDialogWindowForEditor( pEditor ); + if( !pDlgWin ) + return; + ScriptDocument aDocument( pDlgWin->GetDocument() ); + DBG_ASSERT( aDocument.isValid(), "LocalizationMgr::renameControlResourceIDsForEditorObject: invalid document!" ); + if ( !aDocument.isValid() ) + return; + const OUString& rLibName = pDlgWin->GetLibName(); + Reference< container::XNameContainer > xDialogLib( aDocument.getLibrary( E_DIALOGS, rLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + + // Set resource property + if( !xStringResourceManager.is() || !xStringResourceManager->getLocales().hasElements() ) + return; + + OUString aDialogName = pDlgWin->GetName(); + Reference< XStringResourceResolver > xDummyStringResolver; + implHandleControlResourceProperties + ( rControlAny, aDialogName, aNewCtrlName, xStringResourceManager, + xDummyStringResolver, RENAME_CONTROL_IDS ); +} + + +void LocalizationMgr::deleteControlResourceIDsForDeletedEditorObject( DlgEditor const * pEditor, + const Any& rControlAny, std::u16string_view aCtrlName ) +{ + // Get library for DlgEditor + DialogWindow* pDlgWin = FindDialogWindowForEditor( pEditor ); + if( !pDlgWin ) + return; + ScriptDocument aDocument( pDlgWin->GetDocument() ); + DBG_ASSERT( aDocument.isValid(), "LocalizationMgr::deleteControlResourceIDsForDeletedEditorObject: invalid document!" ); + if ( !aDocument.isValid() ) + return; + const OUString& rLibName = pDlgWin->GetLibName(); + Reference< container::XNameContainer > xDialogLib( aDocument.getLibrary( E_DIALOGS, rLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + + OUString aDialogName = pDlgWin->GetName(); + Reference< XStringResourceResolver > xDummyStringResolver; + sal_Int32 nChangedCount = implHandleControlResourceProperties + ( rControlAny, aDialogName, aCtrlName, xStringResourceManager, + xDummyStringResolver, REMOVE_IDS_FROM_RESOURCE ); + + if( nChangedCount ) + MarkDocumentModified( aDocument ); +} + +void LocalizationMgr::setStringResourceAtDialog( const ScriptDocument& rDocument, const OUString& aLibName, + std::u16string_view aDlgName, const Reference< container::XNameContainer >& xDialogModel ) +{ + // Get library + Reference< container::XNameContainer > xDialogLib( rDocument.getLibrary( E_DIALOGS, aLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + + // Set resource property + if( !xStringResourceManager.is() ) + return; + + // Not very elegant as dialog may or may not be localized yet + // TODO: Find better place, where dialog is created + if( xStringResourceManager->getLocales().hasElements() ) + { + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + Reference< XStringResourceResolver > xDummyStringResolver; + implHandleControlResourceProperties( aDialogCtrl, aDlgName, + std::u16string_view(), xStringResourceManager, + xDummyStringResolver, SET_IDS ); + } + + Reference< beans::XPropertySet > xDlgPSet( xDialogModel, UNO_QUERY ); + xDlgPSet->setPropertyValue( "ResourceResolver", Any(xStringResourceManager) ); +} + +void LocalizationMgr::renameStringResourceIDs( const ScriptDocument& rDocument, const OUString& aLibName, + std::u16string_view aDlgName, const Reference< container::XNameContainer >& xDialogModel ) +{ + // Get library + Reference< container::XNameContainer > xDialogLib( rDocument.getLibrary( E_DIALOGS, aLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + if( !xStringResourceManager.is() ) + return; + + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + Reference< XStringResourceResolver > xDummyStringResolver; + implHandleControlResourceProperties( aDialogCtrl, aDlgName, + std::u16string_view(), xStringResourceManager, + xDummyStringResolver, RENAME_DIALOG_IDS ); + + // Handle all controls + for(const auto& rCtrlName : xDialogModel->getElementNames()) { + Any aCtrl = xDialogModel->getByName( rCtrlName ); + implHandleControlResourceProperties( aCtrl, aDlgName, + rCtrlName, xStringResourceManager, + xDummyStringResolver, RENAME_DIALOG_IDS ); + } +} + +void LocalizationMgr::removeResourceForDialog( const ScriptDocument& rDocument, const OUString& aLibName, + std::u16string_view aDlgName, const Reference< container::XNameContainer >& xDialogModel ) +{ + // Get library + Reference< container::XNameContainer > xDialogLib( rDocument.getLibrary( E_DIALOGS, aLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + if( !xStringResourceManager.is() ) + return; + + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + Reference< XStringResourceResolver > xDummyStringResolver; + implHandleControlResourceProperties( aDialogCtrl, aDlgName, + std::u16string_view(), xStringResourceManager, + xDummyStringResolver, REMOVE_IDS_FROM_RESOURCE ); + + // Handle all controls + for(const auto& rCtrlName : xDialogModel->getElementNames()) { + Any aCtrl = xDialogModel->getByName( rCtrlName ); + implHandleControlResourceProperties( aCtrl, aDlgName, + rCtrlName, xStringResourceManager, + xDummyStringResolver, REMOVE_IDS_FROM_RESOURCE ); + } +} + +void LocalizationMgr::resetResourceForDialog( const Reference< container::XNameContainer >& xDialogModel, + const Reference< XStringResourceManager >& xStringResourceManager ) +{ + if( !xStringResourceManager.is() ) + return; + + // Dialog as control + std::u16string_view aDummyName; + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + Reference< XStringResourceResolver > xDummyStringResolver; + implHandleControlResourceProperties( aDialogCtrl, aDummyName, + aDummyName, xStringResourceManager, xDummyStringResolver, RESET_IDS ); + + // Handle all controls + for(const auto& rCtrlName : xDialogModel->getElementNames()){ + Any aCtrl = xDialogModel->getByName( rCtrlName ); + implHandleControlResourceProperties( aCtrl, aDummyName, + rCtrlName, xStringResourceManager, xDummyStringResolver, RESET_IDS ); + } +} + +void LocalizationMgr::setResourceIDsForDialog( const Reference< container::XNameContainer >& xDialogModel, + const Reference< XStringResourceManager >& xStringResourceManager ) +{ + if( !xStringResourceManager.is() ) + return; + + // Dialog as control + std::u16string_view aDummyName; + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + Reference< XStringResourceResolver > xDummyStringResolver; + implHandleControlResourceProperties( aDialogCtrl, aDummyName, + aDummyName, xStringResourceManager, xDummyStringResolver, SET_IDS ); + + // Handle all controls + for(const auto& rCtrlName : xDialogModel->getElementNames()) { + Any aCtrl = xDialogModel->getByName( rCtrlName ); + implHandleControlResourceProperties( aCtrl, aDummyName, + rCtrlName, xStringResourceManager, xDummyStringResolver, SET_IDS ); + } +} + +void LocalizationMgr::copyResourcesForPastedEditorObject( DlgEditor const * pEditor, + const Any& rControlAny, std::u16string_view aCtrlName, + const Reference< XStringResourceResolver >& xSourceStringResolver ) +{ + // Get library for DlgEditor + DialogWindow* pDlgWin = FindDialogWindowForEditor( pEditor ); + if( !pDlgWin ) + return; + ScriptDocument aDocument( pDlgWin->GetDocument() ); + DBG_ASSERT( aDocument.isValid(), "LocalizationMgr::copyResourcesForPastedEditorObject: invalid document!" ); + if ( !aDocument.isValid() ) + return; + const OUString& rLibName = pDlgWin->GetLibName(); + Reference< container::XNameContainer > xDialogLib( aDocument.getLibrary( E_DIALOGS, rLibName, true ) ); + Reference< XStringResourceManager > xStringResourceManager = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + + // Set resource property + if( !xStringResourceManager.is() || !xStringResourceManager->getLocales().hasElements() ) + return; + + OUString aDialogName = pDlgWin->GetName(); + implHandleControlResourceProperties + ( rControlAny, aDialogName, aCtrlName, xStringResourceManager, + xSourceStringResolver, MOVE_RESOURCES ); +} + +void LocalizationMgr::copyResourceForDroppedDialog( const Reference< container::XNameContainer >& xDialogModel, + std::u16string_view aDialogName, + const Reference< XStringResourceManager >& xStringResourceManager, + const Reference< XStringResourceResolver >& xSourceStringResolver ) +{ + if( !xStringResourceManager.is() ) + return; + + // Dialog as control + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + implHandleControlResourceProperties( aDialogCtrl, aDialogName, + std::u16string_view(), xStringResourceManager, xSourceStringResolver, MOVE_RESOURCES ); + + // Handle all controls + for(const auto& rCtrlName : xDialogModel->getElementNames()) { + Any aCtrl = xDialogModel->getByName( rCtrlName ); + implHandleControlResourceProperties( aCtrl, aDialogName, + rCtrlName, xStringResourceManager, xSourceStringResolver, MOVE_RESOURCES ); + } +} + +void LocalizationMgr::copyResourceForDialog( + const Reference< container::XNameContainer >& xDialogModel, + const Reference< XStringResourceResolver >& xSourceStringResolver, + const Reference< XStringResourceManager >& xTargetStringResourceManager ) +{ + if( !xDialogModel.is() || !xSourceStringResolver.is() || !xTargetStringResourceManager.is() ) + return; + + std::u16string_view aDummyName; + Any aDialogCtrl; + aDialogCtrl <<= xDialogModel; + implHandleControlResourceProperties + ( aDialogCtrl, aDummyName, aDummyName, xTargetStringResourceManager, + xSourceStringResolver, COPY_RESOURCES ); + + // Handle all controls + for(const auto& rCtrlName : xDialogModel->getElementNames()) { + Any aCtrl = xDialogModel->getByName( rCtrlName ); + implHandleControlResourceProperties( aCtrl, aDummyName, aDummyName, + xTargetStringResourceManager, xSourceStringResolver, COPY_RESOURCES ); + } +} + +Reference< XStringResourceManager > LocalizationMgr::getStringResourceFromDialogLibrary + ( const Reference< container::XNameContainer >& xDialogLib ) +{ + Reference< XStringResourceManager > xStringResourceManager; + if( xDialogLib.is() ) + { + Reference< resource::XStringResourceSupplier > xStringResourceSupplier( xDialogLib, UNO_QUERY ); + if( xStringResourceSupplier.is() ) + { + Reference< resource::XStringResourceResolver > + xStringResourceResolver = xStringResourceSupplier->getStringResource(); + + xStringResourceManager = + Reference< resource::XStringResourceManager >( xStringResourceResolver, UNO_QUERY ); + } + } + return xStringResourceManager; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/macrodlg.cxx b/basctl/source/basicide/macrodlg.cxx new file mode 100644 index 0000000000..6b4afb79f7 --- /dev/null +++ b/basctl/source/basicide/macrodlg.cxx @@ -0,0 +1,879 @@ +/* -*- 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 "macrodlg.hxx" +#include <basidesh.hxx> +#include <strings.hrc> +#include <iderid.hxx> + +#include <iderdll.hxx> +#include "iderdll2.hxx" + +#include "moduldlg.hxx" +#include <basic/basmgr.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbmod.hxx> +#include <com/sun/star/script/XLibraryContainer2.hpp> +#include <sal/log.hxx> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/minfitem.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <tools/debug.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <osl/diagnose.h> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +MacroChooser::MacroChooser(weld::Window* pParnt, const Reference< frame::XFrame >& xDocFrame) + : SfxDialogController(pParnt, "modules/BasicIDE/ui/basicmacrodialog.ui", "BasicMacroDialog") + , m_xDocumentFrame(xDocFrame) + // the Sfx doesn't ask the BasicManager whether modified or not + // => start saving in case of a change without a into the BasicIDE. + , bForceStoreBasic(false) + , nMode(All) + , m_xMacroNameEdit(m_xBuilder->weld_entry("macronameedit")) + , m_xMacroFromTxT(m_xBuilder->weld_label("macrofromft")) + , m_xMacrosSaveInTxt(m_xBuilder->weld_label("macrotoft")) + , m_xBasicBox(new SbTreeListBox(m_xBuilder->weld_tree_view("libraries"), m_xDialog.get())) + , m_xBasicBoxIter(m_xBasicBox->make_iterator()) + , m_xMacrosInTxt(m_xBuilder->weld_label("existingmacrosft")) + , m_xMacroBox(m_xBuilder->weld_tree_view("macros")) + , m_xMacroBoxIter(m_xMacroBox->make_iterator()) + , m_xRunButton(m_xBuilder->weld_button("ok")) + , m_xCloseButton(m_xBuilder->weld_button("close")) + , m_xAssignButton(m_xBuilder->weld_button("assign")) + , m_xEditButton(m_xBuilder->weld_button("edit")) + , m_xDelButton(m_xBuilder->weld_button("delete")) + , m_xNewButton(m_xBuilder->weld_button("new")) + , m_xOrganizeButton(m_xBuilder->weld_button("organize")) + , m_xNewLibButton(m_xBuilder->weld_button("newlibrary")) + , m_xNewModButton(m_xBuilder->weld_button("newmodule")) +{ + m_xBasicBox->set_size_request(m_xBasicBox->get_approximate_digit_width() * 30, m_xBasicBox->get_height_rows(18)); + m_xMacroBox->set_size_request(m_xMacroBox->get_approximate_digit_width() * 30, m_xMacroBox->get_height_rows(18)); + + m_aMacrosInTxtBaseStr = m_xMacrosInTxt->get_label(); + + m_xRunButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xCloseButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xAssignButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xEditButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xDelButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xNewButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xOrganizeButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + + // Buttons only for MacroChooser::Recording + m_xNewLibButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xNewModButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) ); + m_xNewLibButton->hide(); // default + m_xNewModButton->hide(); // default + m_xMacrosSaveInTxt->hide(); // default + + m_xMacroNameEdit->connect_changed( LINK( this, MacroChooser, EditModifyHdl ) ); + + m_xBasicBox->connect_changed( LINK( this, MacroChooser, BasicSelectHdl ) ); + + m_xMacroBox->connect_row_activated( LINK( this, MacroChooser, MacroDoubleClickHdl ) ); + m_xMacroBox->connect_changed( LINK( this, MacroChooser, MacroSelectHdl ) ); + m_xMacroBox->connect_popup_menu( LINK( this, MacroChooser, ContextMenuHdl ) ); + + m_xBasicBox->SetMode( BrowseMode::Modules ); + + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute( SID_BASICIDE_STOREALLMODULESOURCES ); + + m_xBasicBox->ScanAllEntries(); +} + +MacroChooser::~MacroChooser() +{ + if (bForceStoreBasic) + { + SfxGetpApp()->SaveBasicAndDialogContainer(); + bForceStoreBasic = false; + } +} + +void MacroChooser::StoreMacroDescription() +{ + if (!m_xBasicBox->get_selected(m_xBasicBoxIter.get())) + return; + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()); + OUString aMethodName; + if (m_xMacroBox->get_selected(m_xMacroBoxIter.get())) + aMethodName = m_xMacroBox->get_text(*m_xMacroBoxIter); + else + aMethodName = m_xMacroNameEdit->get_text(); + if ( !aMethodName.isEmpty() ) + { + aDesc.SetMethodName( aMethodName ); + aDesc.SetType( OBJ_TYPE_METHOD ); + } + + if (ExtraData* pData = basctl::GetExtraData()) + pData->SetLastEntryDescriptor( aDesc ); +} + +void MacroChooser::RestoreMacroDescription() +{ + // The following call is a workaround to ensure the last used macro is scrolled to in kf5 + m_xDialog->resize_to_request(); + + EntryDescriptor aDesc; + if (Shell* pShell = GetShell()) + { + if (BaseWindow* pCurWin = pShell->GetCurWindow()) + aDesc = pCurWin->CreateEntryDescriptor(); + } + else + { + if (ExtraData* pData = basctl::GetExtraData()) + aDesc = pData->GetLastEntryDescriptor(); + } + + m_xBasicBox->SetCurrentEntry(aDesc); + BasicSelectHdl(m_xBasicBox->get_widget()); + + OUString aLastMacro( aDesc.GetMethodName() ); + if (!aLastMacro.isEmpty()) + { + // find entry in macro box + auto nIndex = m_xMacroBox->find_text(aLastMacro); + if (nIndex != -1) + m_xMacroBox->select(nIndex); + else + { + m_xMacroNameEdit->set_text(aLastMacro); + m_xMacroNameEdit->select_region(0, 0); + } + } +} + +short MacroChooser::run() +{ + RestoreMacroDescription(); + + // #104198 Check if "wrong" document is active + bool bSelectedEntry = m_xBasicBox->get_cursor(m_xBasicBoxIter.get()); + EntryDescriptor aDesc(m_xBasicBox->GetEntryDescriptor(bSelectedEntry ? m_xBasicBoxIter.get() : nullptr)); + const ScriptDocument& rSelectedDoc(aDesc.GetDocument()); + + // App Basic is always ok, so only check if shell was found + if( rSelectedDoc.isDocument() && !rSelectedDoc.isActive() ) + { + // Search for the right entry + bool bValidIter = m_xBasicBox->get_iter_first(*m_xBasicBoxIter); + while (bValidIter) + { + EntryDescriptor aCmpDesc(m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get())); + const ScriptDocument& rCmpDoc( aCmpDesc.GetDocument() ); + if (rCmpDoc.isDocument() && rCmpDoc.isActive()) + { + std::unique_ptr<weld::TreeIter> xEntry(m_xBasicBox->make_iterator()); + m_xBasicBox->copy_iterator(*m_xBasicBoxIter, *xEntry); + std::unique_ptr<weld::TreeIter> xLastValid(m_xBasicBox->make_iterator()); + bool bValidEntryIter = true; + do + { + m_xBasicBox->copy_iterator(*xEntry, *xLastValid); + bValidEntryIter = m_xBasicBox->iter_children(*xEntry); + } + while (bValidEntryIter); + m_xBasicBox->set_cursor(*xLastValid); + } + bValidIter = m_xBasicBox->iter_next_sibling(*m_xBasicBoxIter); + } + } + + CheckButtons(); + UpdateFields(); + + // tdf#62955 - Allow searching a name with typing the first letter + m_xBasicBox->get_widget().grab_focus(); + + if ( StarBASIC::IsRunning() ) + m_xCloseButton->grab_focus(); + + return SfxDialogController::run(); +} + +void MacroChooser::EnableButton(weld::Button& rButton, bool bEnable) +{ + if ( bEnable ) + { + if (nMode == ChooseOnly || nMode == Recording) + rButton.set_sensitive(&rButton == m_xRunButton.get()); + else + rButton.set_sensitive(true); + } + else + rButton.set_sensitive(false); +} + +SbMethod* MacroChooser::GetMacro() +{ + if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get())) + return nullptr; + SbModule* pModule = m_xBasicBox->FindModule(m_xBasicBoxIter.get()); + if (!pModule) + return nullptr; + if (!m_xMacroBox->get_selected(m_xMacroBoxIter.get())) + return nullptr; + OUString aMacroName(m_xMacroBox->get_text(*m_xMacroBoxIter)); + return pModule->FindMethod(aMacroName, SbxClassType::Method); +} + +void MacroChooser::DeleteMacro() +{ + SbMethod* pMethod = GetMacro(); + DBG_ASSERT( pMethod, "DeleteMacro: No Macro !" ); + if (!(pMethod && QueryDelMacro(pMethod->GetName(), m_xDialog.get()))) + return; + + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute( SID_BASICIDE_STOREALLMODULESOURCES ); + + // mark current doc as modified: + StarBASIC* pBasic = FindBasic(pMethod); + assert(pBasic && "Basic?!"); + BasicManager* pBasMgr = FindBasicManager( pBasic ); + DBG_ASSERT( pBasMgr, "BasMgr?" ); + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + if ( aDocument.isDocument() ) + { + aDocument.setDocumentModified(); + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_SAVEDOC ); + } + + SbModule* pModule = pMethod->GetModule(); + assert(pModule && "DeleteMacro: No Module?!"); + OUString aSource( pModule->GetSource32() ); + sal_uInt16 nStart, nEnd; + pMethod->GetLineRange( nStart, nEnd ); + pModule->GetMethods()->Remove( pMethod ); + CutLines( aSource, nStart-1, nEnd-nStart+1 ); + pModule->SetSource32( aSource ); + + // update module in library + OUString aLibName = pBasic->GetName(); + OUString aModName = pModule->GetName(); + OSL_VERIFY( aDocument.updateModule( aLibName, aModName, aSource ) ); + + bool bSelected = m_xMacroBox->get_selected(m_xMacroBoxIter.get()); + DBG_ASSERT(bSelected, "DeleteMacro: Entry ?!"); + m_xMacroBox->remove(*m_xMacroBoxIter); + bForceStoreBasic = true; +} + +SbMethod* MacroChooser::CreateMacro() +{ + SbMethod* pMethod = nullptr; + if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get()) && !m_xBasicBox->get_iter_first(*m_xBasicBoxIter)) + { + SAL_WARN("basctl.basicide", "neither cursor set nor root entry to use as fallback"); + return nullptr; + } + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + OSL_ENSURE( aDocument.isAlive(), "MacroChooser::CreateMacro: no document!" ); + if ( !aDocument.isAlive() ) + return nullptr; + + OUString aLibName( aDesc.GetLibName() ); + + if ( aLibName.isEmpty() ) + aLibName = "Standard" ; + + aDocument.getOrCreateLibrary( E_SCRIPTS, aLibName ); + + OUString aOULibName( aLibName ); + Reference< script::XLibraryContainer > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) && !xModLibContainer->isLibraryLoaded( aOULibName ) ) + xModLibContainer->loadLibrary( aOULibName ); + Reference< script::XLibraryContainer > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ) ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aOULibName ) && !xDlgLibContainer->isLibraryLoaded( aOULibName ) ) + xDlgLibContainer->loadLibrary( aOULibName ); + + BasicManager* pBasMgr = aDocument.getBasicManager(); + StarBASIC* pBasic = pBasMgr ? pBasMgr->GetLib( aLibName ) : nullptr; + if ( pBasic ) + { + SbModule* pModule = nullptr; + OUString aModName( aDesc.GetName() ); + if ( !aModName.isEmpty() ) + { + // extract the module name from the string like "Sheet1 (Example1)" + if( aDesc.GetLibSubName() == IDEResId(RID_STR_DOCUMENT_OBJECTS) ) + { + aModName = aModName.getToken( 0, ' ' ); + } + pModule = pBasic->FindModule( aModName ); + } + else if ( !pBasic->GetModules().empty() ) + pModule = pBasic->GetModules().front().get(); + + // Retain the desired macro name before the macro dialog box is forced to close + // by opening the module name dialog window when no module exists in the current library. + OUString aSubName = m_xMacroNameEdit->get_text(); + + if ( !pModule ) + { + pModule = createModImpl(m_xDialog.get(), aDocument, *m_xBasicBox, aLibName, aModName, false); + } + + DBG_ASSERT( !pModule || !pModule->FindMethod( aSubName, SbxClassType::Method ), "Macro exists already!" ); + pMethod = pModule ? basctl::CreateMacro( pModule, aSubName ) : nullptr; + } + + return pMethod; +} + +void MacroChooser::SaveSetCurEntry(weld::TreeView& rBox, const weld::TreeIter& rEntry) +{ + // the edit would be killed by the highlight otherwise: + + OUString aSaveText(m_xMacroNameEdit->get_text()); + int nStartPos, nEndPos; + m_xMacroNameEdit->get_selection_bounds(nStartPos, nEndPos); + + rBox.set_cursor(rEntry); + + m_xMacroNameEdit->set_text(aSaveText); + m_xMacroNameEdit->select_region(nStartPos, nEndPos); +} + +void MacroChooser::CheckButtons() +{ + const bool bCurEntry = m_xBasicBox->get_cursor(m_xBasicBoxIter.get()); + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(bCurEntry ? m_xBasicBoxIter.get() : nullptr); + const bool bMacroEntry = m_xMacroBox->get_selected(nullptr); + SbMethod* pMethod = GetMacro(); + + // check, if corresponding libraries are readonly + bool bReadOnly = false; + sal_uInt16 nDepth = bCurEntry ? m_xBasicBox->get_iter_depth(*m_xBasicBoxIter) : 0; + if ( nDepth == 1 || nDepth == 2 ) + { + const ScriptDocument& aDocument( aDesc.GetDocument() ); + const OUString& aOULibName( aDesc.GetLibName() ); + Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) && xModLibContainer->isLibraryReadOnly( aOULibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aOULibName ) && xDlgLibContainer->isLibraryReadOnly( aOULibName ) ) ) + { + bReadOnly = true; + } + } + + if (nMode != Recording) + { + // Run... + bool bEnable = pMethod != nullptr; + if (nMode != ChooseOnly && StarBASIC::IsRunning()) + bEnable = false; + EnableButton(*m_xRunButton, bEnable); + } + + // organising still possible? + + // Assign... + EnableButton(*m_xAssignButton, pMethod != nullptr); + + // Edit... + EnableButton(*m_xEditButton, bMacroEntry); + + // Organizer... + EnableButton(*m_xOrganizeButton, !StarBASIC::IsRunning() && nMode == All); + + // m_xDelButton/m_xNewButton ->... + bool bProtected = bCurEntry && m_xBasicBox->IsEntryProtected(m_xBasicBoxIter.get()); + bool bShare = ( aDesc.GetLocation() == LIBRARY_LOCATION_SHARE ); + bool bEnable = !StarBASIC::IsRunning() && nMode == All && !bProtected && !bReadOnly && !bShare; + EnableButton(*m_xDelButton, bEnable); + EnableButton(*m_xNewButton, bEnable); + if (nMode == All) + { + if (pMethod) + { + m_xDelButton->show(); + m_xNewButton->hide(); + } + else + { + m_xNewButton->show(); + m_xDelButton->hide(); + } + } + + if (nMode == Recording) + { + // save button + m_xRunButton->set_sensitive(!bProtected && !bReadOnly && !bShare); + // new library button + m_xNewLibButton->set_sensitive(!bShare); + // new module button + m_xNewModButton->set_sensitive(!bProtected && !bReadOnly && !bShare); + } +} + +IMPL_LINK_NOARG(MacroChooser, MacroDoubleClickHdl, weld::TreeView&, bool) +{ + SbMethod* pMethod = GetMacro(); + SbModule* pModule = pMethod ? pMethod->GetModule() : nullptr; + StarBASIC* pBasic = pModule ? static_cast<StarBASIC*>(pModule->GetParent()) : nullptr; + BasicManager* pBasMgr = pBasic ? FindBasicManager(pBasic) : nullptr; + ScriptDocument aDocument(ScriptDocument::getDocumentForBasicManager(pBasMgr)); + if (aDocument.isDocument() && !aDocument.allowMacros()) + { + std::unique_ptr<weld::MessageDialog> xError( + Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Warning, + VclButtonsType::Ok, IDEResId(RID_STR_CANNOTRUNMACRO))); + xError->run(); + return true; + } + + StoreMacroDescription(); + if (nMode == Recording) + { + if (pMethod && !QueryReplaceMacro(pMethod->GetName(), m_xDialog.get())) + return true; + } + + m_xDialog->response(Macro_OkRun); + return true; +} + +IMPL_LINK_NOARG(MacroChooser, MacroSelectHdl, weld::TreeView&, void) +{ + UpdateFields(); + CheckButtons(); +} + +IMPL_LINK_NOARG(MacroChooser, BasicSelectHdl, weld::TreeView&, void) +{ + SbModule* pModule = nullptr; + if (m_xBasicBox->get_cursor(m_xBasicBoxIter.get())) + pModule = m_xBasicBox->FindModule(m_xBasicBoxIter.get()); + m_xMacroBox->clear(); + if (pModule) + { + m_xMacrosInTxt->set_label(m_aMacrosInTxtBaseStr + " " + pModule->GetName()); + + m_xMacroBox->freeze(); + + sal_uInt32 nMacroCount = pModule->GetMethods()->Count(); + for ( sal_uInt32 iMeth = 0; iMeth < nMacroCount; iMeth++ ) + { + SbMethod* pMethod = static_cast<SbMethod*>(pModule->GetMethods()->Get(iMeth)); + assert(pMethod && "Method not found!"); + if (pMethod->IsHidden()) + continue; + m_xMacroBox->append_text(pMethod->GetName()); + } + + m_xMacroBox->thaw(); + + if (m_xMacroBox->get_iter_first(*m_xMacroBoxIter)) + m_xMacroBox->set_cursor(*m_xMacroBoxIter); + } + + UpdateFields(); + CheckButtons(); +} + +IMPL_LINK_NOARG(MacroChooser, EditModifyHdl, weld::Entry&, void) +{ + // select the module in which the macro is put at "new", + // if BasicManager or Lib is selecting + if (m_xBasicBox->get_cursor(m_xBasicBoxIter.get())) + { + sal_uInt16 nDepth = m_xBasicBox->get_iter_depth(*m_xBasicBoxIter); + if (nDepth == 1 && m_xBasicBox->IsEntryProtected(m_xBasicBoxIter.get())) + { + // then put to the respective Std-Lib... + m_xBasicBox->iter_parent(*m_xBasicBoxIter); + m_xBasicBox->iter_children(*m_xBasicBoxIter); + } + if (nDepth < 2) + { + std::unique_ptr<weld::TreeIter> xNewEntry(m_xBasicBox->make_iterator()); + m_xBasicBox->copy_iterator(*m_xBasicBoxIter, *xNewEntry); + bool bCurEntry = true; + do + { + bCurEntry = m_xBasicBox->iter_children(*m_xBasicBoxIter); + if (bCurEntry) + { + m_xBasicBox->copy_iterator(*m_xBasicBoxIter, *xNewEntry); + nDepth = m_xBasicBox->get_iter_depth(*m_xBasicBoxIter); + } + } + while (bCurEntry && (nDepth < 2)); + SaveSetCurEntry(m_xBasicBox->get_widget(), *xNewEntry); + } + auto nCount = m_xMacroBox->n_children(); + if (nCount) + { + OUString aEdtText(m_xMacroNameEdit->get_text()); + bool bFound = false; + bool bValidIter = m_xMacroBox->get_iter_first(*m_xMacroBoxIter); + while (bValidIter) + { + if (m_xMacroBox->get_text(*m_xMacroBoxIter).equalsIgnoreAsciiCase(aEdtText)) + { + SaveSetCurEntry(*m_xMacroBox, *m_xMacroBoxIter); + bFound = true; + break; + } + bValidIter = m_xMacroBox->iter_next_sibling(*m_xMacroBoxIter); + } + if (!bFound) + { + bValidIter = m_xMacroBox->get_selected(m_xMacroBoxIter.get()); + // if the entry exists ->Select ->Description... + if (bValidIter) + m_xMacroBox->unselect(*m_xMacroBoxIter); + } + } + } + + CheckButtons(); +} + +IMPL_LINK(MacroChooser, ButtonHdl, weld::Button&, rButton, void) +{ + // apart from New/Record the Description is done by LoseFocus + if (&rButton == m_xRunButton.get()) + { + StoreMacroDescription(); + + // #116444# check security settings before macro execution + if (nMode == All) + { + SbMethod* pMethod = GetMacro(); + SbModule* pModule = pMethod ? pMethod->GetModule() : nullptr; + StarBASIC* pBasic = pModule ? static_cast<StarBASIC*>(pModule->GetParent()) : nullptr; + BasicManager* pBasMgr = pBasic ? FindBasicManager(pBasic) : nullptr; + if ( pBasMgr ) + { + ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); + if ( aDocument.isDocument() && !aDocument.allowMacros() ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_CANNOTRUNMACRO))); + xError->run(); + return; + } + } + } + else if (nMode == Recording ) + { + if ( !IsValidSbxName(m_xMacroNameEdit->get_text()) ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xError->run(); + m_xMacroNameEdit->select_region(0, -1); + m_xMacroNameEdit->grab_focus(); + return; + } + + SbMethod* pMethod = GetMacro(); + if (pMethod && !QueryReplaceMacro(pMethod->GetName(), m_xDialog.get())) + return; + } + + m_xDialog->response(Macro_OkRun); + } + else if (&rButton == m_xCloseButton.get()) + { + StoreMacroDescription(); + m_xDialog->response(Macro_Close); + } + else if (&rButton == m_xEditButton.get() || &rButton == m_xDelButton.get() || &rButton == m_xNewButton.get()) + { + if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get()) && !m_xBasicBox->get_iter_first(*m_xBasicBoxIter)) + { + SAL_WARN("basctl.basicide", "neither cursor set nor root entry to use as fallback"); + return; + } + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + DBG_ASSERT( aDocument.isAlive(), "MacroChooser::ButtonHdl: no document, or document is dead!" ); + if ( !aDocument.isAlive() ) + return; + BasicManager* pBasMgr = aDocument.getBasicManager(); + const OUString& aLib( aDesc.GetLibName() ); + OUString aMod( aDesc.GetName() ); + // extract the module name from the string like "Sheet1 (Example1)" + if( aDesc.GetLibSubName() == IDEResId(RID_STR_DOCUMENT_OBJECTS) ) + { + aMod = aMod.getToken( 0, ' ' ); + } + const OUString& aSub( aDesc.GetMethodName() ); + SfxMacroInfoItem aInfoItem( SID_BASICIDE_ARG_MACROINFO, pBasMgr, aLib, aMod, aSub, OUString() ); + if (&rButton == m_xEditButton.get()) + { + if (m_xMacroBox->get_selected(m_xMacroBoxIter.get())) + aInfoItem.SetMethod(m_xMacroBox->get_text(*m_xMacroBoxIter)); + StoreMacroDescription(); + m_xDialog->hide(); // tdf#126828 dismiss dialog before opening new window + + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs ); + SfxGetpApp()->ExecuteSlot( aRequest ); + + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + pDispatcher->ExecuteList(SID_BASICIDE_EDITMACRO, + SfxCallMode::ASYNCHRON, { &aInfoItem }); + } + m_xDialog->response(Macro_Edit); + } + else + { + if (&rButton == m_xDelButton.get()) + { + DeleteMacro(); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + pDispatcher->ExecuteList( SID_BASICIDE_UPDATEMODULESOURCE, + SfxCallMode::SYNCHRON, { &aInfoItem }); + } + CheckButtons(); + UpdateFields(); + //if ( m_xMacroBox->GetCurEntry() ) // OV-Bug ? + // m_xMacroBox->Select( m_xMacroBox->GetCurEntry() ); + } + else + { + if ( !IsValidSbxName(m_xMacroNameEdit->get_text()) ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xError->run(); + m_xMacroNameEdit->select_region(0, -1); + m_xMacroNameEdit->grab_focus(); + return; + } + SbMethod* pMethod = CreateMacro(); + if ( pMethod ) + { + aInfoItem.SetMethod( pMethod->GetName() ); + aInfoItem.SetModule( pMethod->GetModule()->GetName() ); + aInfoItem.SetLib( pMethod->GetModule()->GetParent()->GetName() ); + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs ); + SfxGetpApp()->ExecuteSlot( aRequest ); + + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + pDispatcher->ExecuteList(SID_BASICIDE_EDITMACRO, + SfxCallMode::ASYNCHRON, { &aInfoItem }); + } + StoreMacroDescription(); + m_xDialog->response(Macro_New); + } + } + } + } + else if (&rButton == m_xAssignButton.get()) + { + if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get()) && !m_xBasicBox->get_iter_first(*m_xBasicBoxIter)) + { + SAL_WARN("basctl.basicide", "neither cursor set nor root entry to use as fallback"); + return; + } + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + DBG_ASSERT( aDocument.isAlive(), "MacroChooser::ButtonHdl: no document, or document is dead!" ); + if ( !aDocument.isAlive() ) + return; + BasicManager* pBasMgr = aDocument.getBasicManager(); + const OUString& aLib( aDesc.GetLibName() ); + const OUString& aMod( aDesc.GetName() ); + OUString aSub( m_xMacroNameEdit->get_text() ); + SbMethod* pMethod = GetMacro(); + DBG_ASSERT( pBasMgr, "BasMgr?" ); + DBG_ASSERT( pMethod, "Method?" ); + OUString aComment( GetInfo( pMethod ) ); + SfxMacroInfoItem aItem( SID_MACROINFO, pBasMgr, aLib, aMod, aSub, aComment ); + SfxAllItemSet Args( SfxGetpApp()->GetPool() ); + + SfxAllItemSet aInternalSet(SfxGetpApp()->GetPool()); + if (m_xDocumentFrame.is()) + aInternalSet.Put(SfxUnoFrameItem(SID_FILLFRAME, m_xDocumentFrame)); + + SfxRequest aRequest(SID_CONFIGACCEL, SfxCallMode::SYNCHRON, Args, aInternalSet); + aRequest.AppendItem( aItem ); + SfxGetpApp()->ExecuteSlot( aRequest ); + } + else if (&rButton == m_xNewLibButton.get()) + { + if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get()) && !m_xBasicBox->get_iter_first(*m_xBasicBoxIter)) + { + SAL_WARN("basctl.basicide", "neither cursor set nor root entry to use as fallback"); + return; + } + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + createLibImpl(m_xDialog.get(), aDocument, nullptr, m_xBasicBox.get()); + } + else if (&rButton == m_xNewModButton.get()) + { + if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get()) && !m_xBasicBox->get_iter_first(*m_xBasicBoxIter)) + { + SAL_WARN("basctl.basicide", "neither cursor set nor root entry to use as fallback"); + return; + } + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + const OUString& aLibName( aDesc.GetLibName() ); + createModImpl(m_xDialog.get(), aDocument, *m_xBasicBox, aLibName, OUString(), true); + } + else if (&rButton == m_xOrganizeButton.get()) + { + StoreMacroDescription(); + + m_xBasicBox->get_selected(m_xBasicBoxIter.get()); + auto xDlg(std::make_shared<OrganizeDialog>(m_xDialog.get(), nullptr, 0)); + weld::DialogController::runAsync(xDlg, [this](sal_Int32 nRet) { + if (nRet == RET_OK) // not only closed + { + m_xDialog->response(Macro_Edit); + return; + } + + Shell* pShell = GetShell(); + if ( pShell && pShell->IsAppBasicModified() ) + bForceStoreBasic = true; + + m_xBasicBox->UpdateEntries(); + }); + } +} + +IMPL_LINK(MacroChooser, ContextMenuHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu || !m_xMacroBox->n_children()) + return false; + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xMacroBox.get(), "modules/BasicIDE/ui/sortmenu.ui")); + std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("sortmenu")); + std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu("sortsubmenu")); + xDropMenu->set_active("alphabetically", m_xMacroBox->get_sort_order()); + xDropMenu->set_active("properorder", !m_xMacroBox->get_sort_order()); + + OUString sCommand(xPopup->popup_at_rect(m_xMacroBox.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); + if (sCommand == "alphabetically") + { + m_xMacroBox->make_sorted(); + } + else if (sCommand == "properorder") + { + m_xMacroBox->make_unsorted(); + BasicSelectHdl(m_xBasicBox->get_widget()); + } + else if (!sCommand.isEmpty()) + { + SAL_WARN("basctl.basicide", "Unknown context menu action: " << sCommand ); + } + + return true; +} + +void MacroChooser::UpdateFields() +{ + auto nMacroEntry = m_xMacroBox->get_selected_index(); + m_xMacroNameEdit->set_text(""); + if (nMacroEntry != -1) + m_xMacroNameEdit->set_text(m_xMacroBox->get_text(nMacroEntry)); +} + +void MacroChooser::SetMode (Mode nM) +{ + nMode = nM; + switch (nMode) + { + case All: + { + m_xRunButton->set_label(IDEResId(RID_STR_RUN)); + EnableButton(*m_xDelButton, true); + EnableButton(*m_xNewButton, true); + EnableButton(*m_xOrganizeButton, true); + break; + } + + case ChooseOnly: + { + m_xRunButton->set_label(IDEResId(RID_STR_CHOOSE)); + EnableButton(*m_xDelButton, false); + EnableButton(*m_xNewButton, false); + EnableButton(*m_xOrganizeButton, false); + break; + } + + case Recording: + { + m_xRunButton->set_label(IDEResId(RID_STR_RECORD)); + EnableButton(*m_xDelButton, false); + EnableButton(*m_xNewButton, false); + EnableButton(*m_xOrganizeButton, false); + + m_xAssignButton->hide(); + m_xEditButton->hide(); + m_xDelButton->hide(); + m_xNewButton->hide(); + m_xOrganizeButton->hide(); + m_xMacroFromTxT->hide(); + + m_xNewLibButton->show(); + m_xNewModButton->show(); + m_xMacrosSaveInTxt->show(); + + break; + } + } + CheckButtons(); +} + +OUString MacroChooser::GetInfo( SbxVariable* pVar ) +{ + OUString aComment; + SbxInfoRef xInfo = pVar->GetInfo(); + if ( xInfo.is() ) + aComment = xInfo->GetComment(); + return aComment; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/macrodlg.hxx b/basctl/source/basicide/macrodlg.hxx new file mode 100644 index 0000000000..0e50ee5de0 --- /dev/null +++ b/basctl/source/basicide/macrodlg.hxx @@ -0,0 +1,108 @@ +/* -*- 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 . + */ + +#pragma once + +#include <basobj.hxx> +#include <bastype2.hxx> +#include <sfx2/basedlgs.hxx> +#include <com/sun/star/frame/XFrame.hpp> +#include <vcl/weld.hxx> + +namespace basctl +{ + +enum MacroExitCode { + Macro_Close = 110, + Macro_OkRun = 111, + Macro_New = 112, + Macro_Edit = 114, +}; + +class MacroChooser : public SfxDialogController +{ +public: + enum Mode { + All = 1, + ChooseOnly = 2, + Recording = 3, + }; + +private: + OUString m_aMacrosInTxtBaseStr; + + // For forwarding to Assign dialog + ::css::uno::Reference< ::css::frame::XFrame > m_xDocumentFrame; + + bool bForceStoreBasic; + + Mode nMode; + + DECL_LINK(MacroSelectHdl, weld::TreeView&, void); + DECL_LINK(MacroDoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(BasicSelectHdl, weld::TreeView&, void); + DECL_LINK(EditModifyHdl, weld::Entry&, void); + DECL_LINK(ContextMenuHdl, const CommandEvent&, bool); + DECL_LINK(ButtonHdl, weld::Button&, void); + + void CheckButtons(); + void SaveSetCurEntry(weld::TreeView& rBox, const weld::TreeIter& rEntry); + void UpdateFields(); + + void EnableButton(weld::Button& rButton, bool bEnable); + + static OUString GetInfo( SbxVariable* pVar ); + + void StoreMacroDescription(); + void RestoreMacroDescription(); + + std::unique_ptr<weld::Entry> m_xMacroNameEdit; + std::unique_ptr<weld::Label> m_xMacroFromTxT; + std::unique_ptr<weld::Label> m_xMacrosSaveInTxt; + std::unique_ptr<SbTreeListBox> m_xBasicBox; + std::unique_ptr<weld::TreeIter> m_xBasicBoxIter; + std::unique_ptr<weld::Label> m_xMacrosInTxt; + std::unique_ptr<weld::TreeView> m_xMacroBox; + std::unique_ptr<weld::TreeIter> m_xMacroBoxIter; + std::unique_ptr<weld::Button> m_xRunButton; + std::unique_ptr<weld::Button> m_xCloseButton; + std::unique_ptr<weld::Button> m_xAssignButton; + std::unique_ptr<weld::Button> m_xEditButton; + std::unique_ptr<weld::Button> m_xDelButton; + std::unique_ptr<weld::Button> m_xNewButton; + std::unique_ptr<weld::Button> m_xOrganizeButton; + std::unique_ptr<weld::Button> m_xNewLibButton; + std::unique_ptr<weld::Button> m_xNewModButton; +public: + MacroChooser(weld::Window *pParent, const ::css::uno::Reference< ::css::frame::XFrame >& xDocFrame); + virtual ~MacroChooser() override; + + SbMethod* GetMacro(); + void DeleteMacro(); + SbMethod* CreateMacro(); + + virtual short run() override; + + void SetMode (Mode); + Mode GetMode () const { return nMode; } +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/moduldl2.cxx b/basctl/source/basicide/moduldl2.cxx new file mode 100644 index 0000000000..1221b00942 --- /dev/null +++ b/basctl/source/basicide/moduldl2.cxx @@ -0,0 +1,1373 @@ +/* -*- 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 "moduldlg.hxx" +#include <basidesh.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <iderdll.hxx> +#include "iderdll2.hxx" +#include <iderid.hxx> +#include <basobj.hxx> +#include <svx/passwd.hxx> +#include <ucbhelper/content.hxx> +#include <rtl/uri.hxx> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/filedlghelper.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/viewfrm.hxx> +#include <svl/stritem.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> + +#include <com/sun/star/io/Pipe.hpp> +#include <com/sun/star/ui/dialogs/XFilePicker3.hpp> +#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp> +#include <com/sun/star/ui/dialogs/TemplateDescription.hpp> +#include <com/sun/star/script/DocumentScriptLibraryContainer.hpp> +#include <com/sun/star/script/DocumentDialogLibraryContainer.hpp> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/script/XLibraryContainerExport.hpp> +#include <com/sun/star/task/InteractionHandler.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/NameClash.hpp> +#include <com/sun/star/packages/manifest/ManifestWriter.hpp> +#include <unotools/pathoptions.hxx> + +#include <com/sun/star/util/VetoException.hpp> +#include <com/sun/star/script/ModuleSizeExceededRequest.hpp> + +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <o3tl/string_view.hxx> + +#include <cassert> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ucb; +using namespace ::com::sun::star::ui::dialogs; + +namespace +{ + +class DummyInteractionHandler : public ::cppu::WeakImplHelper< task::XInteractionHandler > +{ + Reference< task::XInteractionHandler2 > m_xHandler; +public: + explicit DummyInteractionHandler(const Reference<task::XInteractionHandler2>& xHandler) + : m_xHandler(xHandler) + { + } + + virtual void SAL_CALL handle( const Reference< task::XInteractionRequest >& rRequest ) override + { + if ( m_xHandler.is() ) + { + script::ModuleSizeExceededRequest aModSizeException; + if ( rRequest->getRequest() >>= aModSizeException ) + m_xHandler->handle( rRequest ); + } + } +}; + +} // namespace + +namespace +{ + int FindEntry(const weld::TreeView& rBox, std::u16string_view rName) + { + int nCount = rBox.n_children(); + for (int i = 0; i < nCount; ++i) + { + if (o3tl::equalsIgnoreAsciiCase(rName, rBox.get_text(i, 0))) + return i; + } + return -1; + } +} + +// NewObjectDialog +IMPL_LINK_NOARG(NewObjectDialog, OkButtonHandler, weld::Button&, void) +{ + if (!m_bCheckName || IsValidSbxName(m_xEdit->get_text())) + m_xDialog->response(RET_OK); + else + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xErrorBox->run(); + m_xEdit->grab_focus(); + } +} + +NewObjectDialog::NewObjectDialog(weld::Window * pParent, ObjectMode eMode, bool bCheckName) + : GenericDialogController(pParent, "modules/BasicIDE/ui/newlibdialog.ui", "NewLibDialog") + , m_xEdit(m_xBuilder->weld_entry("entry")) + , m_xOKButton(m_xBuilder->weld_button("ok")) + , m_bCheckName(bCheckName) +{ + switch (eMode) + { + case ObjectMode::Library: + m_xDialog->set_title(IDEResId(RID_STR_NEWLIB)); + break; + case ObjectMode::Module: + m_xDialog->set_title(IDEResId(RID_STR_NEWMOD)); + break; + case ObjectMode::Dialog: + m_xDialog->set_title(IDEResId(RID_STR_NEWDLG)); + break; + default: + assert(false); + } + m_xOKButton->connect_clicked(LINK(this, NewObjectDialog, OkButtonHandler)); +} + +// GotoLineDialog +GotoLineDialog::GotoLineDialog(weld::Window* pParent ) + : GenericDialogController(pParent, "modules/BasicIDE/ui/gotolinedialog.ui", "GotoLineDialog") + , m_xEdit(m_xBuilder->weld_entry("entry")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + m_xEdit->grab_focus(); + m_xOKButton->connect_clicked(LINK(this, GotoLineDialog, OkButtonHandler)); +} + +GotoLineDialog::~GotoLineDialog() +{ +} + +sal_Int32 GotoLineDialog::GetLineNumber() const +{ + return m_xEdit->get_text().toInt32(); +} + +IMPL_LINK_NOARG(GotoLineDialog, OkButtonHandler, weld::Button&, void) +{ + if (GetLineNumber()) + m_xDialog->response(RET_OK); + else + m_xEdit->select_region(0, -1); +} + +// ExportDialog +IMPL_LINK_NOARG(ExportDialog, OkButtonHandler, weld::Button&, void) +{ + m_bExportAsPackage = m_xExportAsPackageButton->get_active(); + m_xDialog->response(RET_OK); +} + +ExportDialog::ExportDialog(weld::Window * pParent) + : GenericDialogController(pParent, "modules/BasicIDE/ui/exportdialog.ui", "ExportDialog") + , m_bExportAsPackage(false) + , m_xExportAsPackageButton(m_xBuilder->weld_radio_button("extension")) + , m_xOKButton(m_xBuilder->weld_button("ok")) +{ + m_xExportAsPackageButton->set_active(true); + m_xOKButton->connect_clicked(LINK(this, ExportDialog, OkButtonHandler)); +} + +ExportDialog::~ExportDialog() +{ +} + +// LibPage +LibPage::LibPage(weld::Container* pParent, OrganizeDialog* pDialog) + : OrganizePage(pParent, "modules/BasicIDE/ui/libpage.ui", "LibPage", pDialog) + , m_xBasicsBox(m_xBuilder->weld_combo_box("location")) + , m_xLibBox(m_xBuilder->weld_tree_view("library")) + , m_xEditButton(m_xBuilder->weld_button("edit")) + , m_xPasswordButton(m_xBuilder->weld_button("password")) + , m_xNewLibButton(m_xBuilder->weld_button("new")) + , m_xInsertLibButton(m_xBuilder->weld_button("import")) + , m_xExportButton(m_xBuilder->weld_button("export")) + , m_xDelButton(m_xBuilder->weld_button("delete")) + , m_aCurDocument(ScriptDocument::getApplicationScriptDocument()) + , m_eCurLocation(LIBRARY_LOCATION_UNKNOWN) +{ + Size aSize(m_xLibBox->get_approximate_digit_width() * 40, + m_xLibBox->get_height_rows(10)); + m_xLibBox->set_size_request(aSize.Width(), aSize.Height()); + + // tdf#93476 The libraries should be listed alphabetically + m_xLibBox->make_sorted(); + + m_xEditButton->connect_clicked( LINK( this, LibPage, ButtonHdl ) ); + m_xNewLibButton->connect_clicked( LINK( this, LibPage, ButtonHdl ) ); + m_xPasswordButton->connect_clicked( LINK( this, LibPage, ButtonHdl ) ); + m_xExportButton->connect_clicked( LINK( this, LibPage, ButtonHdl ) ); + m_xInsertLibButton->connect_clicked( LINK( this, LibPage, ButtonHdl ) ); + m_xDelButton->connect_clicked( LINK( this, LibPage, ButtonHdl ) ); + m_xLibBox->connect_changed( LINK( this, LibPage, TreeListHighlightHdl ) ); + + m_xBasicsBox->connect_changed( LINK( this, LibPage, BasicSelectHdl ) ); + + m_xLibBox->connect_editing(LINK(this, LibPage, EditingEntryHdl), + LINK(this, LibPage, EditedEntryHdl)); + + FillListBox(); + m_xBasicsBox->set_active(0); + SetCurLib(); + + CheckButtons(); +} + +IMPL_LINK(LibPage, EditingEntryHdl, const weld::TreeIter&, rIter, bool) +{ + // check, if Standard library + OUString aLibName = m_xLibBox->get_text(rIter, 0); + + if ( aLibName.equalsIgnoreAsciiCase( "Standard" ) ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_CANNOTCHANGENAMESTDLIB))); + xErrorBox->run(); + return false; + } + + // check, if library is readonly + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) && !xModLibContainer->isLibraryLink( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) && !xDlgLibContainer->isLibraryLink( aLibName ) ) ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_LIBISREADONLY))); + xErrorBox->run(); + return false; + } + + // i24094: Password verification necessary for renaming + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && !xModLibContainer->isLibraryLoaded( aLibName ) ) + { + bool bOK = true; + // check password + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aLibName ) && !xPasswd->isLibraryPasswordVerified( aLibName ) ) + { + OUString aPassword; + bOK = QueryPassword(m_pDialog->getDialog(), xModLibContainer, aLibName, aPassword); + } + if ( !bOK ) + return false; + } + + // TODO: check if library is reference/link + + return true; +} + +IMPL_LINK(LibPage, EditedEntryHdl, const IterString&, rIterString, bool) +{ + const weld::TreeIter& rIter = rIterString.first; + OUString sNewName = rIterString.second; + + bool bValid = sNewName.getLength() <= 30 && IsValidSbxName(sNewName); + OUString aOldName(m_xLibBox->get_text(rIter, 0)); + + if (bValid && aOldName != sNewName) + { + try + { + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + if ( xModLibContainer.is() ) + xModLibContainer->renameLibrary( aOldName, sNewName ); + + Reference< script::XLibraryContainer2 > xDlgLibContainer( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( xDlgLibContainer.is() ) + xDlgLibContainer->renameLibrary( aOldName, sNewName ); + + MarkDocumentModified( m_aCurDocument ); + if (SfxBindings* pBindings = GetBindingsPtr()) + { + pBindings->Invalidate( SID_BASICIDE_LIBSELECTOR ); + pBindings->Update( SID_BASICIDE_LIBSELECTOR ); + } + } + catch (const container::ElementExistException& ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED))); + xErrorBox->run(); + return false; + } + catch (const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + return false; + } + } + + if ( !bValid ) + { + OUString sWarning(sNewName.getLength() > 30 ? IDEResId(RID_STR_LIBNAMETOLONG) : IDEResId(RID_STR_BADSBXNAME)); + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, sWarning)); + xErrorBox->run(); + + } + + return bValid; +} + +LibPage::~LibPage() +{ + if (m_xBasicsBox) + { + const sal_Int32 nCount = m_xBasicsBox->get_count(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + DocumentEntry* pEntry = weld::fromId<DocumentEntry*>(m_xBasicsBox->get_id(i)); + delete pEntry; + } + } +} + +void LibPage::CheckButtons() +{ + std::unique_ptr<weld::TreeIter> xCur(m_xLibBox->make_iterator()); + if (!m_xLibBox->get_cursor(xCur.get())) + return; + + OUString aLibName = m_xLibBox->get_text(*xCur, 0); + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + + if ( m_eCurLocation == LIBRARY_LOCATION_SHARE ) + { + m_xPasswordButton->set_sensitive(false); + m_xNewLibButton->set_sensitive(false); + m_xInsertLibButton->set_sensitive(false); + m_xDelButton->set_sensitive(false); + } + else if ( aLibName.equalsIgnoreAsciiCase( "Standard" ) ) + { + m_xPasswordButton->set_sensitive(false); + m_xNewLibButton->set_sensitive(true); + m_xInsertLibButton->set_sensitive(true); + m_xExportButton->set_sensitive(false); + m_xDelButton->set_sensitive(false); + } + else if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) ) ) + { + m_xPasswordButton->set_sensitive(false); + m_xNewLibButton->set_sensitive(true); + m_xInsertLibButton->set_sensitive(true); + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) && !xModLibContainer->isLibraryLink( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) && !xDlgLibContainer->isLibraryLink( aLibName ) ) ) + m_xDelButton->set_sensitive(false); + else + m_xDelButton->set_sensitive(true); + } + else + { + if ( xModLibContainer.is() && !xModLibContainer->hasByName( aLibName ) ) + m_xPasswordButton->set_sensitive(false); + else + m_xPasswordButton->set_sensitive(true); + + m_xNewLibButton->set_sensitive(true); + m_xInsertLibButton->set_sensitive(true); + m_xExportButton->set_sensitive(true); + m_xDelButton->set_sensitive(true); + } +} + +void LibPage::ActivatePage() +{ + SetCurLib(); +} + +IMPL_LINK_NOARG(LibPage, TreeListHighlightHdl, weld::TreeView&, void) +{ + CheckButtons(); +} + +IMPL_LINK_NOARG( LibPage, BasicSelectHdl, weld::ComboBox&, void ) +{ + SetCurLib(); + CheckButtons(); +} + +IMPL_LINK( LibPage, ButtonHdl, weld::Button&, rButton, void ) +{ + if (&rButton == m_xEditButton.get()) + { + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs ); + SfxGetpApp()->ExecuteSlot( aRequest ); + + SfxUnoAnyItem aDocItem( SID_BASICIDE_ARG_DOCUMENT_MODEL, Any( m_aCurDocument.getDocumentOrNull() ) ); + + std::unique_ptr<weld::TreeIter> xCurEntry(m_xLibBox->make_iterator()); + if (!m_xLibBox->get_cursor(xCurEntry.get())) + return; + OUString aLibName(m_xLibBox->get_text(*xCurEntry, 0)); + SfxStringItem aLibNameItem( SID_BASICIDE_ARG_LIBNAME, aLibName ); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->ExecuteList( SID_BASICIDE_LIBSELECTED, + SfxCallMode::ASYNCHRON, { &aDocItem, &aLibNameItem }); + EndTabDialog(); + return; + } + else if (&rButton == m_xNewLibButton.get()) + NewLib(); + else if (&rButton == m_xInsertLibButton.get()) + InsertLib(); + else if (&rButton == m_xExportButton.get()) + Export(); + else if (&rButton == m_xDelButton.get()) + DeleteCurrent(); + else if (&rButton == m_xPasswordButton.get()) + { + std::unique_ptr<weld::TreeIter> xCurEntry(m_xLibBox->make_iterator()); + if (!m_xLibBox->get_cursor(xCurEntry.get())) + return; + OUString aLibName(m_xLibBox->get_text(*xCurEntry, 0)); + + // load module library (if not loaded) + Reference< script::XLibraryContainer > xModLibContainer = m_aCurDocument.getLibraryContainer( E_SCRIPTS ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && !xModLibContainer->isLibraryLoaded( aLibName ) ) + { + Shell* pShell = GetShell(); + if (pShell) + pShell->GetViewFrame().GetWindow().EnterWait(); + xModLibContainer->loadLibrary( aLibName ); + if (pShell) + pShell->GetViewFrame().GetWindow().LeaveWait(); + } + + // load dialog library (if not loaded) + Reference< script::XLibraryContainer > xDlgLibContainer = m_aCurDocument.getLibraryContainer( E_DIALOGS ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && !xDlgLibContainer->isLibraryLoaded( aLibName ) ) + { + Shell* pShell = GetShell(); + if (pShell) + pShell->GetViewFrame().GetWindow().EnterWait(); + xDlgLibContainer->loadLibrary( aLibName ); + if (pShell) + pShell->GetViewFrame().GetWindow().LeaveWait(); + } + + // check, if library is password protected + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() ) + { + bool const bProtected = xPasswd->isLibraryPasswordProtected( aLibName ); + + // change password dialog + SvxPasswordDialog aDlg(m_pDialog->getDialog(), !bProtected); + aDlg.SetCheckPasswordHdl(LINK(this, LibPage, CheckPasswordHdl)); + + if (aDlg.run() == RET_OK) + { + bool const bNewProtected = xPasswd->isLibraryPasswordProtected( aLibName ); + + if ( bNewProtected != bProtected ) + { + int nPos = m_xLibBox->get_iter_index_in_parent(*xCurEntry); + m_xLibBox->remove(*xCurEntry); + ImpInsertLibEntry(aLibName, nPos); + m_xLibBox->set_cursor(nPos); + } + + MarkDocumentModified( m_aCurDocument ); + } + } + } + } + CheckButtons(); +} + +IMPL_LINK( LibPage, CheckPasswordHdl, SvxPasswordDialog *, pDlg, bool ) +{ + bool bRet = false; + + std::unique_ptr<weld::TreeIter> xCurEntry(m_xLibBox->make_iterator()); + if (!m_xLibBox->get_cursor(xCurEntry.get())) + return bRet; + + OUString aLibName(m_xLibBox->get_text(*xCurEntry, 0)); + Reference< script::XLibraryContainerPassword > xPasswd( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + + if ( xPasswd.is() ) + { + try + { + OUString aOldPassword( pDlg->GetOldPassword() ); + OUString aNewPassword( pDlg->GetNewPassword() ); + xPasswd->changeLibraryPassword( aLibName, aOldPassword, aNewPassword ); + bRet = true; + } + catch (...) + { + } + } + + return bRet; +} + +void LibPage::NewLib() +{ + createLibImpl(m_pDialog->getDialog(), m_aCurDocument, m_xLibBox.get(), nullptr); +} + +void LibPage::InsertLib() +{ + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + // file open dialog + sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, m_pDialog->getDialog()); + aDlg.SetContext(sfx2::FileDialogHelper::BasicInsertLib); + const Reference <XFilePicker3>& xFP = aDlg.GetFilePicker(); + + xFP->setTitle(IDEResId(RID_STR_APPENDLIBS)); + + // filter + OUString aTitle(IDEResId(RID_STR_BASIC)); + xFP->appendFilter( aTitle, "*.sbl;*.xlc;*.xlb" // library files + ";*.sdw;*.sxw;*.odt" // text + ";*.vor;*.stw;*.ott" // text template + ";*.sgl;*.sxg;*.odm" // master document + ";*.oth" // html document template + ";*.sdc;*.sxc;*.ods" // spreadsheet + ";*.stc;*.ots" // spreadsheet template + ";*.sda;*.sxd;*.odg" // drawing + ";*.std;*.otg" // drawing template + ";*.sdd;*.sxi;*.odp" // presentation + ";*.sti;*.otp" // presentation template + ";*.sxm;*.odf" ); // formula + + OUString aLastFilter(GetExtraData()->GetAddLibFilter()); + if ( !aLastFilter.isEmpty() ) + xFP->setCurrentFilter( aLastFilter ); + else + xFP->setCurrentFilter( IDEResId(RID_STR_BASIC) ); + + if ( xFP->execute() != RET_OK ) + return; + + GetExtraData()->SetAddLibPath( xFP->getDisplayDirectory() ); + GetExtraData()->SetAddLibFilter( xFP->getCurrentFilter() ); + + // library containers for import + Reference< script::XLibraryContainer2 > xModLibContImport; + Reference< script::XLibraryContainer2 > xDlgLibContImport; + + // file URLs + Sequence< OUString > aFiles = xFP->getSelectedFiles(); + INetURLObject aURLObj( aFiles[0] ); + auto xModURLObj = std::make_shared<INetURLObject>(aURLObj); + auto xDlgURLObj = std::make_shared<INetURLObject>(aURLObj); + + OUString aBase = aURLObj.getBase(); + OUString aModBase( "script" ); + OUString aDlgBase( "dialog" ); + + if ( aBase == aModBase || aBase == aDlgBase ) + { + xModURLObj->setBase( aModBase ); + xDlgURLObj->setBase( aDlgBase ); + } + + Reference< XSimpleFileAccess3 > xSFA( SimpleFileAccess::create(comphelper::getProcessComponentContext()) ); + + OUString aModURL( xModURLObj->GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + if ( xSFA->exists( aModURL ) ) + { + xModLibContImport = script::DocumentScriptLibraryContainer::createWithURL(xContext, aModURL); + } + + OUString aDlgURL( xDlgURLObj->GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + if ( xSFA->exists( aDlgURL ) ) + { + xDlgLibContImport = script::DocumentDialogLibraryContainer::createWithURL(xContext, aDlgURL); + } + + if ( !xModLibContImport.is() && !xDlgLibContImport.is() ) + return; + + std::shared_ptr<LibDialog> xLibDlg; + + Sequence< OUString > aLibNames = GetMergedLibraryNames( xModLibContImport, xDlgLibContImport ); + sal_Int32 nLibCount = aLibNames.getLength(); + if (nLibCount) + { + // library import dialog + xLibDlg = std::make_shared<LibDialog>(m_pDialog->getDialog()); + xLibDlg->SetStorageName(aURLObj.getName()); + weld::TreeView& rView = xLibDlg->GetLibBox(); + rView.make_unsorted(); + rView.freeze(); + + const OUString* pLibNames = aLibNames.getConstArray(); + for (sal_Int32 i = 0 ; i < nLibCount; ++i) + { + // libbox entries + OUString aLibName( pLibNames[ i ] ); + if ( !( ( xModLibContImport.is() && xModLibContImport->hasByName( aLibName ) && xModLibContImport->isLibraryLink( aLibName ) ) || + ( xDlgLibContImport.is() && xDlgLibContImport->hasByName( aLibName ) && xDlgLibContImport->isLibraryLink( aLibName ) ) ) ) + { + rView.append(); + const int nRow = rView.n_children() - 1; + rView.set_toggle(nRow, TRISTATE_TRUE); + rView.set_text(nRow, aLibName, 0); + } + } + + rView.thaw(); + rView.make_sorted(); + + if (rView.n_children()) + rView.set_cursor(0); + } + + if (!xLibDlg) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_NOLIBINSTORAGE))); + xErrorBox->run(); + return; + } + + OUString aExtension( aURLObj.getExtension() ); + OUString aLibExtension( "xlb" ); + OUString aContExtension( "xlc" ); + + // disable reference checkbox for documents and sbls + if ( aExtension != aLibExtension && aExtension != aContExtension ) + xLibDlg->EnableReference(false); + + weld::DialogController::runAsync(xLibDlg, [aContExtension, xDlgURLObj, aExtension, aLibExtension, xModURLObj, xLibDlg, xDlgLibContImport, xModLibContImport, this](sal_Int32 nResult) + { + if (!nResult ) + return; + + bool bChanges = false; + bool bRemove = false; + bool bReplace = xLibDlg->IsReplace(); + bool bReference = xLibDlg->IsReference(); + weld::TreeView& rView = xLibDlg->GetLibBox(); + for (int nLib = 0, nChildren = rView.n_children(); nLib < nChildren; ++nLib) + { + if (rView.get_toggle(nLib) == TRISTATE_TRUE) + { + OUString aLibName(rView.get_text(nLib)); + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + + // check, if the library is already existing + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) ) ) + { + if ( bReplace ) + { + // check, if the library is the Standard library + if ( aLibName == "Standard" ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_REPLACESTDLIB))); + xErrorBox->run(); + continue; + } + + // check, if the library is readonly and not a link + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) && !xModLibContainer->isLibraryLink( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) && !xDlgLibContainer->isLibraryLink( aLibName ) ) ) + { + OUString aErrStr( IDEResId(RID_STR_REPLACELIB) ); + aErrStr = aErrStr.replaceAll("XX", aLibName) + "\n" + IDEResId(RID_STR_LIBISREADONLY); + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, aErrStr)); + xErrorBox->run(); + continue; + } + + // remove existing libraries + bRemove = true; + } + else + { + OUString aErrStr; + if ( bReference ) + aErrStr = IDEResId(RID_STR_REFNOTPOSSIBLE); + else + aErrStr = IDEResId(RID_STR_IMPORTNOTPOSSIBLE); + aErrStr = aErrStr.replaceAll("XX", aLibName) + "\n" +IDEResId(RID_STR_SBXNAMEALLREADYUSED); + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, aErrStr)); + xErrorBox->run(); + continue; + } + } + + // check, if the library is password protected + bool bOK = false; + OUString aPassword; + if ( xModLibContImport.is() && xModLibContImport->hasByName( aLibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContImport, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aLibName ) && !xPasswd->isLibraryPasswordVerified( aLibName ) && !bReference ) + { + bOK = QueryPassword(m_pDialog->getDialog(), xModLibContImport, aLibName, aPassword, true, true); + + if ( !bOK ) + { + OUString aErrStr( IDEResId(RID_STR_NOIMPORT) ); + aErrStr = aErrStr.replaceAll("XX", aLibName); + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, aErrStr)); + xErrorBox->run(); + continue; + } + } + } + + // remove existing libraries + if ( bRemove ) + { + // remove listbox entry + int nEntry_ = FindEntry(*m_xLibBox, aLibName); + if (nEntry_ != -1) + m_xLibBox->remove(nEntry_); + + // remove module library + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) + xModLibContainer->removeLibrary( aLibName ); + + // remove dialog library + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) ) + xDlgLibContainer->removeLibrary( aLibName ); + } + + // copy module library + if ( xModLibContImport.is() && xModLibContImport->hasByName( aLibName ) && xModLibContainer.is() && !xModLibContainer->hasByName( aLibName ) ) + { + Reference< container::XNameContainer > xModLib; + if ( bReference ) + { + // storage URL + INetURLObject aModStorageURLObj(*xModURLObj); + if ( aExtension == aContExtension ) + { + sal_Int32 nCount = aModStorageURLObj.getSegmentCount(); + aModStorageURLObj.insertName( aLibName, false, nCount-1 ); + aModStorageURLObj.setExtension( aLibExtension ); + aModStorageURLObj.setFinalSlash(); + } + OUString aModStorageURL( aModStorageURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + // create library link + xModLib.set( xModLibContainer->createLibraryLink( aLibName, aModStorageURL, true ), UNO_QUERY); + } + else + { + // create library + xModLib = xModLibContainer->createLibrary( aLibName ); + if ( xModLib.is() ) + { + // get import library + Reference< container::XNameContainer > xModLibImport; + Any aElement = xModLibContImport->getByName( aLibName ); + aElement >>= xModLibImport; + + if ( xModLibImport.is() ) + { + // load library + if ( !xModLibContImport->isLibraryLoaded( aLibName ) ) + xModLibContImport->loadLibrary( aLibName ); + + // copy all modules + Sequence< OUString > aModNames = xModLibImport->getElementNames(); + sal_Int32 nModCount = aModNames.getLength(); + const OUString* pModNames = aModNames.getConstArray(); + for ( sal_Int32 i = 0 ; i < nModCount ; i++ ) + { + OUString aModName( pModNames[ i ] ); + Any aElement_ = xModLibImport->getByName( aModName ); + xModLib->insertByName( aModName, aElement_ ); + } + + // set password + if ( bOK ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() ) + { + try + { + xPasswd->changeLibraryPassword( aLibName, OUString(), aPassword ); + } + catch (...) + { + } + } + } + } + } + } + } + + // copy dialog library + if ( xDlgLibContImport.is() && xDlgLibContImport->hasByName( aLibName ) && xDlgLibContainer.is() && !xDlgLibContainer->hasByName( aLibName ) ) + { + Reference< container::XNameContainer > xDlgLib; + if ( bReference ) + { + // storage URL + INetURLObject aDlgStorageURLObj( *xDlgURLObj ); + if ( aExtension == aContExtension ) + { + sal_Int32 nCount = aDlgStorageURLObj.getSegmentCount(); + aDlgStorageURLObj.insertName( aLibName, false, nCount - 1 ); + aDlgStorageURLObj.setExtension( aLibExtension ); + aDlgStorageURLObj.setFinalSlash(); + } + OUString aDlgStorageURL( aDlgStorageURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + // create library link + xDlgLib.set( xDlgLibContainer->createLibraryLink( aLibName, aDlgStorageURL, true ), UNO_QUERY); + } + else + { + // create library + xDlgLib = xDlgLibContainer->createLibrary( aLibName ); + if ( xDlgLib.is() ) + { + // get import library + Reference< container::XNameContainer > xDlgLibImport; + Any aElement = xDlgLibContImport->getByName( aLibName ); + aElement >>= xDlgLibImport; + + if ( xDlgLibImport.is() ) + { + // load library + if ( !xDlgLibContImport->isLibraryLoaded( aLibName ) ) + xDlgLibContImport->loadLibrary( aLibName ); + + // copy all dialogs + Sequence< OUString > aDlgNames = xDlgLibImport->getElementNames(); + sal_Int32 nDlgCount = aDlgNames.getLength(); + const OUString* pDlgNames = aDlgNames.getConstArray(); + for ( sal_Int32 i = 0 ; i < nDlgCount ; i++ ) + { + OUString aDlgName( pDlgNames[ i ] ); + Any aElement_ = xDlgLibImport->getByName( aDlgName ); + xDlgLib->insertByName( aDlgName, aElement_ ); + } + } + } + } + } + + // insert listbox entry + ImpInsertLibEntry( aLibName, m_xLibBox->n_children() ); + m_xLibBox->set_cursor( m_xLibBox->find_text(aLibName) ); + bChanges = true; + } + } + + if ( bChanges ) + MarkDocumentModified( m_aCurDocument ); + }); +} + +void LibPage::Export() +{ + std::unique_ptr<weld::TreeIter> xCurEntry(m_xLibBox->make_iterator()); + if (!m_xLibBox->get_cursor(xCurEntry.get())) + return; + OUString aLibName(m_xLibBox->get_text(*xCurEntry, 0)); + + // Password verification + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && !xModLibContainer->isLibraryLoaded( aLibName ) ) + { + bool bOK = true; + + // check password + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aLibName ) && !xPasswd->isLibraryPasswordVerified( aLibName ) ) + { + OUString aPassword; + bOK = QueryPassword(m_pDialog->getDialog(), xModLibContainer, aLibName, aPassword); + } + if ( !bOK ) + return; + } + + std::unique_ptr<ExportDialog> xNewDlg(new ExportDialog(m_pDialog->getDialog())); + if (xNewDlg->run() != RET_OK) + return; + + try + { + bool bExportAsPackage = xNewDlg->isExportAsPackage(); + //tdf#112063 ensure closing xNewDlg is not selected as + //parent of file dialog from ExportAs... + xNewDlg.reset(); + if (bExportAsPackage) + ExportAsPackage( aLibName ); + else + ExportAsBasic( aLibName ); + } + catch(const util::VetoException& ) // user canceled operation + { + } +} + +void LibPage::implExportLib( const OUString& aLibName, const OUString& aTargetURL, + const Reference< task::XInteractionHandler >& Handler ) +{ + Reference< script::XLibraryContainerExport > xModLibContainerExport + ( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainerExport > xDlgLibContainerExport + ( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( xModLibContainerExport.is() ) + xModLibContainerExport->exportLibrary( aLibName, aTargetURL, Handler ); + + if (!xDlgLibContainerExport.is()) + return; + Reference<container::XNameAccess> xNameAcc(xDlgLibContainerExport, UNO_QUERY); + if (!xNameAcc.is()) + return; + if (!xNameAcc->hasByName(aLibName)) + return; + xDlgLibContainerExport->exportLibrary(aLibName, aTargetURL, Handler); +} + +// Implementation XCommandEnvironment + +namespace { + +class OLibCommandEnvironment : public cppu::WeakImplHelper< XCommandEnvironment > +{ + Reference< task::XInteractionHandler > mxInteraction; + +public: + explicit OLibCommandEnvironment(const Reference<task::XInteractionHandler>& xInteraction) + : mxInteraction( xInteraction ) + {} + + // Methods + virtual Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() override; + virtual Reference< XProgressHandler > SAL_CALL getProgressHandler() override; +}; + +} + +Reference< task::XInteractionHandler > OLibCommandEnvironment::getInteractionHandler() +{ + return mxInteraction; +} + +Reference< XProgressHandler > OLibCommandEnvironment::getProgressHandler() +{ + Reference< XProgressHandler > xRet; + return xRet; +} + +void LibPage::ExportAsPackage( const OUString& aLibName ) +{ + // file open dialog + sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILESAVE_SIMPLE, FileDialogFlags::NONE, m_pDialog->getDialog()); + aDlg.SetContext(sfx2::FileDialogHelper::BasicExportPackage); + const Reference <XFilePicker3>& xFP = aDlg.GetFilePicker(); + + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< task::XInteractionHandler2 > xHandler( task::InteractionHandler::createWithParent(xContext, nullptr) ); + Reference< XSimpleFileAccess3 > xSFA = SimpleFileAccess::create(xContext); + + xFP->setTitle(IDEResId(RID_STR_EXPORTPACKAGE)); + + // filter + OUString aTitle(IDEResId(RID_STR_PACKAGE_BUNDLE)); + xFP->appendFilter( aTitle, "*.oxt" ); // library files + + xFP->setCurrentFilter( aTitle ); + + if ( xFP->execute() != RET_OK ) + return; + + GetExtraData()->SetAddLibPath(xFP->getDisplayDirectory()); + + Sequence< OUString > aFiles = xFP->getSelectedFiles(); + INetURLObject aURL( aFiles[0] ); + if( aURL.getExtension().isEmpty() ) + aURL.setExtension( u"oxt" ); + + OUString aPackageURL( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + OUString aTmpPath = SvtPathOptions().GetTempPath(); + INetURLObject aInetObj( aTmpPath ); + aInetObj.insertName( aLibName, true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + OUString aSourcePath = aInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( xSFA->exists( aSourcePath ) ) + xSFA->kill( aSourcePath ); + Reference< task::XInteractionHandler > xDummyHandler( new DummyInteractionHandler( xHandler ) ); + implExportLib( aLibName, aTmpPath, xDummyHandler ); + + Reference< XCommandEnvironment > xCmdEnv = new OLibCommandEnvironment(xHandler); + + ::ucbhelper::Content sourceContent( aSourcePath, xCmdEnv, comphelper::getProcessComponentContext() ); + + OUString destFolder = "vnd.sun.star.zip://" + + ::rtl::Uri::encode( aPackageURL, + rtl_UriCharClassRegName, + rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8 ) + + "/"; + + if( xSFA->exists( aPackageURL ) ) + xSFA->kill( aPackageURL ); + + ::ucbhelper::Content destFolderContent( destFolder, xCmdEnv, comphelper::getProcessComponentContext() ); + destFolderContent.transferContent( + sourceContent, ::ucbhelper::InsertOperation::Copy, + OUString(), NameClash::OVERWRITE ); + + INetURLObject aMetaInfInetObj( aTmpPath ); + aMetaInfInetObj.insertName( u"META-INF", + true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + OUString aMetaInfFolder = aMetaInfInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + if( xSFA->exists( aMetaInfFolder ) ) + xSFA->kill( aMetaInfFolder ); + xSFA->createFolder( aMetaInfFolder ); + + std::vector< Sequence<beans::PropertyValue> > manifest; + + OUString fullPath = aLibName + + "/" ; + auto attribs(::comphelper::InitPropertySequence({ + { "FullPath", Any(fullPath) }, + { "MediaType", Any(OUString("application/vnd.sun.star.basic-library")) } + })); + manifest.push_back( attribs ); + + // write into pipe: + Reference<packages::manifest::XManifestWriter> xManifestWriter = packages::manifest::ManifestWriter::create( xContext ); + Reference<io::XOutputStream> xPipe( io::Pipe::create( xContext ), UNO_QUERY_THROW ); + xManifestWriter->writeManifestSequence( + xPipe, Sequence< Sequence<beans::PropertyValue> >( + manifest.data(), manifest.size() ) ); + + aMetaInfInetObj.insertName( u"manifest.xml", + true, INetURLObject::LAST_SEGMENT, INetURLObject::EncodeMechanism::All ); + + // write buffered pipe data to content: + ::ucbhelper::Content manifestContent( aMetaInfInetObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xCmdEnv, comphelper::getProcessComponentContext() ); + manifestContent.writeStream( Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ), true ); + + ::ucbhelper::Content MetaInfContent( aMetaInfFolder, xCmdEnv, comphelper::getProcessComponentContext() ); + destFolderContent.transferContent( + MetaInfContent, ::ucbhelper::InsertOperation::Copy, + OUString(), NameClash::OVERWRITE ); + + if( xSFA->exists( aSourcePath ) ) + xSFA->kill( aSourcePath ); + if( xSFA->exists( aMetaInfFolder ) ) + xSFA->kill( aMetaInfFolder ); +} + +void LibPage::ExportAsBasic( const OUString& aLibName ) +{ + // Folder picker + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XFolderPicker2 > xFolderPicker = sfx2::createFolderPicker(xContext, m_pDialog->getDialog()); + Reference< task::XInteractionHandler2 > xHandler( task::InteractionHandler::createWithParent(xContext, nullptr) ); + + xFolderPicker->setTitle(IDEResId(RID_STR_EXPORTBASIC)); + + // set display directory and filter + OUString aPath =GetExtraData()->GetAddLibPath(); + if( aPath.isEmpty() ) + aPath = SvtPathOptions().GetWorkPath(); + + // INetURLObject aURL(m_sSavePath, INetProtocol::File); + xFolderPicker->setDisplayDirectory( aPath ); + short nRet = xFolderPicker->execute(); + if( nRet == RET_OK ) + { + OUString aTargetURL = xFolderPicker->getDirectory(); + GetExtraData()->SetAddLibPath(aTargetURL); + + Reference< task::XInteractionHandler > xDummyHandler( new DummyInteractionHandler( xHandler ) ); + implExportLib( aLibName, aTargetURL, xDummyHandler ); + } +} + +void LibPage::DeleteCurrent() +{ + std::unique_ptr<weld::TreeIter> xCurEntry(m_xLibBox->make_iterator()); + if (!m_xLibBox->get_cursor(xCurEntry.get())) + return; + OUString aLibName(m_xLibBox->get_text(*xCurEntry, 0)); + + // check, if library is link + bool bIsLibraryLink = false; + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( m_aCurDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryLink( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryLink( aLibName ) ) ) + { + bIsLibraryLink = true; + } + + if (!QueryDelLib(aLibName, bIsLibraryLink, m_pDialog->getDialog())) + return; + + // inform BasicIDE + SfxUnoAnyItem aDocItem( SID_BASICIDE_ARG_DOCUMENT_MODEL, Any( m_aCurDocument.getDocumentOrNull() ) ); + SfxStringItem aLibNameItem( SID_BASICIDE_ARG_LIBNAME, aLibName ); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->ExecuteList(SID_BASICIDE_LIBREMOVED, + SfxCallMode::SYNCHRON, { &aDocItem, &aLibNameItem }); + + // remove library from module and dialog library containers + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) ) + xModLibContainer->removeLibrary( aLibName ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) ) + xDlgLibContainer->removeLibrary( aLibName ); + + m_xLibBox->remove(*xCurEntry); + MarkDocumentModified( m_aCurDocument ); +} + +void LibPage::EndTabDialog() +{ + m_pDialog->response(RET_OK); +} + +void LibPage::FillListBox() +{ + InsertListBoxEntry( ScriptDocument::getApplicationScriptDocument(), LIBRARY_LOCATION_USER ); + InsertListBoxEntry( ScriptDocument::getApplicationScriptDocument(), LIBRARY_LOCATION_SHARE ); + + ScriptDocuments aDocuments( ScriptDocument::getAllScriptDocuments( ScriptDocument::DocumentsSorted ) ); + for (auto const& doc : aDocuments) + { + InsertListBoxEntry( doc, LIBRARY_LOCATION_DOCUMENT ); + } +} + +void LibPage::InsertListBoxEntry( const ScriptDocument& rDocument, LibraryLocation eLocation ) +{ + OUString aEntryText(rDocument.getTitle(eLocation)); + OUString sId(weld::toId(new DocumentEntry(rDocument, eLocation))); + m_xBasicsBox->append(sId, aEntryText); +} + +void LibPage::SetCurLib() +{ + DocumentEntry* pEntry = weld::fromId<DocumentEntry*>(m_xBasicsBox->get_active_id()); + if (!pEntry) + return; + + const ScriptDocument& aDocument( pEntry->GetDocument() ); + DBG_ASSERT( aDocument.isAlive(), "LibPage::SetCurLib: no document, or document is dead!" ); + if ( !aDocument.isAlive() ) + return; + LibraryLocation eLocation = pEntry->GetLocation(); + if ( aDocument == m_aCurDocument && eLocation == m_eCurLocation ) + return; + + m_aCurDocument = aDocument; + m_eCurLocation = eLocation; + m_xLibBox->clear(); + + // get a sorted list of library names + Sequence< OUString > aLibNames = aDocument.getLibraryNames(); + sal_Int32 nLibCount = aLibNames.getLength(); + const OUString* pLibNames = aLibNames.getConstArray(); + + int nEntry = 0; + for (int i = 0 ; i < nLibCount; ++i) + { + OUString aLibName(pLibNames[i]); + if (eLocation == aDocument.getLibraryLocation(aLibName)) + ImpInsertLibEntry(aLibName, nEntry++); + } + + int nEntry_ = FindEntry(*m_xLibBox, u"Standard"); + if (nEntry_ == -1 && m_xLibBox->n_children()) + nEntry_ = 0; + m_xLibBox->set_cursor(nEntry_); +} + +void LibPage::ImpInsertLibEntry( const OUString& rLibName, int nPos ) +{ + // check, if library is password protected + bool bProtected = false; + Reference< script::XLibraryContainer2 > xModLibContainer( m_aCurDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( rLibName ) ) + { + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() ) + { + bProtected = xPasswd->isLibraryPasswordProtected( rLibName ); + } + } + + m_xLibBox->insert_text(nPos, rLibName); + + if (bProtected) + m_xLibBox->set_image(nPos, RID_BMP_LOCKED); + + // check, if library is link + if ( xModLibContainer.is() && xModLibContainer->hasByName( rLibName ) && xModLibContainer->isLibraryLink( rLibName ) ) + { + OUString aLinkURL = xModLibContainer->getLibraryLinkURL( rLibName ); + m_xLibBox->set_text(nPos, aLinkURL, 1); + } +} + +// Helper function +void createLibImpl(weld::Window* pWin, const ScriptDocument& rDocument, + weld::TreeView* pLibBox, SbTreeListBox* pBasicBox) +{ + OSL_ENSURE( rDocument.isAlive(), "createLibImpl: invalid document!" ); + if ( !rDocument.isAlive() ) + return; + + // create library name + OUString aLibName; + bool bValid = false; + sal_Int32 i = 1; + while ( !bValid ) + { + aLibName = "Library" + OUString::number( i ); + if ( !rDocument.hasLibrary( E_SCRIPTS, aLibName ) && !rDocument.hasLibrary( E_DIALOGS, aLibName ) ) + bValid = true; + i++; + } + + NewObjectDialog aNewDlg(pWin, ObjectMode::Library); + aNewDlg.SetObjectName(aLibName); + + if (!aNewDlg.run()) + return; + + if (!aNewDlg.GetObjectName().isEmpty()) + aLibName = aNewDlg.GetObjectName(); + + if ( aLibName.getLength() > 30 ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pWin, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_LIBNAMETOLONG))); + xErrorBox->run(); + } + else if ( !IsValidSbxName( aLibName ) ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pWin, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xErrorBox->run(); + } + else if ( rDocument.hasLibrary( E_SCRIPTS, aLibName ) || rDocument.hasLibrary( E_DIALOGS, aLibName ) ) + { + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pWin, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2))); + xErrorBox->run(); + } + else + { + try + { + // create module and dialog library + rDocument.getOrCreateLibrary( E_SCRIPTS, aLibName ); + rDocument.getOrCreateLibrary( E_DIALOGS, aLibName ); + + if( pLibBox ) + { + pLibBox->append_text(aLibName); + pLibBox->set_cursor(pLibBox->find_text(aLibName)); + } + + // create a module + OUString aModName = rDocument.createObjectName( E_SCRIPTS, aLibName ); + OUString sModuleCode; + if ( !rDocument.createModule( aLibName, aModName, true, sModuleCode ) ) + throw Exception("could not create module " + aModName, nullptr); + + // tdf#151741 - store all libraries to the file system, otherwise they + // cannot be renamed/moved since the SfxLibraryContainer::renameLibrary + // moves the folders/files on the file system + Reference<script::XLibraryContainer2> xModLibContainer( + rDocument.getLibraryContainer(E_SCRIPTS), UNO_QUERY); + Reference<script::XLibraryContainer2> xDlgLibContainer( + rDocument.getLibraryContainer(E_DIALOGS), UNO_QUERY); + Reference<script::XPersistentLibraryContainer> xModPersLibContainer(xModLibContainer, + UNO_QUERY); + if (xModPersLibContainer.is()) + xModPersLibContainer->storeLibraries(); + Reference<script::XPersistentLibraryContainer> xDlgPersLibContainer(xDlgLibContainer, + UNO_QUERY); + if (xDlgPersLibContainer.is()) + xDlgPersLibContainer->storeLibraries(); + + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, rDocument, aLibName, aModName, TYPE_MODULE ); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->ExecuteList(SID_BASICIDE_SBXINSERTED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + + if( pBasicBox ) + { + std::unique_ptr<weld::TreeIter> xIter(pBasicBox->make_iterator(nullptr)); + bool bValidIter = pBasicBox->get_cursor(xIter.get()); + std::unique_ptr<weld::TreeIter> xRootEntry(pBasicBox->make_iterator(xIter.get())); + while (bValidIter) + { + pBasicBox->copy_iterator(*xIter, *xRootEntry); + bValidIter = pBasicBox->iter_parent(*xIter); + } + + BrowseMode nMode = pBasicBox->GetMode(); + bool bDlgMode = ( nMode & BrowseMode::Dialogs ) && !( nMode & BrowseMode::Modules ); + const auto sId = bDlgMode ? RID_BMP_DLGLIB : RID_BMP_MODLIB; + pBasicBox->AddEntry(aLibName, sId, xRootEntry.get(), false, std::make_unique<Entry>(OBJ_TYPE_LIBRARY)); + pBasicBox->AddEntry(aModName, RID_BMP_MODULE, xRootEntry.get(), false, std::make_unique<Entry>(OBJ_TYPE_MODULE)); + pBasicBox->set_cursor(*xRootEntry); + pBasicBox->select(*xRootEntry); + } + } + catch (const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/moduldlg.cxx b/basctl/source/basicide/moduldlg.cxx new file mode 100644 index 0000000000..4b67e320b5 --- /dev/null +++ b/basctl/source/basicide/moduldlg.cxx @@ -0,0 +1,1052 @@ +/* -*- 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 <sal/config.h> + +#include <strings.hrc> +#include <iderid.hxx> +#include <bitmaps.hlst> + +#include "moduldlg.hxx" +#include <localizationmgr.hxx> +#include <basidesh.hxx> +#include <basobj.hxx> + +#include <basic/basmgr.hxx> +#include <com/sun/star/script/XLibraryContainerPassword.hpp> +#include <com/sun/star/script/XLibraryContainer2.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <comphelper/processfactory.hxx> +#include <sfx2/app.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/frame.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/stritem.hxx> +#include <vcl/transfer.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <xmlscript/xmldlg_imexp.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::resource; + +IMPL_LINK(ObjectPage, EditingEntryHdl, const weld::TreeIter&, rEntry, bool) +{ + bool bRet = false; + + sal_uInt16 nDepth = m_xBasicBox->get_iter_depth(rEntry); + if (nDepth >= 2) + { + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(&rEntry); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + const OUString& aLibName( aDesc.GetLibName() ); + Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( !( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) ) ) ) + { + // allow editing only for libraries, which are not readonly + bRet = true; + } + } + + return bRet; +} + +IMPL_LINK(ObjectPage, EditedEntryHdl, const IterString&, rIterString, bool) +{ + const weld::TreeIter& rEntry = rIterString.first; + OUString sNewText = rIterString.second; + + if ( !IsValidSbxName(sNewText) ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); + xError->run(); + return false; + } + + OUString aCurText(m_xBasicBox->get_text(rEntry)); + if ( aCurText == sNewText ) + // nothing to do + return true; + + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(&rEntry); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + DBG_ASSERT( aDocument.isValid(), "ExtTreeListBox::EditedEntry: no document!" ); + if ( !aDocument.isValid() ) + return false; + const OUString& aLibName( aDesc.GetLibName() ); + EntryType eType = aDesc.GetType(); + + bool bSuccess = eType == OBJ_TYPE_MODULE ? + RenameModule(m_pDialog->getDialog(), aDocument, aLibName, aCurText, sNewText) : + RenameDialog(m_pDialog->getDialog(), aDocument, aLibName, aCurText, sNewText); + + if ( !bSuccess ) + return false; + + MarkDocumentModified( aDocument ); + + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + SbxItem aSbxItem(SID_BASICIDE_ARG_SBX, aDocument, aLibName, sNewText, SbTreeListBox::ConvertType(eType)); + pDispatcher->ExecuteList( SID_BASICIDE_SBXRENAMED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + + // OV-Bug?! + m_xBasicBox->set_text(rEntry, sNewText); + m_xBasicBox->set_cursor(rEntry); + m_xBasicBox->unselect(rEntry); + m_xBasicBox->select(rEntry); // so that handler is called => update edit + + return true; +} + +void Shell::CopyDialogResources( + Reference< io::XInputStreamProvider >& io_xISP, + ScriptDocument const& rSourceDoc, + OUString const& rSourceLibName, + ScriptDocument const& rDestDoc, + OUString const& rDestLibName, + std::u16string_view rDlgName +) +{ + if ( !io_xISP.is() ) + return; + + // Get StringResourceManager + Reference< container::XNameContainer > xSourceDialogLib( rSourceDoc.getLibrary( E_DIALOGS, rSourceLibName, true ) ); + Reference< XStringResourceManager > xSourceMgr = + LocalizationMgr::getStringResourceFromDialogLibrary( xSourceDialogLib ); + if( !xSourceMgr.is() ) + return; + bool bSourceLocalized = xSourceMgr->getLocales().hasElements(); + + Reference< container::XNameContainer > xDestDialogLib( rDestDoc.getLibrary( E_DIALOGS, rDestLibName, true ) ); + Reference< XStringResourceManager > xDestMgr = + LocalizationMgr::getStringResourceFromDialogLibrary( xDestDialogLib ); + if( !xDestMgr.is() ) + return; + bool bDestLocalized = xDestMgr->getLocales().hasElements(); + + if( !bSourceLocalized && !bDestLocalized ) + return; + + // create dialog model + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + Reference< container::XNameContainer > xDialogModel( xContext->getServiceManager()->createInstanceWithContext + ( "com.sun.star.awt.UnoControlDialogModel", xContext ), UNO_QUERY ); + Reference< io::XInputStream > xInput( io_xISP->createInputStream() ); + ::xmlscript::importDialogModel( xInput, xDialogModel, xContext, rSourceDoc.isDocument() ? rSourceDoc.getDocument() : Reference< frame::XModel >() ); + + if( !xDialogModel.is() ) + return; + + if( bSourceLocalized && bDestLocalized ) + { + LocalizationMgr::copyResourceForDroppedDialog( xDialogModel, rDlgName, xDestMgr, xSourceMgr ); + } + else if( bSourceLocalized ) + { + LocalizationMgr::resetResourceForDialog( xDialogModel, xSourceMgr ); + } + else if( bDestLocalized ) + { + LocalizationMgr::setResourceIDsForDialog( xDialogModel, xDestMgr ); + } + io_xISP = ::xmlscript::exportDialogModel( xDialogModel, xContext, rDestDoc.isDocument() ? rDestDoc.getDocument() : Reference< frame::XModel >() ); +} + +void OrganizeDialog::SetCurrentEntry(const css::uno::Reference<css::frame::XFrame>& xDocFrame) +{ + if (!xDocFrame) + return; + Reference<css::frame::XController> xController(xDocFrame->getController()); + if (!xController) + return; + Reference<css::frame::XModel> xModel(xController->getModel()); + if (!xModel) + return; + ScriptDocument aScriptDocument(xModel); + EntryDescriptor aDesc(aScriptDocument, LIBRARY_LOCATION_DOCUMENT, OUString(), OUString(), OUString(), OBJ_TYPE_DOCUMENT); + m_xModulePage->SetCurrentEntry(aDesc); + m_xDialogPage->SetCurrentEntry(aDesc); +} + +// OrganizeDialog +OrganizeDialog::OrganizeDialog(weld::Window* pParent, const css::uno::Reference<css::frame::XFrame>& xDocFrame, sal_Int16 tabId) + : GenericDialogController(pParent, "modules/BasicIDE/ui/organizedialog.ui", "OrganizeDialog") + , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , m_xModulePage(new ObjectPage(m_xTabCtrl->get_page("modules"), "ModulePage", BrowseMode::Modules, this)) + , m_xDialogPage(new ObjectPage(m_xTabCtrl->get_page("dialogs"), "DialogPage", BrowseMode::Dialogs, this)) + , m_xLibPage(new LibPage(m_xTabCtrl->get_page("libraries"), this)) +{ + m_xTabCtrl->connect_enter_page(LINK(this, OrganizeDialog, ActivatePageHdl)); + + SetCurrentEntry(xDocFrame); + + OUString sPage; + if (tabId == 0) + sPage = "modules"; + else if (tabId == 1) + sPage = "dialogs"; + else + sPage = "libraries"; + m_xTabCtrl->set_current_page(sPage); + ActivatePageHdl(sPage); + + if (SfxDispatcher* pDispatcher = GetDispatcher()) + pDispatcher->Execute( SID_BASICIDE_STOREALLMODULESOURCES ); +} + +IMPL_LINK(OrganizeDialog, ActivatePageHdl, const OUString&, rPage, void) +{ + if (rPage == "modules") + m_xModulePage->ActivatePage(); + else if (rPage == "dialogs") + m_xDialogPage->ActivatePage(); + else if (rPage == "libraries") + m_xLibPage->ActivatePage(); +} + +OrganizeDialog::~OrganizeDialog() +{ +} + +OrganizePage::OrganizePage(weld::Container* pParent, const OUString& rUIFile, const OUString &rName, OrganizeDialog* pDialog) + : m_pDialog(pDialog) + , m_xBuilder(Application::CreateBuilder(pParent, rUIFile)) + , m_xContainer(m_xBuilder->weld_container(rName)) +{ +} + +OrganizePage::~OrganizePage() +{ +} + +class SbTreeListBoxDropTarget : public DropTargetHelper +{ +private: + SbTreeListBox& m_rTreeView; + + virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override + { + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true); + + weld::TreeView* pSource = rWidget.get_drag_source(); + if (!pSource) + return DND_ACTION_NONE; + + // tdf#145722 only return a DND_ACTION_MOVE possibility if that + // is requested as an option + const bool bCheckForMove = rEvt.mnAction & DND_ACTION_MOVE; + + sal_Int8 nMode = DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xEntry(pSource->make_iterator()); + if (pSource->get_selected(xEntry.get())) + { + sal_uInt16 nDepth = pSource->get_iter_depth(*xEntry); + if (nDepth >= 2) + { + nMode = DND_ACTION_COPY; + if (bCheckForMove) + { + EntryDescriptor aDesc = m_rTreeView.GetEntryDescriptor(xEntry.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + const OUString& aLibName( aDesc.GetLibName() ); + // allow MOVE mode only for libraries, which are not readonly + Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( !( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) ) ) ) + { + // Only allow copy for localized libraries + bool bAllowMove = true; + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) ) + { + // Get StringResourceManager + Reference< container::XNameContainer > xDialogLib( aDocument.getLibrary( E_DIALOGS, aLibName, true ) ); + Reference< XStringResourceManager > xSourceMgr = + LocalizationMgr::getStringResourceFromDialogLibrary( xDialogLib ); + if( xSourceMgr.is() ) + bAllowMove = ( xSourceMgr->getLocales().getLength() == 0 ); + } + if( bAllowMove ) + nMode |= DND_ACTION_MOVE; + } + } + } + } + return nMode; + } + + virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override + { + weld::TreeView& rWidget = m_rTreeView.get_widget(); + weld::TreeView* pSource = rWidget.get_drag_source(); + if (!pSource) + return DND_ACTION_NONE; + + std::unique_ptr<weld::TreeIter> xEntry(rWidget.make_iterator()); + bool bEntry = rWidget.get_dest_row_at_pos(rEvt.maPosPixel, xEntry.get(), true); + + // don't drop on a BasicManager (nDepth == 0) + sal_uInt16 nDepth = bEntry ? m_rTreeView.get_iter_depth(*xEntry) : 0; + bool bValid = nDepth != 0; + // don't drop in the same library + std::unique_ptr<weld::TreeIter> xSelected(pSource->make_iterator()); + bool bSelected = pSource->get_selected(xSelected.get()); + if (!bSelected) + bValid = false; + else if (nDepth == 1) + { + std::unique_ptr<weld::TreeIter> xSelParent(pSource->make_iterator(xSelected.get())); + if (pSource->iter_parent(*xSelParent) && pSource->iter_compare(*xEntry, *xSelParent) == 0) + bValid = false; + } + else if (nDepth == 2) + { + std::unique_ptr<weld::TreeIter> xParent(pSource->make_iterator(xEntry.get())); + std::unique_ptr<weld::TreeIter> xSelParent(pSource->make_iterator(xSelected.get())); + if (pSource->iter_parent(*xParent) && pSource->iter_parent(*xSelParent) && pSource->iter_compare(*xParent, *xSelParent) == 0) + bValid = false; + } + + // don't drop on a library, which is not loaded, readonly or password protected + // or which already has a module/dialog with this name + if ( bValid && ( nDepth > 0 ) ) + { + // get source module/dialog name + EntryDescriptor aSourceDesc = m_rTreeView.GetEntryDescriptor(xSelected.get()); + const OUString& aSourceName = aSourceDesc.GetName(); + EntryType eSourceType = aSourceDesc.GetType(); + + // get target shell and target library name + EntryDescriptor aDestDesc = m_rTreeView.GetEntryDescriptor(xEntry.get()); + ScriptDocument const& rDestDoc = aDestDesc.GetDocument(); + const OUString& aDestLibName = aDestDesc.GetLibName(); + + // check if module library is not loaded, readonly or password protected + Reference< script::XLibraryContainer2 > xModLibContainer( rDestDoc.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aDestLibName ) ) + { + if ( !xModLibContainer->isLibraryLoaded( aDestLibName ) ) + bValid = false; + + if ( xModLibContainer->isLibraryReadOnly( aDestLibName ) ) + bValid = false; + + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aDestLibName ) && !xPasswd->isLibraryPasswordVerified( aDestLibName ) ) + bValid = false; + } + + // check if dialog library is not loaded or readonly + Reference< script::XLibraryContainer2 > xDlgLibContainer( rDestDoc.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aDestLibName ) ) + { + if ( !xDlgLibContainer->isLibraryLoaded( aDestLibName ) ) + bValid = false; + + if ( xDlgLibContainer->isLibraryReadOnly( aDestLibName ) ) + bValid = false; + } + + // check, if module/dialog with this name is already existing in target library + if ( ( eSourceType == OBJ_TYPE_MODULE && rDestDoc.hasModule( aDestLibName, aSourceName ) ) || + ( eSourceType == OBJ_TYPE_DIALOG && rDestDoc.hasDialog( aDestLibName, aSourceName ) ) ) + { + bValid = false; + } + } + + if (bValid) + NotifyCopyingMoving(*xEntry, rEvt.mnAction & DND_ACTION_MOVE); + + return DND_ACTION_NONE; + } + + void NotifyCopyingMoving(const weld::TreeIter& rTarget, bool bMove) + { + sal_uInt16 nDepth = m_rTreeView.get_iter_depth(rTarget); + std::unique_ptr<weld::TreeIter> xNewParent(m_rTreeView.make_iterator(&rTarget)); + int nNewChildPos = 0; + DBG_ASSERT( nDepth, "Depth?" ); + if ( nDepth >= 2 ) + { + // Target = module/dialog => put module/dialog under the superordinate Basic + m_rTreeView.iter_parent(*xNewParent); + nNewChildPos = m_rTreeView.get_iter_index_in_parent(rTarget) + 1; + } + + // get target shell and target library name + EntryDescriptor aDestDesc = m_rTreeView.GetEntryDescriptor(xNewParent.get()); + const ScriptDocument& rDestDoc( aDestDesc.GetDocument() ); + const OUString& aDestLibName( aDestDesc.GetLibName() ); + + // get source shell, library name and module/dialog name + std::unique_ptr<weld::TreeIter> xSelected(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_selected(xSelected.get())) + return; + EntryDescriptor aSourceDesc = m_rTreeView.GetEntryDescriptor(xSelected.get()); + const ScriptDocument& rSourceDoc( aSourceDesc.GetDocument() ); + const OUString& aSourceLibName( aSourceDesc.GetLibName() ); + const OUString& aSourceName( aSourceDesc.GetName() ); + EntryType eType = aSourceDesc.GetType(); + + // get dispatcher + SfxDispatcher* pDispatcher = GetDispatcher(); + + if ( bMove ) // move + { + // remove source module/dialog window + if ( rSourceDoc != rDestDoc || aSourceLibName != aDestLibName ) + { + if( pDispatcher ) + { + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, rSourceDoc, aSourceLibName, aSourceName, SbTreeListBox::ConvertType(eType) ); + pDispatcher->ExecuteList( SID_BASICIDE_SBXDELETED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + } + + try + { + if ( eType == OBJ_TYPE_MODULE ) // module + { + // get module + OUString aModule; + if ( rSourceDoc.getModule( aSourceLibName, aSourceName, aModule ) ) + { + // remove module from source library + if ( rSourceDoc.removeModule( aSourceLibName, aSourceName ) ) + { + MarkDocumentModified( rSourceDoc ); + + // insert module into target library + if ( rDestDoc.insertModule( aDestLibName, aSourceName, aModule ) ) + MarkDocumentModified( rDestDoc ); + } + } + } + else if ( eType == OBJ_TYPE_DIALOG ) // dialog + { + // get dialog + Reference< io::XInputStreamProvider > xISP; + if ( rSourceDoc.getDialog( aSourceLibName, aSourceName, xISP ) ) + { + Shell::CopyDialogResources( xISP, rSourceDoc, + aSourceLibName, rDestDoc, aDestLibName, aSourceName ); + + // remove dialog from source library + if (RemoveDialog(rSourceDoc, aSourceLibName, aSourceName)) + { + MarkDocumentModified(rSourceDoc); + + // insert dialog into target library + if ( rDestDoc.insertDialog( aDestLibName, aSourceName, xISP ) ) + MarkDocumentModified(rDestDoc); + } + } + } + } + catch (const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + else // copy + { + try + { + if ( eType == OBJ_TYPE_MODULE ) // module + { + // get module + OUString aModule; + if ( rSourceDoc.getModule( aSourceLibName, aSourceName, aModule ) ) + { + // insert module into target library + if ( rDestDoc.insertModule( aDestLibName, aSourceName, aModule ) ) + MarkDocumentModified( rDestDoc ); + } + } + else if ( eType == OBJ_TYPE_DIALOG ) // dialog + { + // get dialog + Reference< io::XInputStreamProvider > xISP; + if ( rSourceDoc.getDialog( aSourceLibName, aSourceName, xISP ) ) + { + Shell::CopyDialogResources( xISP, rSourceDoc, + aSourceLibName, rDestDoc, aDestLibName, aSourceName ); + + // insert dialog into target library + if ( rDestDoc.insertDialog( aDestLibName, aSourceName, xISP ) ) + MarkDocumentModified( rDestDoc ); + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + + OUString sText(m_rTreeView.get_text(*xSelected)); + OUString sId(m_rTreeView.get_id(*xSelected)); + /// if copying then clone the userdata + if (Entry* pEntry = bMove ? nullptr : weld::fromId<Entry*>(sId)) + { + assert(pEntry->GetType() != OBJ_TYPE_DOCUMENT); + std::unique_ptr<Entry> xNewUserData(std::make_unique<Entry>(*pEntry)); + sId = weld::toId(xNewUserData.release()); + } + std::unique_ptr<weld::TreeIter> xRet(m_rTreeView.make_iterator()); + m_rTreeView.get_widget().insert(xNewParent.get(), nNewChildPos, &sText, &sId, nullptr, nullptr, false, xRet.get()); + if (eType == OBJ_TYPE_MODULE) + m_rTreeView.get_widget().set_image(*xRet, RID_BMP_MODULE); + else if (eType == OBJ_TYPE_DIALOG) + m_rTreeView.get_widget().set_image(*xRet, RID_BMP_DIALOG); + if (!m_rTreeView.get_row_expanded(*xNewParent)) + m_rTreeView.expand_row(*xNewParent); + m_rTreeView.select(*xRet); + + if (bMove) + m_rTreeView.remove(*xSelected); + + // create target module/dialog window + if ( rSourceDoc != rDestDoc || aSourceLibName != aDestLibName ) + { + if( pDispatcher ) + { + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, rDestDoc, aDestLibName, aSourceName, SbTreeListBox::ConvertType(eType) ); + pDispatcher->ExecuteList( SID_BASICIDE_SBXINSERTED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + } + } + +public: + SbTreeListBoxDropTarget(SbTreeListBox& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) + { + } +}; + +// ObjectPage +ObjectPage::ObjectPage(weld::Container* pParent, const OUString &rName, BrowseMode nMode, OrganizeDialog* pDialog) + : OrganizePage(pParent, "modules/BasicIDE/ui/" + rName.toAsciiLowerCase() + ".ui", + rName, pDialog) + , m_xBasicBox(new SbTreeListBox(m_xBuilder->weld_tree_view("library"), pDialog->getDialog())) + , m_xEditButton(m_xBuilder->weld_button("edit")) + , m_xNewModButton(m_xBuilder->weld_button("newmodule")) + , m_xNewDlgButton(m_xBuilder->weld_button("newdialog")) + , m_xDelButton(m_xBuilder->weld_button("delete")) +{ + Size aSize(m_xBasicBox->get_approximate_digit_width() * 40, + m_xBasicBox->get_height_rows(14)); + m_xBasicBox->set_size_request(aSize.Width(), aSize.Height()); + + // tdf#93476 The dialogs should be listed alphabetically + m_xBasicBox->make_sorted(); + + m_xEditButton->connect_clicked( LINK( this, ObjectPage, ButtonHdl ) ); + m_xDelButton->connect_clicked( LINK( this, ObjectPage, ButtonHdl ) ); + m_xBasicBox->connect_changed( LINK( this, ObjectPage, BasicBoxHighlightHdl ) ); + + if( nMode & BrowseMode::Modules ) + { + m_xNewModButton->connect_clicked( LINK( this, ObjectPage, ButtonHdl ) ); + m_xNewDlgButton->hide(); + } + else if ( nMode & BrowseMode::Dialogs ) + { + m_xNewDlgButton->connect_clicked( LINK( this, ObjectPage, ButtonHdl ) ); + m_xNewModButton->hide(); + } + + m_xDropTarget.reset(new SbTreeListBoxDropTarget(*m_xBasicBox)); + // tdf#145722 explicitly claim COPY and MOVE are options + rtl::Reference<TransferDataContainer> xHelper(new TransferDataContainer); + m_xBasicBox->get_widget().enable_drag_source(xHelper, DND_ACTION_COPYMOVE); + + m_xBasicBox->connect_editing(LINK(this, ObjectPage, EditingEntryHdl), + LINK(this, ObjectPage, EditedEntryHdl)); + + m_xBasicBox->SetMode( nMode ); + m_xBasicBox->ScanAllEntries(); + + m_xEditButton->grab_focus(); + CheckButtons(); +} + +ObjectPage::~ObjectPage() +{ +} + +void ObjectPage::ActivatePage() +{ + m_xBasicBox->UpdateEntries(); + CheckButtons(); +} + +void ObjectPage::CheckButtons() +{ + // enable/disable edit button + std::unique_ptr<weld::TreeIter> xCurEntry(m_xBasicBox->make_iterator()); + if (!m_xBasicBox->get_cursor(xCurEntry.get())) + xCurEntry.reset(); + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(xCurEntry.get()); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + const OUString& aLibName( aDesc.GetLibName() ); + const OUString& aLibSubName( aDesc.GetLibSubName() ); + bool bVBAEnabled = aDocument.isInVBAMode(); + BrowseMode nMode = m_xBasicBox->GetMode(); + + sal_uInt16 nDepth = xCurEntry ? m_xBasicBox->get_iter_depth(*xCurEntry) : 0; + if ( nDepth >= 2 ) + { + if( bVBAEnabled && ( nMode & BrowseMode::Modules ) && ( nDepth == 2 ) ) + m_xEditButton->set_sensitive(false); + else + m_xEditButton->set_sensitive(true); + } + else + m_xEditButton->set_sensitive(false); + + // enable/disable new module/dialog buttons + LibraryLocation eLocation( aDesc.GetLocation() ); + bool bReadOnly = false; + if ( nDepth > 0 ) + { + Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + Reference< script::XLibraryContainer2 > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY ); + if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && xModLibContainer->isLibraryReadOnly( aLibName ) ) || + ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && xDlgLibContainer->isLibraryReadOnly( aLibName ) ) ) + { + bReadOnly = true; + } + } + if ( bReadOnly || eLocation == LIBRARY_LOCATION_SHARE ) + { + m_xNewModButton->set_sensitive(false); + m_xNewDlgButton->set_sensitive(false); + } + else + { + m_xNewModButton->set_sensitive(true); + m_xNewDlgButton->set_sensitive(true); + } + + // enable/disable delete button + if ( nDepth >= 2 && !bReadOnly && eLocation != LIBRARY_LOCATION_SHARE ) + { + if( bVBAEnabled && ( nMode & BrowseMode::Modules ) && ( ( nDepth == 2 ) || aLibSubName == IDEResId(RID_STR_DOCUMENT_OBJECTS) ) ) + m_xDelButton->set_sensitive(false); + else + m_xDelButton->set_sensitive(true); + } + else + m_xDelButton->set_sensitive(false); +} + +IMPL_LINK_NOARG(ObjectPage, BasicBoxHighlightHdl, weld::TreeView&, void) +{ + CheckButtons(); +} + +IMPL_LINK(ObjectPage, ButtonHdl, weld::Button&, rButton, void) +{ + if (&rButton == m_xEditButton.get()) + { + SfxAllItemSet aArgs( SfxGetpApp()->GetPool() ); + SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs ); + SfxGetpApp()->ExecuteSlot( aRequest ); + + SfxDispatcher* pDispatcher = GetDispatcher(); + + std::unique_ptr<weld::TreeIter> xCurEntry(m_xBasicBox->make_iterator()); + if (!m_xBasicBox->get_cursor(xCurEntry.get())) + return; + if (m_xBasicBox->get_iter_depth(*xCurEntry) >= 2) + { + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(xCurEntry.get()); + if ( pDispatcher ) + { + OUString aModName( aDesc.GetName() ); + // extract the module name from the string like "Sheet1 (Example1)" + if( aDesc.GetLibSubName() == IDEResId(RID_STR_DOCUMENT_OBJECTS) ) + { + aModName = aModName.getToken( 0, ' ' ); + } + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, aDesc.GetDocument(), aDesc.GetLibName(), + aModName, SbTreeListBox::ConvertType( aDesc.GetType() ) ); + pDispatcher->ExecuteList(SID_BASICIDE_SHOWSBX, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + } + else // only Lib selected + { + DBG_ASSERT( m_xBasicBox->get_iter_depth(*xCurEntry) == 1, "No LibEntry?!" ); + ScriptDocument aDocument( ScriptDocument::getApplicationScriptDocument() ); + std::unique_ptr<weld::TreeIter> xParentEntry(m_xBasicBox->make_iterator(xCurEntry.get())); + if (m_xBasicBox->iter_parent(*xParentEntry)) + { + DocumentEntry* pDocumentEntry = weld::fromId<DocumentEntry*>(m_xBasicBox->get_id(*xParentEntry)); + if (pDocumentEntry) + aDocument = pDocumentEntry->GetDocument(); + } + SfxUnoAnyItem aDocItem( SID_BASICIDE_ARG_DOCUMENT_MODEL, Any( aDocument.getDocumentOrNull() ) ); + OUString aLibName(m_xBasicBox->get_text(*xCurEntry)); + SfxStringItem aLibNameItem( SID_BASICIDE_ARG_LIBNAME, aLibName ); + if ( pDispatcher ) + { + pDispatcher->ExecuteList(SID_BASICIDE_LIBSELECTED, + SfxCallMode::ASYNCHRON, { &aDocItem, &aLibNameItem }); + } + } + EndTabDialog(); + } + else if (&rButton == m_xNewModButton.get()) + NewModule(); + else if (&rButton == m_xNewDlgButton.get()) + NewDialog(); + else if (&rButton == m_xDelButton.get()) + DeleteCurrent(); +} + +bool ObjectPage::GetSelection( ScriptDocument& rDocument, OUString& rLibName ) +{ + bool bRet = false; + + std::unique_ptr<weld::TreeIter> xCurEntry(m_xBasicBox->make_iterator()); + if (!m_xBasicBox->get_cursor(xCurEntry.get())) + xCurEntry.reset(); + EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(xCurEntry.get()); + rDocument = aDesc.GetDocument(); + rLibName = aDesc.GetLibName(); + if ( rLibName.isEmpty() ) + rLibName = "Standard" ; + + DBG_ASSERT( rDocument.isAlive(), "ObjectPage::GetSelection: no or dead ScriptDocument in the selection!" ); + if ( !rDocument.isAlive() ) + return false; + + // check if the module library is loaded + bool bOK = true; + OUString aLibName( rLibName ); + Reference< script::XLibraryContainer > xModLibContainer( rDocument.getLibraryContainer( E_SCRIPTS ) ); + if ( xModLibContainer.is() && xModLibContainer->hasByName( aLibName ) && !xModLibContainer->isLibraryLoaded( aLibName ) ) + { + // check password + Reference< script::XLibraryContainerPassword > xPasswd( xModLibContainer, UNO_QUERY ); + if ( xPasswd.is() && xPasswd->isLibraryPasswordProtected( aLibName ) && !xPasswd->isLibraryPasswordVerified( aLibName ) ) + { + OUString aPassword; + bOK = QueryPassword(m_pDialog->getDialog(), xModLibContainer, rLibName, aPassword); + } + + // load library + if ( bOK ) + xModLibContainer->loadLibrary( aLibName ); + } + + // check if the dialog library is loaded + Reference< script::XLibraryContainer > xDlgLibContainer( rDocument.getLibraryContainer( E_DIALOGS ) ); + if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aLibName ) && !xDlgLibContainer->isLibraryLoaded( aLibName ) ) + { + // load library + if ( bOK ) + xDlgLibContainer->loadLibrary( aLibName ); + } + + if ( bOK ) + bRet = true; + + return bRet; +} + +void ObjectPage::NewModule() +{ + ScriptDocument aDocument( ScriptDocument::getApplicationScriptDocument() ); + OUString aLibName; + + if ( GetSelection( aDocument, aLibName ) ) + { + createModImpl(m_pDialog->getDialog(), aDocument, + *m_xBasicBox, aLibName, OUString(), true); + } +} + +void ObjectPage::NewDialog() +{ + ScriptDocument aDocument( ScriptDocument::getApplicationScriptDocument() ); + OUString aLibName; + + if ( !GetSelection( aDocument, aLibName ) ) + return; + + aDocument.getOrCreateLibrary( E_DIALOGS, aLibName ); + + NewObjectDialog aNewDlg(m_pDialog->getDialog(), ObjectMode::Dialog, true); + aNewDlg.SetObjectName(aDocument.createObjectName(E_DIALOGS, aLibName)); + + if (aNewDlg.run() == RET_CANCEL) + return; + + OUString aDlgName = aNewDlg.GetObjectName(); + if (aDlgName.isEmpty()) + aDlgName = aDocument.createObjectName( E_DIALOGS, aLibName); + + if ( aDocument.hasDialog( aLibName, aDlgName ) ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_pDialog->getDialog(), + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2))); + xError->run(); + } + else + { + Reference< io::XInputStreamProvider > xISP; + if ( !aDocument.createDialog( aLibName, aDlgName, xISP ) ) + return; + + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, aDocument, aLibName, aDlgName, TYPE_DIALOG ); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + pDispatcher->ExecuteList( SID_BASICIDE_SBXINSERTED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + LibraryLocation eLocation = aDocument.getLibraryLocation( aLibName ); + std::unique_ptr<weld::TreeIter> xIter(m_xBasicBox->make_iterator()); + bool bRootEntry = m_xBasicBox->FindRootEntry(aDocument, eLocation, *xIter); + if (bRootEntry) + { + if (!m_xBasicBox->get_row_expanded(*xIter)) + m_xBasicBox->expand_row(*xIter); + bool bLibEntry = m_xBasicBox->FindEntry(aLibName, OBJ_TYPE_LIBRARY, *xIter); + DBG_ASSERT( bLibEntry, "LibEntry not found!" ); + if (bLibEntry) + { + if (!m_xBasicBox->get_row_expanded(*xIter)) + m_xBasicBox->expand_row(*xIter); + std::unique_ptr<weld::TreeIter> xSubRootEntry(m_xBasicBox->make_iterator(xIter.get())); + bool bDlgEntry = m_xBasicBox->FindEntry(aDlgName, OBJ_TYPE_DIALOG, *xIter); + if (!bDlgEntry) + { + m_xBasicBox->AddEntry(aDlgName, RID_BMP_DIALOG, xSubRootEntry.get(), false, + std::make_unique<Entry>(OBJ_TYPE_DIALOG), xIter.get()); + assert(xIter && "Insert entry failed!"); + } + m_xBasicBox->set_cursor(*xIter); + m_xBasicBox->select(*xIter); + } + } + } +} + +void ObjectPage::DeleteCurrent() +{ + std::unique_ptr<weld::TreeIter> xCurEntry(m_xBasicBox->make_iterator()); + if (!m_xBasicBox->get_cursor(xCurEntry.get())) + xCurEntry.reset(); + DBG_ASSERT( xCurEntry, "No current entry!" ); + if (!xCurEntry) + return; + EntryDescriptor aDesc( m_xBasicBox->GetEntryDescriptor( xCurEntry.get() ) ); + const ScriptDocument& aDocument( aDesc.GetDocument() ); + DBG_ASSERT( aDocument.isAlive(), "ObjectPage::DeleteCurrent: no document!" ); + if ( !aDocument.isAlive() ) + return; + const OUString& aLibName( aDesc.GetLibName() ); + const OUString& aName( aDesc.GetName() ); + EntryType eType = aDesc.GetType(); + + if ( !(( eType == OBJ_TYPE_MODULE && QueryDelModule(aName, m_pDialog->getDialog()) ) || + ( eType == OBJ_TYPE_DIALOG && QueryDelDialog(aName, m_pDialog->getDialog()) )) ) + return; + + m_xBasicBox->remove(*xCurEntry); + if (m_xBasicBox->get_cursor(xCurEntry.get())) + m_xBasicBox->select(*xCurEntry); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, aDocument, aLibName, aName, SbTreeListBox::ConvertType( eType ) ); + pDispatcher->ExecuteList( SID_BASICIDE_SBXDELETED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + + try + { + bool bSuccess = false; + if ( eType == OBJ_TYPE_MODULE ) + bSuccess = aDocument.removeModule( aLibName, aName ); + else if ( eType == OBJ_TYPE_DIALOG ) + bSuccess = RemoveDialog( aDocument, aLibName, aName ); + + if ( bSuccess ) + MarkDocumentModified( aDocument ); + } + catch (const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } +} + +void ObjectPage::EndTabDialog() +{ + m_pDialog->response(RET_OK); +} + +LibDialog::LibDialog(weld::Window* pParent) + : GenericDialogController(pParent, "modules/BasicIDE/ui/importlibdialog.ui", "ImportLibDialog") + , m_xStorageFrame(m_xBuilder->weld_frame("storageframe")) + , m_xLibBox(m_xBuilder->weld_tree_view("entries")) + , m_xReferenceBox(m_xBuilder->weld_check_button("ref")) + , m_xReplaceBox(m_xBuilder->weld_check_button("replace")) +{ + m_xLibBox->set_size_request(m_xLibBox->get_approximate_digit_width() * 28, + m_xLibBox->get_height_rows(8)); + m_xLibBox->enable_toggle_buttons(weld::ColumnToggleType::Check); + // tdf#93476 The libraries should be listed alphabetically + m_xLibBox->make_sorted(); +} + +LibDialog::~LibDialog() +{ +} + +void LibDialog::SetStorageName( std::u16string_view rName ) +{ + OUString aName = IDEResId(RID_STR_FILENAME) + rName; + m_xStorageFrame->set_label(aName); +} + +// Helper function +SbModule* createModImpl(weld::Window* pWin, const ScriptDocument& rDocument, + SbTreeListBox& rBasicBox, const OUString& rLibName, const OUString& _aModName, bool bMain ) +{ + OSL_ENSURE( rDocument.isAlive(), "createModImpl: invalid document!" ); + if ( !rDocument.isAlive() ) + return nullptr; + + SbModule* pModule = nullptr; + + OUString aLibName( rLibName ); + if ( aLibName.isEmpty() ) + aLibName = "Standard" ; + rDocument.getOrCreateLibrary( E_SCRIPTS, aLibName ); + OUString aModName = _aModName; + if ( aModName.isEmpty() ) + aModName = rDocument.createObjectName( E_SCRIPTS, aLibName ); + + NewObjectDialog aNewDlg(pWin, ObjectMode::Module, true); + aNewDlg.SetObjectName(aModName); + + if (aNewDlg.run() != RET_CANCEL) + { + if (!aNewDlg.GetObjectName().isEmpty()) + aModName = aNewDlg.GetObjectName(); + + try + { + OUString sModuleCode; + // the module has existed + if( rDocument.hasModule( aLibName, aModName ) ) + return nullptr; + rDocument.createModule( aLibName, aModName, bMain, sModuleCode ); + BasicManager* pBasMgr = rDocument.getBasicManager(); + StarBASIC* pBasic = pBasMgr? pBasMgr->GetLib( aLibName ) : nullptr; + if ( pBasic ) + pModule = pBasic->FindModule( aModName ); + SbxItem aSbxItem( SID_BASICIDE_ARG_SBX, rDocument, aLibName, aModName, TYPE_MODULE ); + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + pDispatcher->ExecuteList( SID_BASICIDE_SBXINSERTED, + SfxCallMode::SYNCHRON, { &aSbxItem }); + } + LibraryLocation eLocation = rDocument.getLibraryLocation( aLibName ); + std::unique_ptr<weld::TreeIter> xIter(rBasicBox.make_iterator()); + bool bRootEntry = rBasicBox.FindRootEntry(rDocument, eLocation, *xIter); + if (bRootEntry) + { + if (!rBasicBox.get_row_expanded(*xIter)) + rBasicBox.expand_row(*xIter); + bool bLibEntry = rBasicBox.FindEntry(aLibName, OBJ_TYPE_LIBRARY, *xIter); + DBG_ASSERT( bLibEntry, "LibEntry not found!" ); + if (bLibEntry) + { + if (!rBasicBox.get_row_expanded(*xIter)) + rBasicBox.expand_row(*xIter); + std::unique_ptr<weld::TreeIter> xSubRootEntry(rBasicBox.make_iterator(xIter.get())); + if (pBasic && rDocument.isInVBAMode()) + { + // add the new module in the "Modules" entry + std::unique_ptr<weld::TreeIter> xLibSubEntry(rBasicBox.make_iterator(xIter.get())); + bool bLibSubEntry = rBasicBox.FindEntry(IDEResId(RID_STR_NORMAL_MODULES) , OBJ_TYPE_NORMAL_MODULES, *xLibSubEntry); + if (bLibSubEntry) + { + if (!rBasicBox.get_row_expanded(*xLibSubEntry)) + rBasicBox.expand_row(*xLibSubEntry); + rBasicBox.copy_iterator(*xLibSubEntry, *xSubRootEntry); + } + } + + std::unique_ptr<weld::TreeIter> xEntry(rBasicBox.make_iterator(xSubRootEntry.get())); + bool bEntry = rBasicBox.FindEntry(aModName, OBJ_TYPE_MODULE, *xEntry); + if (!bEntry) + { + rBasicBox.AddEntry(aModName, RID_BMP_MODULE, xSubRootEntry.get(), false, + std::make_unique<Entry>(OBJ_TYPE_MODULE), xEntry.get()); + } + rBasicBox.set_cursor(*xEntry); + rBasicBox.select(*xEntry); + } + } + } + catch (const container::ElementExistException& ) + { + std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pWin, + VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2))); + xError->run(); + } + catch (const container::NoSuchElementException& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + return pModule; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/moduldlg.hxx b/basctl/source/basicide/moduldlg.hxx new file mode 100644 index 0000000000..4f4cbcbda3 --- /dev/null +++ b/basctl/source/basicide/moduldlg.hxx @@ -0,0 +1,226 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include <bastype2.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/task/XInteractionHandler.hpp> + +class SvxPasswordDialog; + +namespace basctl +{ + +enum class ObjectMode +{ + Library = 1, + Module = 2, + Dialog = 3, +}; + +class NewObjectDialog : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Entry> m_xEdit; + std::unique_ptr<weld::Button> m_xOKButton; + bool m_bCheckName; + + DECL_LINK(OkButtonHandler, weld::Button&, void); +public: + NewObjectDialog(weld::Window* pParent, ObjectMode, bool bCheckName = false); + OUString GetObjectName() const { return m_xEdit->get_text(); } + void SetObjectName(const OUString& rName) + { + m_xEdit->set_text(rName); + m_xEdit->select_region(0, -1); + } +}; + +class GotoLineDialog : public weld::GenericDialogController +{ + std::unique_ptr<weld::Entry> m_xEdit; + std::unique_ptr<weld::Button> m_xOKButton; + DECL_LINK(OkButtonHandler, weld::Button&, void); +public: + explicit GotoLineDialog(weld::Window* pParent); + virtual ~GotoLineDialog() override; + sal_Int32 GetLineNumber() const; +}; + +class ExportDialog : public weld::GenericDialogController +{ +private: + bool m_bExportAsPackage; + + std::unique_ptr<weld::RadioButton> m_xExportAsPackageButton; + std::unique_ptr<weld::Button> m_xOKButton; + + DECL_LINK(OkButtonHandler, weld::Button&, void); + +public: + explicit ExportDialog(weld::Window * pParent); + virtual ~ExportDialog() override; + + bool isExportAsPackage () const { return m_bExportAsPackage; } +}; + +class LibDialog : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Frame> m_xStorageFrame; + std::unique_ptr<weld::TreeView> m_xLibBox; + std::unique_ptr<weld::CheckButton> m_xReferenceBox; + std::unique_ptr<weld::CheckButton> m_xReplaceBox; + +public: + explicit LibDialog(weld::Window* pParent); + virtual ~LibDialog() override; + + void SetStorageName( std::u16string_view rName ); + + weld::TreeView& GetLibBox() { return *m_xLibBox; } + bool IsReference() const { return m_xReferenceBox->get_active(); } + bool IsReplace() const { return m_xReplaceBox->get_active(); } + + void EnableReference (bool b) { m_xReferenceBox->set_sensitive(b); } +}; + +class OrganizeDialog; + +class OrganizePage +{ +protected: + OrganizeDialog* m_pDialog; + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + + OrganizePage(weld::Container* pParent, const OUString& rUIFile, const OUString &rName, OrganizeDialog* pDialog); + virtual ~OrganizePage(); + +public: + virtual void ActivatePage() = 0; +}; + +class SbTreeListBoxDropTarget; + +class ObjectPage final : public OrganizePage +{ + std::unique_ptr<SbTreeListBox> m_xBasicBox; + std::unique_ptr<weld::Button> m_xEditButton; + std::unique_ptr<weld::Button> m_xNewModButton; + std::unique_ptr<weld::Button> m_xNewDlgButton; + std::unique_ptr<weld::Button> m_xDelButton; + std::unique_ptr<SbTreeListBoxDropTarget> m_xDropTarget; + + DECL_LINK( BasicBoxHighlightHdl, weld::TreeView&, void ); + DECL_LINK( ButtonHdl, weld::Button&, void ); + DECL_LINK( EditingEntryHdl, const weld::TreeIter&, bool ); + typedef std::pair<const weld::TreeIter&, OUString> IterString; + DECL_LINK( EditedEntryHdl, const IterString&, bool ); + + void CheckButtons(); + bool GetSelection( ScriptDocument& rDocument, OUString& rLibName ); + void DeleteCurrent(); + void NewModule(); + void NewDialog(); + void EndTabDialog(); + +public: + ObjectPage(weld::Container* pParent, const OUString& rName, BrowseMode nMode, OrganizeDialog* pDialog); + virtual ~ObjectPage() override; + + void SetCurrentEntry(const EntryDescriptor& rDesc) { m_xBasicBox->SetCurrentEntry(rDesc); } + + virtual void ActivatePage() override; +}; + +class LibPage final : public OrganizePage +{ + std::unique_ptr<weld::ComboBox> m_xBasicsBox; + std::unique_ptr<weld::TreeView> m_xLibBox; + std::unique_ptr<weld::Button> m_xEditButton; + std::unique_ptr<weld::Button> m_xPasswordButton; + std::unique_ptr<weld::Button> m_xNewLibButton; + std::unique_ptr<weld::Button> m_xInsertLibButton; + std::unique_ptr<weld::Button> m_xExportButton; + std::unique_ptr<weld::Button> m_xDelButton; + + ScriptDocument m_aCurDocument; + LibraryLocation m_eCurLocation; + + DECL_LINK( TreeListHighlightHdl, weld::TreeView&, void ); + DECL_LINK( BasicSelectHdl, weld::ComboBox&, void ); + DECL_LINK( ButtonHdl, weld::Button&, void ); + DECL_LINK( CheckPasswordHdl, SvxPasswordDialog *, bool ); + DECL_LINK( EditingEntryHdl, const weld::TreeIter&, bool ); + typedef std::pair<const weld::TreeIter&, OUString> IterString; + DECL_LINK( EditedEntryHdl, const IterString&, bool ); + + void CheckButtons(); + void DeleteCurrent(); + void NewLib(); + void InsertLib(); + void implExportLib( const OUString& aLibName, const OUString& aTargetURL, + const css::uno::Reference< css::task::XInteractionHandler >& Handler ); + void Export(); + void ExportAsPackage( const OUString& aLibName ); + void ExportAsBasic( const OUString& aLibName ); + void EndTabDialog(); + void FillListBox(); + void InsertListBoxEntry( const ScriptDocument& rDocument, LibraryLocation eLocation ); + void SetCurLib(); + void ImpInsertLibEntry( const OUString& rLibName, int nPos ); + +public: + explicit LibPage(weld::Container* pParent, OrganizeDialog* pDialog); + virtual ~LibPage() override; + virtual void ActivatePage() override; +}; + +class OrganizeDialog : public weld::GenericDialogController +{ +private: + std::unique_ptr<weld::Notebook> m_xTabCtrl; + std::unique_ptr<ObjectPage> m_xModulePage; + std::unique_ptr<ObjectPage> m_xDialogPage; + std::unique_ptr<LibPage> m_xLibPage; + + DECL_LINK(ActivatePageHdl, const OUString&, void); + + void SetCurrentEntry(const css::uno::Reference<css::frame::XFrame>& xDocFrame); + +public: + OrganizeDialog(weld::Window* pParent, const css::uno::Reference<css::frame::XFrame>& xDocFrame, sal_Int16 tabId); + virtual ~OrganizeDialog() override; +}; + +// Helper functions +SbModule* createModImpl(weld::Window* pWin, const ScriptDocument& rDocument, + SbTreeListBox& rBasicBox, const OUString& rLibName, const OUString& aModName, bool bMain); +void createLibImpl(weld::Window* pWin, const ScriptDocument& rDocument, + weld::TreeView* pLibBox, SbTreeListBox* pBasicBox); + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/sbxitem.cxx b/basctl/source/basicide/sbxitem.cxx new file mode 100644 index 0000000000..39c86b1d08 --- /dev/null +++ b/basctl/source/basicide/sbxitem.cxx @@ -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 . + */ + +#include <sbxitem.hxx> +#include <sal/log.hxx> +#include <utility> + +namespace basctl +{ +SfxPoolItem* SbxItem::CreateDefault() { SAL_WARN( "basctl.basicide", "No SbxItem factory available"); return nullptr; } +SbxItem::SbxItem ( + sal_uInt16 nWhichItem, + ScriptDocument aDocument, + OUString aLibName, + OUString aName, + ItemType eType +) : + SfxPoolItem(nWhichItem), + m_aDocument(std::move(aDocument)), + m_aLibName(std::move(aLibName)), + m_aName(std::move(aName)), + m_eType(eType) +{ } + +SbxItem::SbxItem ( + sal_uInt16 nWhichItem, + ScriptDocument aDocument, + OUString aLibName, + OUString aName, + OUString aMethodName, + ItemType eType +) : + SfxPoolItem(nWhichItem), + m_aDocument(std::move(aDocument)), + m_aLibName(std::move(aLibName)), + m_aName(std::move(aName)), + m_aMethodName(std::move(aMethodName)), + m_eType(eType) +{ } + +SbxItem* SbxItem::Clone(SfxItemPool*) const +{ + return new SbxItem(*this); +} + +bool SbxItem::operator==(const SfxPoolItem& rCmp) const +{ + SbxItem const* pSbxItem = static_cast<SbxItem const*>(&rCmp); + return + SfxPoolItem::operator==(rCmp) && + m_aDocument == pSbxItem->m_aDocument && + m_aLibName == pSbxItem->m_aLibName && + m_aName == pSbxItem->m_aName && + m_aMethodName == pSbxItem->m_aMethodName && + m_eType == pSbxItem->m_eType; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/scriptdocument.cxx b/basctl/source/basicide/scriptdocument.cxx new file mode 100644 index 0000000000..c435d7a57d --- /dev/null +++ b/basctl/source/basicide/scriptdocument.cxx @@ -0,0 +1,1505 @@ +/* -*- 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 <scriptdocument.hxx> +#include <basobj.hxx> +#include <strings.hrc> +#include <iderid.hxx> +#include <dlgeddef.hxx> +#include <doceventnotifier.hxx> +#include "documentenumeration.hxx" + +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/document/XEmbeddedScripts.hpp> +#include <com/sun/star/script/vba/XVBACompatibility.hpp> +#include <com/sun/star/script/vba/XVBAModuleInfo.hpp> +#include <com/sun/star/script/ModuleInfo.hpp> +#include <com/sun/star/script/ModuleType.hpp> + +#include <sfx2/app.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/docfile.hxx> + +#include <basic/basicmanagerrepository.hxx> + +#include <xmlscript/xmldlg_imexp.hxx> + +#include <i18nlangtag/languagetag.hxx> + +#include <comphelper/diagnose_ex.hxx> +#include <config_folders.h> +#include <tools/debug.hxx> + +#include <comphelper/documentinfo.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <comphelper/string.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <osl/file.hxx> +#include <rtl/uri.hxx> +#include <set> + + +namespace basctl +{ + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::Reference; + using ::com::sun::star::frame::XModel; + using ::com::sun::star::beans::XPropertySet; + using ::com::sun::star::script::XLibraryContainer; + 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::container::XNameContainer; + using ::com::sun::star::container::NoSuchElementException; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::task::XStatusIndicator; + using ::com::sun::star::uno::Any; + using ::com::sun::star::script::XLibraryContainer2; + using ::com::sun::star::uri::UriReferenceFactory; + using ::com::sun::star::uri::XUriReferenceFactory; + using ::com::sun::star::uri::XUriReference; + using ::com::sun::star::uno::XComponentContext; + using ::com::sun::star::util::XMacroExpander; + using ::com::sun::star::util::theMacroExpander; + using ::com::sun::star::io::XInputStreamProvider; + using ::com::sun::star::uno::Any; + using ::com::sun::star::io::XInputStream; + using ::com::sun::star::frame::XStorable; + using ::com::sun::star::util::XModifiable; + using ::com::sun::star::frame::XController; + using ::com::sun::star::frame::XFrame; + using ::com::sun::star::util::URL; + using ::com::sun::star::frame::XDispatchProvider; + using ::com::sun::star::frame::XDispatch; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::awt::XWindow2; + using ::com::sun::star::document::XEmbeddedScripts; + using ::com::sun::star::script::ModuleInfo; + using ::com::sun::star::script::vba::XVBACompatibility; + using ::com::sun::star::script::vba::XVBAModuleInfo; + + namespace FrameSearchFlag = ::com::sun::star::frame::FrameSearchFlag; + + + namespace + { + class FilterDocuments : public docs::IDocumentDescriptorFilter + { + public: + explicit FilterDocuments(bool _bFilterInvisible) + : m_bFilterInvisible(_bFilterInvisible) + { + } + + virtual ~FilterDocuments() {} + + virtual bool includeDocument( const docs::DocumentDescriptor& _rDocument ) const override; + + private: + static bool impl_isDocumentVisible_nothrow( const docs::DocumentDescriptor& _rDocument ); + + private: + bool m_bFilterInvisible; + }; + + bool FilterDocuments::impl_isDocumentVisible_nothrow( const docs::DocumentDescriptor& _rDocument ) + { + try + { + for (auto const& controller : _rDocument.aControllers) + { + Reference< XFrame > xFrame( controller->getFrame(), UNO_SET_THROW ); + Reference< XWindow2 > xContainer( xFrame->getContainerWindow(), UNO_QUERY_THROW ); + if ( xContainer->isVisible() ) + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + bool FilterDocuments::includeDocument( const docs::DocumentDescriptor& _rDocument ) const + { + Reference< XEmbeddedScripts > xScripts( _rDocument.xModel, UNO_QUERY ); + if ( !xScripts.is() ) + return false; + return !m_bFilterInvisible || impl_isDocumentVisible_nothrow( _rDocument ); + } + + void lcl_getAllModels_throw( docs::Documents& _out_rModels, bool _bVisibleOnly ) + { + _out_rModels.clear(); + + FilterDocuments aFilter( _bVisibleOnly ); + docs::DocumentEnumeration aEnum( + comphelper::getProcessComponentContext(), &aFilter ); + + aEnum.getDocuments( _out_rModels ); + } + } + + class ScriptDocument::Impl : public DocumentEventListener + { + private: + bool m_bIsApplication; + bool m_bValid; + bool m_bDocumentClosed; + Reference< XModel > m_xDocument; + Reference< XModifiable > m_xDocModify; + Reference< XEmbeddedScripts > m_xScriptAccess; + std::unique_ptr< DocumentEventNotifier > m_pDocListener; + + public: + Impl (); + explicit Impl(Reference<XModel> const& rxDocument); + virtual ~Impl() override; + + /** determines whether the instance refers to a valid "document" with script and + dialog libraries + */ + bool isValid() const { return m_bValid; } + /** determines whether the instance refers to a non-closed document + */ + bool isAlive() const { return m_bValid && ( m_bIsApplication || !m_bDocumentClosed ); } + /// determines whether the "document" refers to the application in real + bool isApplication() const { return m_bValid && m_bIsApplication; } + /// determines whether the document refers to a real document (instead of the application) + bool isDocument() const { return m_bValid && !m_bIsApplication; } + + /** invalidates the instance + */ + void invalidate(); + + const Reference< XModel >& + getDocumentRef() const { return m_xDocument; } + + /// returns a library container belonging to the document + Reference< XLibraryContainer > + getLibraryContainer( LibraryContainerType _eType ) const; + + /// determines whether a given library is part of the shared installation + bool isLibraryShared( const OUString& _rLibName, LibraryContainerType _eType ); + + /** returns the current frame of the document + + To be called for documents only, not for the application. + + If <FALSE/> is returned, an assertion will be raised in non-product builds. + */ + bool getCurrentFrame( Reference< XFrame >& _out_rxFrame ) const; + + // versions with the same signature/semantics as in ScriptDocument itself + bool isReadOnly() const; + bool isInVBAMode() const; + BasicManager* + getBasicManager() const; + Reference< XModel > + getDocument() const; + void setDocumentModified() const; + bool isDocumentModified() const; + void saveDocument( const Reference< XStatusIndicator >& _rxStatusIndicator ) const; + + OUString getTitle() const; + OUString getURL() const; + + bool allowMacros() const; + + Reference< XNameContainer > + getLibrary( LibraryContainerType _eType, const OUString& _rLibName, bool _bLoadLibrary ) const; + bool hasLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const; + Reference< XNameContainer > + getOrCreateLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const; + + void loadLibraryIfExists( LibraryContainerType _eType, const OUString& _rLibrary ); + + bool removeModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rModuleName ); + bool hasModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rModName ) const; + bool getModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rObjectName, Any& _out_rModuleOrDialog ); + bool renameModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rOldName, const OUString& _rNewName, const Reference< XNameContainer >& _rxExistingDialogModel ); + bool createModule( const OUString& _rLibName, const OUString& _rModName, bool _bCreateMain, OUString& _out_rNewModuleCode ) const; + bool insertModuleOrDialog( LibraryContainerType _eType, const OUString& _rObjectName, const OUString& _rModName, const Any& _rElement ) const; + bool updateModule( const OUString& _rLibName, const OUString& _rModName, const OUString& _rModuleCode ) const; + bool createDialog( const OUString& _rLibName, const OUString& _rDialogName, Reference< XInputStreamProvider >& _out_rDialogProvider ) const; + + protected: + // DocumentEventListener + virtual void onDocumentCreated( const ScriptDocument& _rDocument ) override; + virtual void onDocumentOpened( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSave( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveDone( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveAs( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveAsDone( const ScriptDocument& _rDocument ) override; + virtual void onDocumentClosed( const ScriptDocument& _rDocument ) override; + virtual void onDocumentTitleChanged( const ScriptDocument& _rDocument ) override; + virtual void onDocumentModeChanged( const ScriptDocument& _rDocument ) override; + + private: + bool impl_initDocument_nothrow( const Reference< XModel >& _rxModel ); + }; + + + ScriptDocument::Impl::Impl() + :m_bIsApplication( true ) + ,m_bValid( true ) + ,m_bDocumentClosed( false ) + { + } + + ScriptDocument::Impl::Impl( const Reference< XModel >& _rxDocument ) + :m_bIsApplication( false ) + ,m_bValid( false ) + ,m_bDocumentClosed( false ) + { + if ( _rxDocument.is() ) + impl_initDocument_nothrow( _rxDocument ); + } + + ScriptDocument::Impl::~Impl() + { + invalidate(); + } + + void ScriptDocument::Impl::invalidate() + { + m_bIsApplication = false; + m_bValid = false; + m_bDocumentClosed = false; + + m_xDocument.clear(); + m_xDocModify.clear(); + m_xScriptAccess.clear(); + + if (m_pDocListener) + m_pDocListener->dispose(); + } + + bool ScriptDocument::Impl::impl_initDocument_nothrow( const Reference< XModel >& _rxModel ) + { + try + { + m_xDocument.set ( _rxModel, UNO_SET_THROW ); + m_xDocModify.set ( _rxModel, UNO_QUERY_THROW ); + m_xScriptAccess.set ( _rxModel, UNO_QUERY ); + + m_bValid = m_xScriptAccess.is(); + + if ( m_bValid ) + m_pDocListener.reset( new DocumentEventNotifier( *this, _rxModel ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + m_bValid = false; + } + + if ( !m_bValid ) + { + invalidate(); + } + + return m_bValid; + } + + Reference< XLibraryContainer > ScriptDocument::Impl::getLibraryContainer( LibraryContainerType _eType ) const + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::getLibraryContainer: invalid!" ); + + Reference< XLibraryContainer > xContainer; + if ( !isValid() ) + return xContainer; + + try + { + if ( isApplication() ) + xContainer.set( _eType == E_SCRIPTS ? SfxGetpApp()->GetBasicContainer() : SfxGetpApp()->GetDialogContainer(), UNO_QUERY_THROW ); + else + { + xContainer.set( + _eType == E_SCRIPTS ? m_xScriptAccess->getBasicLibraries() : m_xScriptAccess->getDialogLibraries(), + UNO_QUERY_THROW ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return xContainer; + } + + bool ScriptDocument::Impl::isReadOnly() const + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::isReadOnly: invalid state!" ); + OSL_ENSURE( !isApplication(), "ScriptDocument::Impl::isReadOnly: not allowed to be called for the application!" ); + + bool bIsReadOnly = true; + if ( isValid() && !isApplication() ) + { + try + { + // note that XStorable is required by the OfficeDocument service + Reference< XStorable > xDocStorable( m_xDocument, UNO_QUERY_THROW ); + bIsReadOnly = xDocStorable->isReadonly(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + return bIsReadOnly; + } + + bool ScriptDocument::Impl::isInVBAMode() const + { + bool bResult = false; + if ( !isApplication() ) + { + Reference< XVBACompatibility > xVBACompat( getLibraryContainer( E_SCRIPTS ), UNO_QUERY ); + if ( xVBACompat.is() ) + bResult = xVBACompat->getVBACompatibilityMode(); + } + return bResult; + } + + BasicManager* ScriptDocument::Impl::getBasicManager() const + { + try + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::getBasicManager: invalid state!" ); + if ( !isValid() ) + return nullptr; + + if ( isApplication() ) + return SfxApplication::GetBasicManager(); + + return ::basic::BasicManagerRepository::getDocumentBasicManager( m_xDocument ); + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION( "basctl.basicide", "ScriptDocument::getBasicManager" ); + } + return nullptr; + } + + Reference< XModel > ScriptDocument::Impl::getDocument() const + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::getDocument: invalid state!" ); + OSL_ENSURE( isDocument(), "ScriptDocument::Impl::getDocument: for documents only!" ); + if ( !isValid() || !isDocument() ) + return nullptr; + + return m_xDocument; + } + + + Reference< XNameContainer > ScriptDocument::Impl::getLibrary( LibraryContainerType _eType, const OUString& _rLibName, bool _bLoadLibrary ) const + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::getLibrary: invalid state!" ); + + Reference< XNameContainer > xContainer; + try + { + Reference< XLibraryContainer > xLibContainer = getLibraryContainer( _eType ); + if ( isValid() && xLibContainer.is() ) + xContainer.set( xLibContainer->getByName( _rLibName ), UNO_QUERY_THROW ); + + if ( !xContainer.is() ) + throw NoSuchElementException(); + + // load library + if ( _bLoadLibrary && !xLibContainer->isLibraryLoaded( _rLibName ) ) + xLibContainer->loadLibrary( _rLibName ); + } + catch( const NoSuchElementException& ) + { + throw; // allowed to leave + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + return xContainer; + } + + + bool ScriptDocument::Impl::hasLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const + { + bool bHas = false; + try + { + Reference< XLibraryContainer > xLibContainer = getLibraryContainer( _eType ); + bHas = xLibContainer.is() && xLibContainer->hasByName( _rLibName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return bHas; + } + + + Reference< XNameContainer > ScriptDocument::Impl::getOrCreateLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const + { + Reference< XNameContainer > xLibrary; + try + { + Reference< XLibraryContainer > xLibContainer( getLibraryContainer( _eType ), UNO_SET_THROW ); + if ( xLibContainer->hasByName( _rLibName ) ) + xLibrary.set( xLibContainer->getByName( _rLibName ), UNO_QUERY_THROW ); + else + xLibrary.set( xLibContainer->createLibrary( _rLibName ), UNO_SET_THROW ); + + if ( !xLibContainer->isLibraryLoaded( _rLibName ) ) + xLibContainer->loadLibrary( _rLibName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return xLibrary; + } + + + void ScriptDocument::Impl::loadLibraryIfExists( LibraryContainerType _eType, const OUString& _rLibrary ) + { + try + { + Reference< XLibraryContainer > xLibContainer( getLibraryContainer( _eType ) ); + if ( xLibContainer.is() && xLibContainer->hasByName( _rLibrary ) && !xLibContainer->isLibraryLoaded( _rLibrary ) ) + xLibContainer->loadLibrary( _rLibrary ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + + + bool ScriptDocument::Impl::removeModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rModuleName ) + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::removeModuleOrDialog: invalid!" ); + if ( !isValid() ) + return false; + try + { + Reference< XNameContainer > xLib( getLibrary( _eType, _rLibName, true ) ); + if ( xLib.is() ) + { + xLib->removeByName( _rModuleName ); + Reference< XVBAModuleInfo > xVBAModuleInfo(xLib, UNO_QUERY); + if(xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo(_rModuleName)) + xVBAModuleInfo->removeModuleInfo(_rModuleName); + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + + bool ScriptDocument::Impl::hasModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rModName ) const + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::hasModuleOrDialog: invalid!" ); + if ( !isValid() ) + return false; + + try + { + Reference< XNameContainer > xLib( getLibrary( _eType, _rLibName, true ) ); + if ( xLib.is() ) + return xLib->hasByName( _rModName ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + + bool ScriptDocument::Impl::getModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rObjectName, Any& _out_rModuleOrDialog ) + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::getModuleOrDialog: invalid!" ); + if ( !isValid() ) + return false; + + _out_rModuleOrDialog.clear(); + try + { + Reference< XNameContainer > xLib( getLibrary( _eType, _rLibName, true ), UNO_SET_THROW ); + if ( xLib->hasByName( _rObjectName ) ) + { + _out_rModuleOrDialog = xLib->getByName( _rObjectName ); + return true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + + bool ScriptDocument::Impl::renameModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, + const OUString& _rOldName, const OUString& _rNewName, const Reference< XNameContainer >& _rxExistingDialogModel ) + { + OSL_ENSURE( isValid(), "ScriptDocument::Impl::renameModuleOrDialog: invalid!" ); + if ( !isValid() ) + return false; + + try + { + Reference< XNameContainer > xLib( getLibrary( _eType, _rLibName, true ), UNO_SET_THROW ); + + // get element + Any aElement( xLib->getByName( _rOldName ) ); + + // remove element from container + xLib->removeByName( _rOldName ); + + // if it's a dialog, import and export, to reflect the new name + if ( _eType == E_DIALOGS ) + { + // create dialog model + Reference< XComponentContext > aContext( + comphelper::getProcessComponentContext() ); + Reference< XNameContainer > xDialogModel; + if ( _rxExistingDialogModel.is() ) + xDialogModel = _rxExistingDialogModel; + else + xDialogModel.set( + ( aContext->getServiceManager()-> + createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", + aContext ) ), + UNO_QUERY_THROW ); + + // import dialog model + Reference< XInputStreamProvider > xISP( aElement, UNO_QUERY_THROW ); + if ( !_rxExistingDialogModel.is() ) + { + Reference< XInputStream > xInput( xISP->createInputStream(), UNO_SET_THROW ); + ::xmlscript::importDialogModel( xInput, xDialogModel, aContext, isDocument() ? getDocument() : Reference< XModel >() ); + } + + // set new name as property + Reference< XPropertySet > xDlgPSet( xDialogModel, UNO_QUERY_THROW ); + xDlgPSet->setPropertyValue( DLGED_PROP_NAME, Any( _rNewName ) ); + + // export dialog model + xISP = ::xmlscript::exportDialogModel( xDialogModel, aContext, isDocument() ? getDocument() : Reference< XModel >() ); + aElement <<= xISP; + } + + // insert element by new name in container + if ( _eType == E_SCRIPTS ) + { + Reference< XVBAModuleInfo > xVBAModuleInfo( xLib, UNO_QUERY ); + if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo( _rOldName ) ) + { + ModuleInfo sModuleInfo = xVBAModuleInfo->getModuleInfo( _rOldName ); + xVBAModuleInfo->removeModuleInfo( _rOldName ); + xVBAModuleInfo->insertModuleInfo( _rNewName, sModuleInfo ); + } + } + xLib->insertByName( _rNewName, aElement ); + return true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + + bool ScriptDocument::Impl::createModule( const OUString& _rLibName, const OUString& _rModName, bool _bCreateMain, OUString& _out_rNewModuleCode ) const + { + _out_rNewModuleCode.clear(); + try + { + Reference< XNameContainer > xLib( getLibrary( E_SCRIPTS, _rLibName, true ) ); + if ( !xLib.is() || xLib->hasByName( _rModName ) ) + return false; + + // create new module + _out_rNewModuleCode = "REM ***** BASIC *****\n\n" ; + if ( _bCreateMain ) + _out_rNewModuleCode += "Sub Main\n\nEnd Sub\n" ; + + Reference< XVBAModuleInfo > xVBAModuleInfo(xLib, UNO_QUERY); + if (xVBAModuleInfo.is()) + { + css::script::ModuleInfo aModuleInfo; + aModuleInfo.ModuleType = css::script::ModuleType::NORMAL; + xVBAModuleInfo->insertModuleInfo(_rModName, aModuleInfo); + } + + // insert module into library + xLib->insertByName( _rModName, Any( _out_rNewModuleCode ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + return false; + } + + return true; + } + + + bool ScriptDocument::Impl::insertModuleOrDialog( LibraryContainerType _eType, const OUString& _rLibName, const OUString& _rObjectName, const Any& _rElement ) const + { + try + { + Reference< XNameContainer > xLib( getOrCreateLibrary( _eType, _rLibName ), UNO_SET_THROW ); + if ( xLib->hasByName( _rObjectName ) ) + return false; + + xLib->insertByName( _rObjectName, _rElement ); + return true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + + bool ScriptDocument::Impl::updateModule( const OUString& _rLibName, const OUString& _rModName, const OUString& _rModuleCode ) const + { + try + { + Reference< XNameContainer > xLib( getOrCreateLibrary( E_SCRIPTS, _rLibName ), UNO_SET_THROW ); + if ( !xLib->hasByName( _rModName ) ) + return false; + xLib->replaceByName( _rModName, Any( _rModuleCode ) ); + return true; + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return false; + } + + + bool ScriptDocument::Impl::createDialog( const OUString& _rLibName, const OUString& _rDialogName, Reference< XInputStreamProvider >& _out_rDialogProvider ) const + { + try + { + Reference< XNameContainer > xLib( getLibrary( E_DIALOGS, _rLibName, true ), UNO_SET_THROW ); + + // create dialog + _out_rDialogProvider.clear(); + if ( xLib->hasByName( _rDialogName ) ) + return false; + + // create new dialog model + Reference< XComponentContext > aContext( + comphelper::getProcessComponentContext() ); + Reference< XNameContainer > xDialogModel( + aContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", aContext ), + UNO_QUERY_THROW ); + + // set name property + Reference< XPropertySet > xDlgPSet( xDialogModel, UNO_QUERY_THROW ); + xDlgPSet->setPropertyValue( DLGED_PROP_NAME, Any( _rDialogName ) ); + + // export dialog model + _out_rDialogProvider = ::xmlscript::exportDialogModel( xDialogModel, aContext, isDocument() ? getDocument() : Reference< XModel >() ); + + // insert dialog into library + xLib->insertByName( _rDialogName, Any( _out_rDialogProvider ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + return _out_rDialogProvider.is(); + } + + + void ScriptDocument::Impl::setDocumentModified() const + { + OSL_ENSURE( isValid() && isDocument(), "ScriptDocument::Impl::setDocumentModified: only to be called for real documents!" ); + if ( isValid() && isDocument() ) + { + try + { + m_xDocModify->setModified( true ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + } + + + bool ScriptDocument::Impl::isDocumentModified() const + { + OSL_ENSURE( isValid() && isDocument(), "ScriptDocument::Impl::isDocumentModified: only to be called for real documents!" ); + bool bIsModified = false; + if ( isValid() && isDocument() ) + { + try + { + bIsModified = m_xDocModify->isModified(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + return bIsModified; + } + + + void ScriptDocument::Impl::saveDocument( const Reference< XStatusIndicator >& _rxStatusIndicator ) const + { + Reference< XFrame > xFrame; + if ( !getCurrentFrame( xFrame ) ) + return; + + Sequence< PropertyValue > aArgs; + if ( _rxStatusIndicator.is() ) + { + aArgs = ::comphelper::InitPropertySequence({ + { "StatusIndicator", Any(_rxStatusIndicator) } + }); + } + + try + { + URL aURL; + aURL.Complete = ".uno:Save" ; + aURL.Main = aURL.Complete; + aURL.Protocol = ".uno:" ; + aURL.Path = "Save" ; + + Reference< XDispatchProvider > xDispProv( xFrame, UNO_QUERY_THROW ); + Reference< XDispatch > xDispatch( + xDispProv->queryDispatch( aURL, "_self", FrameSearchFlag::AUTO ), + UNO_SET_THROW ); + + xDispatch->dispatch( aURL, aArgs ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + + + OUString ScriptDocument::Impl::getTitle() const + { + OSL_PRECOND( isValid() && isDocument(), "ScriptDocument::Impl::getTitle: for documents only!" ); + + OUString sTitle; + if ( isValid() && isDocument() ) + { + sTitle = ::comphelper::DocumentInfo::getDocumentTitle( m_xDocument ); + } + return sTitle; + } + + + OUString ScriptDocument::Impl::getURL() const + { + OSL_PRECOND( isValid() && isDocument(), "ScriptDocument::Impl::getURL: for documents only!" ); + + OUString sURL; + if ( isValid() && isDocument() ) + { + try + { + sURL = m_xDocument->getURL(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + return sURL; + } + + + bool ScriptDocument::Impl::allowMacros() const + { + OSL_ENSURE( isValid() && isDocument(), "ScriptDocument::Impl::allowMacros: for documents only!" ); + bool bAllow = false; + if ( isValid() && isDocument() ) + { + try + { + bAllow = m_xScriptAccess->getAllowMacroExecution(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + } + return bAllow; + } + + + bool ScriptDocument::Impl::getCurrentFrame( Reference< XFrame >& _out_rxFrame ) const + { + _out_rxFrame.clear(); + OSL_PRECOND( isValid() && isDocument(), "ScriptDocument::Impl::getCurrentFrame: documents only!" ); + if ( !isValid() || !isDocument() ) + return false; + + try + { + Reference< XModel > xDocument( m_xDocument, UNO_SET_THROW ); + Reference< XController > xController( xDocument->getCurrentController(), UNO_SET_THROW ); + _out_rxFrame.set( xController->getFrame(), UNO_SET_THROW ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + return _out_rxFrame.is(); + } + + + bool ScriptDocument::Impl::isLibraryShared( const OUString& _rLibName, LibraryContainerType _eType ) + { + bool bIsShared = false; + try + { + Reference< XLibraryContainer2 > xLibContainer( getLibraryContainer( _eType ), UNO_QUERY_THROW ); + + if ( !xLibContainer->hasByName( _rLibName ) || !xLibContainer->isLibraryLink( _rLibName ) ) + return false; + OUString aFileURL; + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XUriReferenceFactory > xUriFac = UriReferenceFactory::create(xContext); + + OUString aLinkURL( xLibContainer->getLibraryLinkURL( _rLibName ) ); + Reference< XUriReference > xUriRef( xUriFac->parse( aLinkURL ), UNO_SET_THROW ); + + OUString aScheme = xUriRef->getScheme(); + if ( aScheme.equalsIgnoreAsciiCase("file") ) + { + aFileURL = aLinkURL; + } + else if ( aScheme.equalsIgnoreAsciiCase("vnd.sun.star.pkg") ) + { + OUString aDecodedURL = xUriRef->getAuthority(); + if (aDecodedURL.startsWithIgnoreAsciiCase("vnd.sun.star.expand:", &aDecodedURL)) + { + aDecodedURL = ::rtl::Uri::decode( aDecodedURL, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 ); + Reference< XMacroExpander > xMacroExpander = theMacroExpander::get(xContext); + aFileURL = xMacroExpander->expandMacros( aDecodedURL ); + } + } + + if ( !aFileURL.isEmpty() ) + { + ::osl::DirectoryItem aFileItem; + ::osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileURL ); + OSL_VERIFY( ::osl::DirectoryItem::get( aFileURL, aFileItem ) == ::osl::FileBase::E_None ); + OSL_VERIFY( aFileItem.getFileStatus( aFileStatus ) == ::osl::FileBase::E_None ); + OUString aCanonicalFileURL( aFileStatus.getFileURL() ); + + if( aCanonicalFileURL.indexOf( LIBO_SHARE_FOLDER "/basic" ) >= 0 || + aCanonicalFileURL.indexOf( LIBO_SHARE_FOLDER "/uno_packages" ) >= 0 || + aCanonicalFileURL.indexOf( LIBO_SHARE_FOLDER "/extensions" ) >= 0 ) + bIsShared = true; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + return bIsShared; + } + + + void ScriptDocument::Impl::onDocumentCreated( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentOpened( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentSave( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentSaveDone( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentSaveAs( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentSaveAsDone( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentClosed( const ScriptDocument& _rDocument ) + { + DBG_TESTSOLARMUTEX(); + OSL_PRECOND( isValid(), "ScriptDocument::Impl::onDocumentClosed: should not be listening if I'm not valid!" ); + + bool bMyDocument = m_xDocument == _rDocument.getDocument(); + OSL_PRECOND( bMyDocument, "ScriptDocument::Impl::onDocumentClosed: didn't want to know *this*!" ); + if ( bMyDocument ) + { + m_bDocumentClosed = true; + } + } + + + void ScriptDocument::Impl::onDocumentTitleChanged( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + void ScriptDocument::Impl::onDocumentModeChanged( const ScriptDocument& /*_rDocument*/ ) + { + // not interested in + } + + + ScriptDocument::ScriptDocument() + :m_pImpl(std::make_shared<Impl>()) + { } + + + ScriptDocument::ScriptDocument( ScriptDocument::SpecialDocument _eType ) + :m_pImpl( std::make_shared<Impl>( Reference< XModel >() ) ) + { + OSL_ENSURE( _eType == NoDocument, "ScriptDocument::ScriptDocument: unknown SpecialDocument type!" ); + } + + + ScriptDocument::ScriptDocument( const Reference< XModel >& _rxDocument ) + :m_pImpl( std::make_shared<Impl>( _rxDocument ) ) + { + OSL_ENSURE( _rxDocument.is(), "ScriptDocument::ScriptDocument: document must not be NULL!" ); + // a NULL document results in an uninitialized instance, and for this + // purpose, there is a dedicated constructor + } + + + const ScriptDocument& ScriptDocument::getApplicationScriptDocument() + { + static ScriptDocument s_aApplicationScripts; + return s_aApplicationScripts; + } + + + ScriptDocument ScriptDocument::getDocumentForBasicManager( const BasicManager* _pManager ) + { + if ( _pManager == SfxApplication::GetBasicManager() ) + return getApplicationScriptDocument(); + + docs::Documents aDocuments; + lcl_getAllModels_throw( aDocuments, false ); + + for (auto const& doc : aDocuments) + { + const BasicManager* pDocBasicManager = ::basic::BasicManagerRepository::getDocumentBasicManager( doc.xModel ); + if ( ( pDocBasicManager != SfxApplication::GetBasicManager() ) + && ( pDocBasicManager == _pManager ) + ) + { + return ScriptDocument( doc.xModel ); + } + } + + OSL_FAIL( "ScriptDocument::getDocumentForBasicManager: did not find a document for this manager!" ); + return ScriptDocument( NoDocument ); + } + + + ScriptDocument ScriptDocument::getDocumentWithURLOrCaption( std::u16string_view _rUrlOrCaption ) + { + ScriptDocument aDocument( getApplicationScriptDocument() ); + if ( _rUrlOrCaption.empty() ) + return aDocument; + + docs::Documents aDocuments; + lcl_getAllModels_throw( aDocuments, false ); + + for (auto const& doc : aDocuments) + { + const ScriptDocument aCheck( doc.xModel ); + if ( _rUrlOrCaption == aCheck.getTitle() + || _rUrlOrCaption == aCheck.m_pImpl->getURL() + ) + { + aDocument = aCheck; + break; + } + } + + return aDocument; + } + + ScriptDocuments ScriptDocument::getAllScriptDocuments( ScriptDocument::ScriptDocumentList _eListType ) + { + ScriptDocuments aScriptDocs; + + // include application? + if ( _eListType == AllWithApplication ) + aScriptDocs.push_back( getApplicationScriptDocument() ); + + // obtain documents + try + { + docs::Documents aDocuments; + lcl_getAllModels_throw( aDocuments, true /* exclude invisible */ ); + + for (auto const& doc : aDocuments) + { + // exclude documents without script/library containers + ScriptDocument aDoc( doc.xModel ); + if ( !aDoc.isValid() ) + continue; + + aScriptDocs.push_back( aDoc ); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + // sort document list by doc title? + if ( _eListType == DocumentsSorted ) + { + auto const sort = comphelper::string::NaturalStringSorter( + comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale()); + std::sort(aScriptDocs.begin(), aScriptDocs.end(), + [&sort](const ScriptDocument& rLHS, const ScriptDocument& rRHS) { + return sort.compare(rLHS.getTitle(), rRHS.getTitle()) < 0; + }); + } + + return aScriptDocs; + } + + + bool ScriptDocument::operator==( const ScriptDocument& _rhs ) const + { + return m_pImpl->getDocumentRef() == _rhs.m_pImpl->getDocumentRef(); + } + + + sal_Int32 ScriptDocument::hashCode() const + { + return sal::static_int_cast<sal_Int32>(reinterpret_cast< sal_IntPtr >( m_pImpl->getDocumentRef().get() )); + } + + + bool ScriptDocument::isValid() const + { + return m_pImpl->isValid(); + } + + + bool ScriptDocument::isAlive() const + { + return m_pImpl->isAlive(); + } + + + Reference< XLibraryContainer > ScriptDocument::getLibraryContainer( LibraryContainerType _eType ) const + { + return m_pImpl->getLibraryContainer( _eType ); + } + + + Reference< XNameContainer > ScriptDocument::getLibrary( LibraryContainerType _eType, const OUString& _rLibName, bool _bLoadLibrary ) const + { + return m_pImpl->getLibrary( _eType, _rLibName, _bLoadLibrary ); + } + + + bool ScriptDocument::hasLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const + { + return m_pImpl->hasLibrary( _eType, _rLibName ); + } + + + Reference< XNameContainer > ScriptDocument::getOrCreateLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const + { + return m_pImpl->getOrCreateLibrary( _eType, _rLibName ); + } + + + void ScriptDocument::loadLibraryIfExists( LibraryContainerType _eType, const OUString& _rLibrary ) + { + m_pImpl->loadLibraryIfExists( _eType, _rLibrary ); + } + + + Sequence< OUString > ScriptDocument::getObjectNames( LibraryContainerType _eType, const OUString& _rLibName ) const + { + Sequence< OUString > aModuleNames; + + try + { + if ( hasLibrary( _eType, _rLibName ) ) + { + Reference< XNameContainer > xLib( getLibrary( _eType, _rLibName, false ) ); + if ( xLib.is() ) + aModuleNames = xLib->getElementNames(); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + + // sort + auto const sort = comphelper::string::NaturalStringSorter( + comphelper::getProcessComponentContext(), + Application::GetSettings().GetUILanguageTag().getLocale()); + auto [begin, end] = asNonConstRange(aModuleNames); + std::sort(begin, end, + [&sort](const OUString& rLHS, const OUString& rRHS) { + return sort.compare(rLHS, rRHS) < 0; + }); + return aModuleNames; + } + + + OUString ScriptDocument::createObjectName( LibraryContainerType _eType, const OUString& _rLibName ) const + { + OUString aObjectName; + + OUString aBaseName = _eType == E_SCRIPTS ? OUString("Module") : OUString("Dialog"); + + const Sequence< OUString > aUsedNames( getObjectNames( _eType, _rLibName ) ); + std::set< OUString > aUsedNamesCheck( aUsedNames.begin(), aUsedNames.end() ); + + bool bValid = false; + sal_Int32 i = 1; + while ( !bValid ) + { + aObjectName = aBaseName + + OUString::number( i ); + + if ( aUsedNamesCheck.find( aObjectName ) == aUsedNamesCheck.end() ) + bValid = true; + + ++i; + } + + return aObjectName; + } + + + Sequence< OUString > ScriptDocument::getLibraryNames() const + { + return GetMergedLibraryNames( getLibraryContainer( E_SCRIPTS ), getLibraryContainer( E_DIALOGS ) ); + } + + + bool ScriptDocument::isReadOnly() const + { + return m_pImpl->isReadOnly(); + } + + + bool ScriptDocument::isApplication() const + { + return m_pImpl->isApplication(); + } + + bool ScriptDocument::isInVBAMode() const + { + return m_pImpl->isInVBAMode(); + } + + + BasicManager* ScriptDocument::getBasicManager() const + { + return m_pImpl->getBasicManager(); + } + + + Reference< XModel > ScriptDocument::getDocument() const + { + return m_pImpl->getDocument(); + } + + + Reference< XModel > ScriptDocument::getDocumentOrNull() const + { + if ( isDocument() ) + return m_pImpl->getDocument(); + return nullptr; + } + + + bool ScriptDocument::removeModule( const OUString& _rLibName, const OUString& _rModuleName ) const + { + return m_pImpl->removeModuleOrDialog( E_SCRIPTS, _rLibName, _rModuleName ); + } + + + bool ScriptDocument::hasModule( const OUString& _rLibName, const OUString& _rModuleName ) const + { + return m_pImpl->hasModuleOrDialog( E_SCRIPTS, _rLibName, _rModuleName ); + } + + + bool ScriptDocument::getModule( const OUString& _rLibName, const OUString& _rModName, OUString& _out_rModuleSource ) const + { + Any aCode; + if ( !m_pImpl->getModuleOrDialog( E_SCRIPTS, _rLibName, _rModName, aCode ) ) + return false; + OSL_VERIFY( aCode >>= _out_rModuleSource ); + return true; + } + + + bool ScriptDocument::renameModule( const OUString& _rLibName, const OUString& _rOldName, const OUString& _rNewName ) const + { + return m_pImpl->renameModuleOrDialog( E_SCRIPTS, _rLibName, _rOldName, _rNewName, nullptr ); + } + + + bool ScriptDocument::createModule( const OUString& _rLibName, const OUString& _rModName, bool _bCreateMain, OUString& _out_rNewModuleCode ) const + { + if ( !m_pImpl->createModule( _rLibName, _rModName, _bCreateMain, _out_rNewModuleCode ) ) + return false; + + // doc shell modified + MarkDocumentModified( *const_cast< ScriptDocument* >( this ) ); // here? + return true; + } + + + bool ScriptDocument::insertModule( const OUString& _rLibName, const OUString& _rModName, const OUString& _rModuleCode ) const + { + return m_pImpl->insertModuleOrDialog( E_SCRIPTS, _rLibName, _rModName, Any( _rModuleCode ) ); + } + + + bool ScriptDocument::updateModule( const OUString& _rLibName, const OUString& _rModName, const OUString& _rModuleCode ) const + { + return m_pImpl->updateModule( _rLibName, _rModName, _rModuleCode ); + } + + + bool ScriptDocument::removeDialog( const OUString& _rLibName, const OUString& _rDialogName ) const + { + return m_pImpl->removeModuleOrDialog( E_DIALOGS, _rLibName, _rDialogName ); + } + + + bool ScriptDocument::hasDialog( const OUString& _rLibName, const OUString& _rDialogName ) const + { + return m_pImpl->hasModuleOrDialog( E_DIALOGS, _rLibName, _rDialogName ); + } + + + bool ScriptDocument::getDialog( const OUString& _rLibName, const OUString& _rDialogName, Reference< XInputStreamProvider >& _out_rDialogProvider ) const + { + Any aCode; + if ( !m_pImpl->getModuleOrDialog( E_DIALOGS, _rLibName, _rDialogName, aCode ) ) + return false; + OSL_VERIFY( aCode >>= _out_rDialogProvider ); + return _out_rDialogProvider.is(); + } + + + bool ScriptDocument::renameDialog( const OUString& _rLibName, const OUString& _rOldName, const OUString& _rNewName, const Reference< XNameContainer >& _rxExistingDialogModel ) const + { + return m_pImpl->renameModuleOrDialog( E_DIALOGS, _rLibName, _rOldName, _rNewName, _rxExistingDialogModel ); + } + + + bool ScriptDocument::createDialog( const OUString& _rLibName, const OUString& _rDialogName, Reference< XInputStreamProvider >& _out_rDialogProvider ) const + { + if ( !m_pImpl->createDialog( _rLibName, _rDialogName, _out_rDialogProvider ) ) + return false; + + MarkDocumentModified( *const_cast< ScriptDocument* >( this ) ); // here? + return true; + } + + + bool ScriptDocument::insertDialog( const OUString& _rLibName, const OUString& _rDialogName, const Reference< XInputStreamProvider >& _rxDialogProvider ) const + { + return m_pImpl->insertModuleOrDialog( E_DIALOGS, _rLibName, _rDialogName, Any( _rxDialogProvider ) ); + } + + + void ScriptDocument::setDocumentModified() const + { + m_pImpl->setDocumentModified(); + } + + + bool ScriptDocument::isDocumentModified() const + { + return m_pImpl->isDocumentModified(); + } + + + void ScriptDocument::saveDocument( const Reference< XStatusIndicator >& _rxStatusIndicator ) const + { + m_pImpl->saveDocument( _rxStatusIndicator ); + } + + + LibraryLocation ScriptDocument::getLibraryLocation( const OUString& _rLibName ) const + { + LibraryLocation eLocation = LIBRARY_LOCATION_UNKNOWN; + if ( !_rLibName.isEmpty() ) + { + if ( isDocument() ) + { + eLocation = LIBRARY_LOCATION_DOCUMENT; + } + else + { + if ( ( hasLibrary( E_SCRIPTS, _rLibName ) && !m_pImpl->isLibraryShared( _rLibName, E_SCRIPTS ) ) + || ( hasLibrary( E_DIALOGS, _rLibName ) && !m_pImpl->isLibraryShared( _rLibName, E_DIALOGS ) ) + ) + { + eLocation = LIBRARY_LOCATION_USER; + } + else + { + eLocation = LIBRARY_LOCATION_SHARE; + } + } + } + + return eLocation; + } + + + OUString ScriptDocument::getTitle( LibraryLocation _eLocation, LibraryType _eType ) const + { + OUString aTitle; + + switch ( _eLocation ) + { + case LIBRARY_LOCATION_USER: + { + switch ( _eType ) + { + case LibraryType::Module: aTitle = IDEResId(RID_STR_USERMACROS); break; + case LibraryType::Dialog: aTitle = IDEResId(RID_STR_USERDIALOGS); break; + case LibraryType::All: aTitle = IDEResId(RID_STR_USERMACROSDIALOGS); break; + default: + break; + } + } + break; + case LIBRARY_LOCATION_SHARE: + { + switch ( _eType ) + { + case LibraryType::Module: aTitle = IDEResId(RID_STR_SHAREMACROS); break; + case LibraryType::Dialog: aTitle = IDEResId(RID_STR_SHAREDIALOGS); break; + case LibraryType::All: aTitle = IDEResId(RID_STR_SHAREMACROSDIALOGS); break; + default: + break; + } + } + break; + case LIBRARY_LOCATION_DOCUMENT: + aTitle = getTitle(); + break; + default: + break; + } + + return aTitle; + } + + + OUString ScriptDocument::getTitle() const + { + return m_pImpl->getTitle(); + } + + + bool ScriptDocument::isActive() const + { + bool bIsActive( false ); + try + { + Reference< XFrame > xFrame; + if ( m_pImpl->getCurrentFrame( xFrame ) ) + bIsActive = xFrame->isActive(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl.basicide"); + } + return bIsActive; + } + + + bool ScriptDocument::allowMacros() const + { + return m_pImpl->allowMacros(); + } + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/textwindowpeer.cxx b/basctl/source/basicide/textwindowpeer.cxx new file mode 100644 index 0000000000..421468a279 --- /dev/null +++ b/basctl/source/basicide/textwindowpeer.cxx @@ -0,0 +1,71 @@ +/* -*- 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 <sal/config.h> + +#include <vcl/svtaccessiblefactory.hxx> +#include <vcl/accessiblefactory.hxx> + +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <toolkit/awt/vclxwindow.hxx> +#include <vcl/texteng.hxx> +#include <vcl/textview.hxx> +#include <vcl/window.hxx> +#include "textwindowpeer.hxx" + +namespace { + +class TextWindowPeer final : public VCLXWindow { +public: + explicit TextWindowPeer(TextView & view); + + TextWindowPeer(const TextWindowPeer&) = delete; + TextWindowPeer& operator=(const TextWindowPeer&) = delete; + +private: + virtual css::uno::Reference<css::accessibility::XAccessibleContext> + CreateAccessibleContext() override; + + TextEngine & m_rEngine; + TextView & m_rView; + vcl::AccessibleFactoryAccess m_aFactoryAccess; +}; + +TextWindowPeer::TextWindowPeer(TextView & view): + m_rEngine(*view.GetTextEngine()), m_rView(view) +{ + SetWindow(view.GetWindow()); +} + +css::uno::Reference<css::accessibility::XAccessibleContext> +TextWindowPeer::CreateAccessibleContext() { + return m_aFactoryAccess.getFactory().createAccessibleTextWindowContext( + this, m_rEngine, m_rView); +} + +} + +css::uno::Reference<css::awt::XVclWindowPeer> basctl::createTextWindowPeer( + TextView & view) +{ + return new TextWindowPeer(view); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/textwindowpeer.hxx b/basctl/source/basicide/textwindowpeer.hxx new file mode 100644 index 0000000000..e29c4a412d --- /dev/null +++ b/basctl/source/basicide/textwindowpeer.hxx @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <com/sun/star/uno/Reference.hxx> + +namespace com::sun::star::awt +{ +class XVclWindowPeer; +} +class TextView; + +namespace basctl +{ +css::uno::Reference<css::awt::XVclWindowPeer> createTextWindowPeer(TextView& view); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/uiobject.cxx b/basctl/source/basicide/uiobject.cxx new file mode 100644 index 0000000000..b875b1eced --- /dev/null +++ b/basctl/source/basicide/uiobject.cxx @@ -0,0 +1,50 @@ +/* -*- 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/. + */ + +#include <memory> +#include "uiobject.hxx" +#include <vcl/xtextedt.hxx> + +namespace basctl +{ +EditorWindowUIObject::EditorWindowUIObject(const VclPtr<basctl::EditorWindow>& xEditorWindow) + : WindowUIObject(xEditorWindow) + , mxEditorWindow(xEditorWindow) +{ +} + +StringMap EditorWindowUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + ExtTextEngine* pEditEngine = mxEditorWindow->GetEditEngine(); + sal_Int32 i, nParas; + OUStringBuffer aRes; + for (i = 0, nParas = pEditEngine->GetParagraphCount(); i < nParas; ++i) + { + aRes.append(pEditEngine->GetText(i) + "\n"); + } + + aMap["Text"] = aRes.makeStringAndClear(); + + return aMap; +} + +std::unique_ptr<UIObject> EditorWindowUIObject::create(vcl::Window* pWindow) +{ + basctl::EditorWindow* pEditorWindow = dynamic_cast<basctl::EditorWindow*>(pWindow); + assert(pEditorWindow); + return std::unique_ptr<UIObject>(new EditorWindowUIObject(pEditorWindow)); +} + +OUString EditorWindowUIObject::get_name() const { return "EditorWindowUIObject"; } + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/uiobject.hxx b/basctl/source/basicide/uiobject.hxx new file mode 100644 index 0000000000..f3a1e5ec88 --- /dev/null +++ b/basctl/source/basicide/uiobject.hxx @@ -0,0 +1,35 @@ +/* -*- 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/. + */ + +#include <memory> +#include <vcl/uitest/uiobject.hxx> +#include "baside2.hxx" + +namespace basctl +{ +class EditorWindow; + +class EditorWindowUIObject : public WindowUIObject +{ + VclPtr<basctl::EditorWindow> mxEditorWindow; + +public: + EditorWindowUIObject(const VclPtr<basctl::EditorWindow>& xEditorWindow); + + virtual StringMap get_state() override; + + static std::unique_ptr<UIObject> create(vcl::Window* pWindow); + +protected: + virtual OUString get_name() const override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/unomodel.cxx b/basctl/source/basicide/unomodel.cxx new file mode 100644 index 0000000000..5d3946b426 --- /dev/null +++ b/basctl/source/basicide/unomodel.cxx @@ -0,0 +1,131 @@ +/* -*- 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 "basdoc.hxx" +#include <iderdll.hxx> +#include <com/sun/star/io/IOException.hpp> +#include <comphelper/sequence.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sfx2/objsh.hxx> +#include <vcl/svapp.hxx> + +#include "unomodel.hxx" + +namespace basctl +{ + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +SIDEModel::SIDEModel( SfxObjectShell *pObjSh ) +: SfxBaseModel(pObjSh) +{ +} + +SIDEModel::~SIDEModel() +{ +} + +uno::Any SAL_CALL SIDEModel::queryInterface( const uno::Type& rType ) +{ + uno::Any aRet = ::cppu::queryInterface ( rType, + // OWeakObject interfaces + static_cast< XInterface* >( static_cast< OWeakObject* >( this ) ), + static_cast< XWeak* > ( this ), + static_cast< XServiceInfo* > ( this ) ); + if (!aRet.hasValue()) + aRet = SfxBaseModel::queryInterface ( rType ); + return aRet; +} + +void SAL_CALL SIDEModel::acquire() noexcept +{ + SolarMutexGuard aGuard; + OWeakObject::acquire(); +} + +void SAL_CALL SIDEModel::release() noexcept +{ + SolarMutexGuard aGuard; + OWeakObject::release(); +} + +uno::Sequence< uno::Type > SAL_CALL SIDEModel::getTypes( ) +{ + return comphelper::concatSequences( + SfxBaseModel::getTypes(), + uno::Sequence { cppu::UnoType<XServiceInfo>::get() }); +} + +OUString SIDEModel::getImplementationName() +{ + return "com.sun.star.comp.basic.BasicIDE"; +} + +sal_Bool SIDEModel::supportsService(const OUString& rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +uno::Sequence< OUString > SIDEModel::getSupportedServiceNames() +{ + return { "com.sun.star.script.BasicIDE" }; +} + +// XStorable +void SAL_CALL SIDEModel::store() +{ + notImplemented(); +} + +void SAL_CALL SIDEModel::storeAsURL( const OUString&, const uno::Sequence< beans::PropertyValue >& ) +{ + notImplemented(); +} + +void SAL_CALL SIDEModel::storeToURL( const OUString&, + const uno::Sequence< beans::PropertyValue >& ) +{ + notImplemented(); +} + +void SIDEModel::notImplemented() +{ + throw io::IOException("Can't store IDE model" ); +} + +} // namespace basctl + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_basic_BasicID_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + SolarMutexGuard aGuard; + basctl::EnsureIde(); + SfxObjectShell* pShell = new basctl::DocShell(); + auto pModel = pShell->GetModel(); + pModel->acquire(); + return pModel.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/basicide/unomodel.hxx b/basctl/source/basicide/unomodel.hxx new file mode 100644 index 0000000000..b478730056 --- /dev/null +++ b/basctl/source/basicide/unomodel.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ +#pragma once + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <sfx2/sfxbasemodel.hxx> + +namespace basctl +{ + +class SIDEModel : public SfxBaseModel, + public com::sun::star::lang::XServiceInfo +{ + /// @throws css::io::IOException + static void notImplemented(); +public: + explicit SIDEModel(SfxObjectShell *pObjSh); + virtual ~SIDEModel() override; + + //XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept override; + + //XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + //XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + // XStorable2 + virtual void SAL_CALL storeSelf( const css::uno::Sequence< css::beans::PropertyValue >& ) override { notImplemented(); } + // XStorable + virtual void SAL_CALL store() override; + virtual void SAL_CALL storeAsURL( const OUString& sURL, + const css::uno::Sequence< css::beans::PropertyValue >& seqArguments ) override; + virtual void SAL_CALL storeToURL( const OUString& sURL, + const css::uno::Sequence< css::beans::PropertyValue >& seqArguments ) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlged.cxx b/basctl/source/dlged/dlged.cxx new file mode 100644 index 0000000000..2eb099718b --- /dev/null +++ b/basctl/source/dlged/dlged.cxx @@ -0,0 +1,1236 @@ +/* -*- 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 <dlged.hxx> +#include <dlgedclip.hxx> +#include <dlgeddef.hxx> +#include <dlgedfac.hxx> +#include <dlgedfunc.hxx> +#include <dlgedmod.hxx> +#include <dlgedobj.hxx> +#include <dlgedpage.hxx> +#include <dlgedview.hxx> +#include <localizationmgr.hxx> +#include <baside3.hxx> + +#include <com/sun/star/awt/Toolkit.hpp> +#include <com/sun/star/awt/UnoControlDialog.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/resource/StringResource.hpp> +#include <com/sun/star/util/XCloneable.hpp> +#include <com/sun/star/util/NumberFormatsSupplier.hpp> +#include <comphelper/types.hxx> +#include <comphelper/processfactory.hxx> +#include <tools/debug.hxx> +#include <svl/itempool.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/svdpagv.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/print.hxx> +#include <vcl/svapp.hxx> +#include <xmlscript/xml_helper.hxx> +#include <xmlscript/xmldlg_imexp.hxx> +#include <osl/diagnose.h> + +namespace basctl +{ + +using namespace comphelper; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::io; + +constexpr OUString aResourceResolverPropName = u"ResourceResolver"_ustr; +constexpr OUString aDecorationPropName = u"Decoration"_ustr; + + +// DlgEdHint + + +DlgEdHint::DlgEdHint(Kind eHint) + : eKind(eHint) + , pDlgEdObj(nullptr) +{ +} + +DlgEdHint::DlgEdHint(Kind eHint, DlgEdObj* pObj) + : eKind(eHint) + , pDlgEdObj(pObj) +{ +} + +DlgEdHint::~DlgEdHint() +{ +} + + +// DlgEditor + + +void DlgEditor::ShowDialog() +{ + uno::Reference< uno::XComponentContext > xContext = getProcessComponentContext(); + + // create a dialog + uno::Reference< awt::XUnoControlDialog > xDlg = awt::UnoControlDialog::create( xContext ); + + // clone the dialog model + uno::Reference< util::XCloneable > xC( m_xUnoControlDialogModel, uno::UNO_QUERY ); + uno::Reference< util::XCloneable > xNew = xC->createClone(); + uno::Reference< awt::XControlModel > xDlgMod( xNew, uno::UNO_QUERY ); + + uno::Reference< beans::XPropertySet > xSrcDlgModPropSet( m_xUnoControlDialogModel, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xNewDlgModPropSet( xDlgMod, uno::UNO_QUERY ); + if( xNewDlgModPropSet.is() ) + { + if( xSrcDlgModPropSet.is() ) + { + try + { + Any aResourceResolver = xSrcDlgModPropSet->getPropertyValue( aResourceResolverPropName ); + xNewDlgModPropSet->setPropertyValue( aResourceResolverPropName, aResourceResolver ); + } + catch(const UnknownPropertyException& ) + { + OSL_FAIL( "DlgEditor::ShowDialog(): No ResourceResolver property" ); + } + } + + // Disable decoration + try + { + bool bDecoration = true; + + Any aDecorationAny = xSrcDlgModPropSet->getPropertyValue( aDecorationPropName ); + aDecorationAny >>= bDecoration; + if( !bDecoration ) + { + xNewDlgModPropSet->setPropertyValue( aDecorationPropName, Any( true ) ); + xNewDlgModPropSet->setPropertyValue( "Title", Any( OUString() ) ); + } + } + catch(const UnknownPropertyException& ) + {} + } + + // set the model + xDlg->setModel( xDlgMod ); + + // create a peer + uno::Reference< awt::XToolkit> xToolkit = awt::Toolkit::create( xContext ); + xDlg->createPeer( xToolkit, rWindow.GetComponentInterface() ); + + xDlg->execute(); + + // need to cast because of multiple inheritance + Reference<awt::XControl>(xDlg)->dispose(); +} + + +bool DlgEditor::UnmarkDialog() +{ + SdrObject* pDlgObj = pDlgEdModel->GetPage(0)->GetObj(0); + SdrPageView* pPgView = pDlgEdView->GetSdrPageView(); + + bool bWasMarked = pDlgEdView->IsObjMarked( pDlgObj ); + + if( bWasMarked ) + pDlgEdView->MarkObj( pDlgObj, pPgView, true ); + + return bWasMarked; +} + + +bool DlgEditor::RemarkDialog() +{ + SdrObject* pDlgObj = pDlgEdModel->GetPage(0)->GetObj(0); + SdrPageView* pPgView = pDlgEdView->GetSdrPageView(); + + bool bWasMarked = pDlgEdView->IsObjMarked( pDlgObj ); + + if( !bWasMarked ) + pDlgEdView->MarkObj( pDlgObj, pPgView ); + + return bWasMarked; +} + + +DlgEditor::DlgEditor ( + vcl::Window& rWindow_, DialogWindowLayout& rLayout_, + css::uno::Reference<css::frame::XModel> const& xModel, + css::uno::Reference<css::container::XNameContainer> const & xDialogModel +) + :pHScroll(nullptr) + ,pVScroll(nullptr) + ,pDlgEdModel(new DlgEdModel()) + ,pDlgEdPage(new DlgEdPage(*pDlgEdModel)) + // set clipboard data flavors + ,m_ClipboardDataFlavors{ { /* MimeType */ "application/vnd.sun.xml.dialog", + /* HumanPresentableName */ "Dialog 6.0", + /* DataType */ cppu::UnoType<Sequence< sal_Int8 >>::get() } } + ,m_ClipboardDataFlavorsResource{ m_ClipboardDataFlavors[0], + { /* MimeType */ "application/vnd.sun.xml.dialogwithresource", + /* HumanPresentableName */ "Dialog 8.0", + /* DataType */ cppu::UnoType<Sequence< sal_Int8 >>::get() } } + ,pObjFac(new DlgEdFactory(xModel)) + ,rWindow(rWindow_) + ,pFunc(new DlgEdFuncSelect(*this)) + ,rLayout(rLayout_) + ,eMode( DlgEditor::SELECT ) + ,eActObj( SdrObjKind::BasicDialogPushButton ) + ,bFirstDraw(false) + ,bCreateOK(true) + ,bDialogModelChanged(false) + ,aMarkIdle("basctl DlgEditor Mark") + ,mnPaintGuard(0) + ,m_xDocument( xModel ) +{ + pDlgEdModel->GetItemPool().FreezeIdRanges(); + pDlgEdView.reset(new DlgEdView(*pDlgEdModel, *rWindow_.GetOutDev(), *this)); + pDlgEdModel->SetScaleUnit( MapUnit::Map100thMM ); + + SdrLayerAdmin& rAdmin = pDlgEdModel->GetLayerAdmin(); + rAdmin.NewLayer( rAdmin.GetControlLayerName() ); + rAdmin.NewLayer( "HiddenLayer" ); + + pDlgEdModel->InsertPage(pDlgEdPage); + + aMarkIdle.SetInvokeHandler( LINK( this, DlgEditor, MarkTimeout ) ); + + rWindow.SetMapMode( MapMode( MapUnit::Map100thMM ) ); + pDlgEdPage->SetSize( rWindow.PixelToLogic( Size(DLGED_PAGE_WIDTH_MIN, DLGED_PAGE_HEIGHT_MIN) ) ); + + pDlgEdView->ShowSdrPage(pDlgEdView->GetModel().GetPage(0)); + pDlgEdView->SetLayerVisible( "HiddenLayer", false ); + pDlgEdView->SetMoveSnapOnlyTopLeft(true); + pDlgEdView->SetWorkArea( tools::Rectangle( Point( 0, 0 ), pDlgEdPage->GetSize() ) ); + + Size aGridSize( 100, 100 ); // 100TH_MM + pDlgEdView->SetGridCoarse( aGridSize ); + pDlgEdView->SetSnapGridWidth(Fraction(aGridSize.Width(), 1), Fraction(aGridSize.Height(), 1)); + pDlgEdView->SetGridSnap( true ); + pDlgEdView->SetGridVisible( false ); + pDlgEdView->SetDragStripes(false); + + pDlgEdView->SetDesignMode(); + + ::comphelper::disposeComponent( m_xControlContainer ); + + SetDialog(xDialogModel); +} + +DlgEditor::~DlgEditor() +{ + aMarkIdle.Stop(); + + ::comphelper::disposeComponent( m_xControlContainer ); +} + +Reference< awt::XControlContainer > const & DlgEditor::GetWindowControlContainer() +{ + if (!m_xControlContainer.is()) + m_xControlContainer = VCLUnoHelper::CreateControlContainer(&rWindow); + return m_xControlContainer; +} + +void DlgEditor::SetScrollBars(ScrollAdaptor* pHS, ScrollAdaptor* pVS) +{ + pHScroll = pHS; + pVScroll = pVS; + + InitScrollBars(); +} + +void DlgEditor::InitScrollBars() +{ + DBG_ASSERT( pHScroll, "DlgEditor::InitScrollBars: no horizontal scroll bar!" ); + DBG_ASSERT( pVScroll, "DlgEditor::InitScrollBars: no vertical scroll bar!" ); + if ( !pHScroll || !pVScroll ) + return; + + Size aOutSize = rWindow.GetOutDev()->GetOutputSize(); + Size aPgSize = pDlgEdPage->GetSize(); + + pHScroll->SetRange( Range( 0, aPgSize.Width() )); + pVScroll->SetRange( Range( 0, aPgSize.Height() )); + pHScroll->SetVisibleSize( aOutSize.Width() ); + pVScroll->SetVisibleSize( aOutSize.Height() ); + + pHScroll->SetLineSize( aOutSize.Width() / 10 ); + pVScroll->SetLineSize( aOutSize.Height() / 10 ); + pHScroll->SetPageSize( aOutSize.Width() / 2 ); + pVScroll->SetPageSize( aOutSize.Height() / 2 ); + + DoScroll(); +} + + +void DlgEditor::DoScroll() +{ + if( !pHScroll || !pVScroll ) + return; + + MapMode aMap = rWindow.GetMapMode(); + Point aOrg = aMap.GetOrigin(); + + Size aScrollPos( pHScroll->GetThumbPos(), pVScroll->GetThumbPos() ); + aScrollPos = rWindow.LogicToPixel( aScrollPos ); + aScrollPos = rWindow.PixelToLogic( aScrollPos ); + + tools::Long nX = aScrollPos.Width() + aOrg.X(); + tools::Long nY = aScrollPos.Height() + aOrg.Y(); + + if( !nX && !nY ) + return; + + rWindow.PaintImmediately(); + + // #i31562# + // When scrolling, someone was rescuing the Wallpaper and forced the window scroll to + // be done without background refresh. I do not know why, but that causes the repaint + // problems. Taking that out. + // Wallpaper aOldBackground = rWindow.GetBackground(); + // rWindow.SetBackground(); + + // #i74769# children should be scrolled + rWindow.Scroll( -nX, -nY, ScrollFlags::Children); + aMap.SetOrigin( Point( -aScrollPos.Width(), -aScrollPos.Height() ) ); + rWindow.SetMapMode( aMap ); + rWindow.PaintImmediately(); + + DlgEdHint aHint( DlgEdHint::WINDOWSCROLLED ); + Broadcast( aHint ); +} + + +void DlgEditor::UpdateScrollBars() +{ + MapMode aMap = rWindow.GetMapMode(); + Point aOrg = aMap.GetOrigin(); + + if ( pHScroll ) + pHScroll->SetThumbPos( -aOrg.X() ); + + if ( pVScroll ) + pVScroll->SetThumbPos( -aOrg.Y() ); +} + + +void DlgEditor::SetDialog( const uno::Reference< container::XNameContainer >& xUnoControlDialogModel ) +{ + // set dialog model + m_xUnoControlDialogModel = xUnoControlDialogModel; + + // create dialog form + pDlgEdForm = new DlgEdForm(*pDlgEdModel, *this); + uno::Reference< awt::XControlModel > xDlgMod( m_xUnoControlDialogModel , uno::UNO_QUERY ); + pDlgEdForm->SetUnoControlModel(xDlgMod); + static_cast<DlgEdPage*>(pDlgEdModel->GetPage(0))->SetDlgEdForm( pDlgEdForm.get() ); + pDlgEdModel->GetPage(0)->InsertObject( pDlgEdForm.get() ); + AdjustPageSize(); + pDlgEdForm->SetRectFromProps(); + pDlgEdForm->UpdateTabIndices(); // for backward compatibility + pDlgEdForm->StartListening(); + + // create controls + if ( m_xUnoControlDialogModel.is() ) + { + // get sequence of control names + Sequence< OUString > aNames = m_xUnoControlDialogModel->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nCtrls = aNames.getLength(); + + // create a map of tab indices and control names, sorted by tab index + IndexToNameMap aIndexToNameMap; + for ( sal_Int32 i = 0; i < nCtrls; ++i ) + { + // get name + OUString aName( pNames[i] ); + + // get tab index + sal_Int16 nTabIndex = -1; + Any aCtrl = m_xUnoControlDialogModel->getByName( aName ); + Reference< css::beans::XPropertySet > xPSet; + aCtrl >>= xPSet; + if ( xPSet.is() ) + xPSet->getPropertyValue( DLGED_PROP_TABINDEX ) >>= nTabIndex; + + // insert into map + aIndexToNameMap.emplace( nTabIndex, aName ); + } + + // create controls and insert them into drawing page + for (auto const& indexToName : aIndexToNameMap) + { + Any aCtrl = m_xUnoControlDialogModel->getByName( indexToName.second ); + Reference< css::awt::XControlModel > xCtrlModel; + aCtrl >>= xCtrlModel; + rtl::Reference<DlgEdObj> pCtrlObj = new DlgEdObj(*pDlgEdModel); + pCtrlObj->SetUnoControlModel( xCtrlModel ); + pCtrlObj->SetDlgEdForm( pDlgEdForm.get() ); + pDlgEdForm->AddChild( pCtrlObj.get() ); + pDlgEdModel->GetPage(0)->InsertObject( pCtrlObj.get() ); + pCtrlObj->SetRectFromProps(); + pCtrlObj->UpdateStep(); + pCtrlObj->StartListening(); + } + } + + bFirstDraw = true; + + pDlgEdModel->SetChanged(false); +} + +void DlgEditor::ResetDialog () +{ + DlgEdForm* pOldDlgEdForm = pDlgEdForm.get(); + DlgEdPage* pPage = static_cast<DlgEdPage*>(pDlgEdModel->GetPage(0)); + SdrPageView* pPgView = pDlgEdView->GetSdrPageView(); + bool bWasMarked = pDlgEdView->IsObjMarked( pOldDlgEdForm ); + pDlgEdView->UnmarkAll(); + + // clear SdrObjects with broadcasting + pPage->ClearSdrObjList(); + + pPage->SetDlgEdForm( nullptr ); + SetDialog( m_xUnoControlDialogModel ); + if( bWasMarked ) + pDlgEdView->MarkObj( pDlgEdForm.get(), pPgView ); +} + + +Reference< util::XNumberFormatsSupplier > const & DlgEditor::GetNumberFormatsSupplier() +{ + if ( !m_xSupplier.is() ) + { + Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< util::XNumberFormatsSupplier > xSupplier( util::NumberFormatsSupplier::createWithDefaultLocale(xContext) ); + + ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() ); + if ( !m_xSupplier.is() ) + { + m_xSupplier = xSupplier; + } + } + return m_xSupplier; +} + + +void DlgEditor::MouseButtonDown( const MouseEvent& rMEvt ) +{ + rWindow.GrabFocus(); + pFunc->MouseButtonDown( rMEvt ); +} + + +void DlgEditor::MouseButtonUp( const MouseEvent& rMEvt ) +{ + bool bRet = pFunc->MouseButtonUp( rMEvt ); + + if( eMode == DlgEditor::INSERT ) + bCreateOK = bRet; +} + + +void DlgEditor::MouseMove( const MouseEvent& rMEvt ) +{ + pFunc->MouseMove( rMEvt ); +} + + +bool DlgEditor::KeyInput( const KeyEvent& rKEvt ) +{ + return pFunc->KeyInput( rKEvt ); +} + + +void DlgEditor::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + aPaintRect = rRect; + mnPaintGuard++; + + if (bFirstDraw && rWindow.IsVisible() && (rRenderContext.GetOutputSize() != Size())) + { + bFirstDraw = false; + + // get property set + css::uno::Reference<css::beans::XPropertySet> xPSet(pDlgEdForm->GetUnoControlModel(), css::uno::UNO_QUERY); + + if (xPSet.is()) + { + // get dialog size from properties + sal_Int32 nWidth = 0, nHeight = 0; + xPSet->getPropertyValue( DLGED_PROP_WIDTH ) >>= nWidth; + xPSet->getPropertyValue( DLGED_PROP_HEIGHT ) >>= nHeight; + + if (nWidth == 0 && nHeight == 0) + { + Size aSize = rRenderContext.PixelToLogic( Size( 400, 300 ) ); + + // align with grid + Size aGridSize_(tools::Long(pDlgEdView->GetSnapGridWidthX()), tools::Long(pDlgEdView->GetSnapGridWidthY())); + aSize.AdjustWidth( -(aSize.Width() % aGridSize_.Width()) ); + aSize.AdjustHeight( -(aSize.Height() % aGridSize_.Height()) ); + + Point aPos; + Size aOutSize = rRenderContext.GetOutputSize(); + aPos.setX( (aOutSize.Width()>>1) - (aSize.Width()>>1) ); + aPos.setY( (aOutSize.Height()>>1) - (aSize.Height()>>1) ); + + // align with grid + aPos.AdjustX( -(aPos.X() % aGridSize_.Width()) ); + aPos.AdjustY( -(aPos.Y() % aGridSize_.Height()) ); + + // don't put in the corner + Point aMinPos = rRenderContext.PixelToLogic( Point( 30, 20 ) ); + if( (aPos.X() < aMinPos.X()) || (aPos.Y() < aMinPos.Y()) ) + { + aPos = aMinPos; + aPos.AdjustX( -(aPos.X() % aGridSize_.Width()) ); + aPos.AdjustY( -(aPos.Y() % aGridSize_.Height()) ); + } + + // set dialog position and size + pDlgEdForm->SetSnapRect( tools::Rectangle( aPos, aSize ) ); + pDlgEdForm->EndListening(false); + pDlgEdForm->SetPropsFromRect(); + pDlgEdForm->GetDlgEditor().SetDialogModelChanged(); + pDlgEdForm->StartListening(); + + // set position and size of controls + for (const rtl::Reference<SdrObject>& pObj : *pDlgEdPage) + { + if (DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj.get())) + { + if (!dynamic_cast<DlgEdForm*>(pDlgEdObj)) + { + pDlgEdObj->SetRectFromProps(); + } + } + } + } + } + } + + // repaint, get PageView and prepare Region + SdrPageView* pPgView = pDlgEdView->GetSdrPageView(); + const vcl::Region aPaintRectRegion(aPaintRect); + + // #i74769# + SdrPaintWindow* pTargetPaintWindow = nullptr; + + // mark repaint start + if (pPgView) + { + pTargetPaintWindow = pPgView->GetView().BeginDrawLayers(&rRenderContext, aPaintRectRegion); + OSL_ENSURE(pTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)"); + } + + // draw background self using wallpaper + // #i79128# ...and use correct OutDev for that + if (pTargetPaintWindow) + { + Color maBackColor = rRenderContext.GetSettings().GetStyleSettings().GetLightColor(); + OutputDevice& rTargetOutDev = pTargetPaintWindow->GetTargetOutputDevice(); + rTargetOutDev.DrawWallpaper(aPaintRect, Wallpaper(maBackColor)); + } + + // do paint (unbuffered) and mark repaint end + if (pPgView) + { + // paint of control layer is done in EndDrawLayers anyway... + pPgView->GetView().EndDrawLayers(*pTargetPaintWindow, true); + } + + mnPaintGuard--; +} + + +IMPL_LINK_NOARG(DlgEditor, MarkTimeout, Timer *, void) +{ + rLayout.UpdatePropertyBrowser(); +} + + +void DlgEditor::SetMode (Mode eNewMode ) +{ + if ( eNewMode != eMode ) + { + if ( eNewMode == INSERT ) + pFunc.reset(new DlgEdFuncInsert(*this)); + else + pFunc.reset(new DlgEdFuncSelect(*this)); + + if ( eNewMode == READONLY ) + pDlgEdModel->SetReadOnly( true ); + else + pDlgEdModel->SetReadOnly( false ); + } + + if ( eNewMode == TEST ) + ShowDialog(); + + eMode = eNewMode; +} + + +void DlgEditor::SetInsertObj(SdrObjKind eObj) +{ + eActObj = eObj; + + pDlgEdView->SetCurrentObj( eActObj, SdrInventor::BasicDialog ); +} + +void DlgEditor::CreateDefaultObject() +{ + // create object by factory + rtl::Reference<SdrObject> pObj = SdrObjFactory::MakeNewObject( + *pDlgEdModel, + pDlgEdView->GetCurrentObjInventor(), + pDlgEdView->GetCurrentObjIdentifier()); + + DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj.get()); + if (!pDlgEdObj) + return; + + // set position and size + Size aSize = rWindow.PixelToLogic( Size( 96, 24 ) ); + Point aPoint = pDlgEdForm->GetSnapRect().Center(); + aPoint.AdjustX( -(aSize.Width() / 2) ); + aPoint.AdjustY( -(aSize.Height() / 2) ); + pDlgEdObj->SetSnapRect( tools::Rectangle( aPoint, aSize ) ); + + // set default property values + pDlgEdObj->SetDefaults(); + + // insert object into drawing page + SdrPageView* pPageView = pDlgEdView->GetSdrPageView(); + if (pDlgEdView->InsertObjectAtView(pDlgEdObj, *pPageView)) + { + // start listening + pDlgEdObj->StartListening(); + } +} + +void DlgEditor::Cut() +{ + Copy(); + Delete(); +} + +static void implCopyStreamToByteSequence( const Reference< XInputStream >& xStream, + Sequence< sal_Int8 >& bytes ) +{ + xStream->readBytes( bytes, xStream->available() ); + for (;;) + { + Sequence< sal_Int8 > readBytes; + sal_Int32 nRead = xStream->readBytes( readBytes, 1024 ); + if (! nRead) + break; + + sal_Int32 nPos = bytes.getLength(); + bytes.realloc( nPos + nRead ); + memcpy( bytes.getArray() + nPos, readBytes.getConstArray(), static_cast<sal_uInt32>(nRead) ); + } +} + +void DlgEditor::Copy() +{ + if( !pDlgEdView->AreObjectsMarked() ) + return; + + // stop all drawing actions + pDlgEdView->BrkAction(); + + // create an empty clipboard dialog model + Reference< util::XCloneable > xClone( m_xUnoControlDialogModel, UNO_QUERY ); + Reference< util::XCloneable > xNewClone = xClone->createClone(); + Reference< container::XNameContainer > xClipDialogModel( xNewClone, UNO_QUERY ); + + if ( xClipDialogModel.is() ) + { + Sequence< OUString > aNames = xClipDialogModel->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_uInt32 nCtrls = aNames.getLength(); + + for ( sal_uInt32 n = 0; n < nCtrls; n++ ) + { + xClipDialogModel->removeByName( pNames[n] ); + } + } + + // insert control models of marked objects into clipboard dialog model + const size_t nMark = pDlgEdView->GetMarkedObjectList().GetMarkCount(); + for( size_t i = 0; i < nMark; ++i ) + { + SdrObject* pObj = pDlgEdView->GetMarkedObjectList().GetMark(i)->GetMarkedSdrObj(); + DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj); + + if (pDlgEdObj && !dynamic_cast<DlgEdForm*>(pDlgEdObj)) + { + OUString aName; + Reference< beans::XPropertySet > xMarkPSet(pDlgEdObj->GetUnoControlModel(), uno::UNO_QUERY); + if (xMarkPSet.is()) + { + xMarkPSet->getPropertyValue( DLGED_PROP_NAME ) >>= aName; + } + + if ( m_xUnoControlDialogModel.is() && m_xUnoControlDialogModel->hasByName(aName) ) + { + Any aCtrl = m_xUnoControlDialogModel->getByName( aName ); + + // clone control model + Reference< util::XCloneable > xCtrl; + aCtrl >>= xCtrl; + Reference< util::XCloneable > xNewCtrl = xCtrl->createClone(); + Any aNewCtrl; + aNewCtrl <<= xNewCtrl; + + if (xClipDialogModel.is()) + xClipDialogModel->insertByName( aName , aNewCtrl ); + } + } + } + + // export clipboard dialog model to xml + Reference< XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XInputStreamProvider > xISP = ::xmlscript::exportDialogModel( xClipDialogModel, xContext, m_xDocument ); + Reference< XInputStream > xStream( xISP->createInputStream() ); + Sequence< sal_Int8 > DialogModelBytes; + implCopyStreamToByteSequence( xStream, DialogModelBytes ); + xStream->closeInput(); + + // set clipboard content + Reference< datatransfer::clipboard::XClipboard > xClipboard = GetWindow().GetClipboard(); + if ( !xClipboard.is() ) + return; + + // With resource? + uno::Reference< beans::XPropertySet > xDialogModelPropSet( m_xUnoControlDialogModel, uno::UNO_QUERY ); + uno::Reference< resource::XStringResourcePersistence > xStringResourcePersistence; + if( xDialogModelPropSet.is() ) + { + try + { + Any aResourceResolver = xDialogModelPropSet->getPropertyValue( aResourceResolverPropName ); + aResourceResolver >>= xStringResourcePersistence; + } + catch(const UnknownPropertyException& ) + {} + } + + rtl::Reference<DlgEdTransferableImpl> pTrans; + if( xStringResourcePersistence.is() ) + { + // With resource, support old and new format + + // Export xClipDialogModel another time with ids replaced by current language string + LocalizationMgr::resetResourceForDialog( xClipDialogModel, xStringResourcePersistence ); + Reference< XInputStreamProvider > xISP2 = ::xmlscript::exportDialogModel( xClipDialogModel, xContext, m_xDocument ); + Reference< XInputStream > xStream2( xISP2->createInputStream() ); + Sequence< sal_Int8 > NoResourceDialogModelBytes; + implCopyStreamToByteSequence( xStream2, NoResourceDialogModelBytes ); + xStream2->closeInput(); + + // Old format contains dialog with replaced ids + Any aNoResourceDialogModelBytesAny; + aNoResourceDialogModelBytesAny <<= NoResourceDialogModelBytes; + + // New format contains dialog and resource + Sequence< sal_Int8 > aResData = xStringResourcePersistence->exportBinary(); + + // Create sequence for combined dialog and resource + sal_Int32 nDialogDataLen = DialogModelBytes.getLength(); + sal_Int32 nResDataLen = aResData.getLength(); + + // Combined data = 4 Bytes 32Bit Offset to begin of resource data, lowest byte first + // + nDialogDataLen bytes dialog data + nResDataLen resource data + sal_Int32 nTotalLen = 4 + nDialogDataLen + nResDataLen; + sal_Int32 nResOffset = 4 + nDialogDataLen; + Sequence< sal_Int8 > aCombinedData( nTotalLen ); + sal_Int8* pCombinedData = aCombinedData.getArray(); + + // Write offset + sal_Int32 n = nResOffset; + for( sal_Int16 i = 0 ; i < 4 ; i++ ) + { + pCombinedData[i] = sal_Int8( n & 0xff ); + n >>= 8; + } + memcpy( pCombinedData + 4, DialogModelBytes.getConstArray(), nDialogDataLen ); + memcpy( pCombinedData + nResOffset, aResData.getConstArray(), nResDataLen ); + + Sequence< Any > aSeqData + { + aNoResourceDialogModelBytesAny, + Any(aCombinedData) + }; + + pTrans = new DlgEdTransferableImpl( m_ClipboardDataFlavorsResource, aSeqData ); + } + else + { + // No resource, support only old format + pTrans = new DlgEdTransferableImpl( m_ClipboardDataFlavors , { Any(DialogModelBytes) } ); + } + SolarMutexReleaser aReleaser; + xClipboard->setContents( pTrans , pTrans ); +} + + +void DlgEditor::Paste() +{ + // stop all drawing actions + pDlgEdView->BrkAction(); + + // unmark all objects + pDlgEdView->UnmarkAll(); + + // get clipboard + Reference< datatransfer::clipboard::XClipboard > xClipboard = GetWindow().GetClipboard(); + if ( !xClipboard.is() ) + return; + + Reference< datatransfer::XTransferable > xTransf; + { + SolarMutexReleaser aReleaser; + // get clipboard content + xTransf = xClipboard->getContents(); + } + if ( !xTransf.is() ) + return; + + // Is target dialog (library) localized? + uno::Reference< beans::XPropertySet > xDialogModelPropSet( m_xUnoControlDialogModel, uno::UNO_QUERY ); + uno::Reference< resource::XStringResourceManager > xStringResourceManager; + if( xDialogModelPropSet.is() ) + { + try + { + Any aResourceResolver = xDialogModelPropSet->getPropertyValue( aResourceResolverPropName ); + aResourceResolver >>= xStringResourceManager; + } + catch(const UnknownPropertyException& ) + {} + } + bool bLocalized = false; + if( xStringResourceManager.is() ) + bLocalized = xStringResourceManager->getLocales().hasElements(); + + if ( !xTransf->isDataFlavorSupported( m_ClipboardDataFlavors[0] ) ) + return; + + // create clipboard dialog model from xml + Reference< XComponentContext > xContext = comphelper::getProcessComponentContext(); + Reference< container::XNameContainer > xClipDialogModel( xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.awt.UnoControlDialogModel", xContext ), uno::UNO_QUERY ); + + bool bSourceIsLocalized = false; + Sequence< sal_Int8 > DialogModelBytes; + Sequence< sal_Int8 > aResData; + if( bLocalized && xTransf->isDataFlavorSupported( m_ClipboardDataFlavorsResource[1] ) ) + { + bSourceIsLocalized = true; + + Any aCombinedDataAny = xTransf->getTransferData( m_ClipboardDataFlavorsResource[1] ); + Sequence< sal_Int8 > aCombinedData; + aCombinedDataAny >>= aCombinedData; + const sal_Int8* pCombinedData = aCombinedData.getConstArray(); + + sal_Int32 nTotalLen = aCombinedData.getLength(); + + // Reading offset + sal_Int32 nResOffset = 0; + sal_Int32 nFactor = 1; + for( sal_Int16 i = 0; i < 4; i++ ) + { + nResOffset += nFactor * sal_uInt8( pCombinedData[i] ); + nFactor *= 256; + } + + sal_Int32 nResDataLen = nTotalLen - nResOffset; + sal_Int32 nDialogDataLen = nTotalLen - nResDataLen - 4; + + DialogModelBytes.realloc( nDialogDataLen ); + memcpy( DialogModelBytes.getArray(), pCombinedData + 4, nDialogDataLen ); + + aResData.realloc( nResDataLen ); + memcpy( aResData.getArray(), pCombinedData + nResOffset, nResDataLen ); + } + else + { + Any aAny = xTransf->getTransferData( m_ClipboardDataFlavors[0] ); + aAny >>= DialogModelBytes; + } + + if ( xClipDialogModel.is() ) + { + Reference<XInputStream> xIn = ::xmlscript::createInputStream( DialogModelBytes.getConstArray(), DialogModelBytes.getLength() ); + ::xmlscript::importDialogModel( xIn , xClipDialogModel, xContext, m_xDocument ); + } + + // get control models from clipboard dialog model + if ( !xClipDialogModel.is() ) + return; + + Sequence< OUString > aNames = xClipDialogModel->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_uInt32 nCtrls = aNames.getLength(); + + Reference< resource::XStringResourcePersistence > xStringResourcePersistence; + if( nCtrls > 0 && bSourceIsLocalized ) + { + xStringResourcePersistence = css::resource::StringResource::create( getProcessComponentContext() ); + xStringResourcePersistence->importBinary( aResData ); + } + for( sal_uInt32 n = 0; n < nCtrls; n++ ) + { + Any aA = xClipDialogModel->getByName( pNames[n] ); + Reference< css::awt::XControlModel > xCM; + aA >>= xCM; + + // clone the control model + Reference< util::XCloneable > xClone( xCM, uno::UNO_QUERY ); + Reference< awt::XControlModel > xCtrlModel( xClone->createClone(), uno::UNO_QUERY ); + + rtl::Reference<DlgEdObj> pCtrlObj = new DlgEdObj(*pDlgEdModel); + pCtrlObj->SetDlgEdForm(pDlgEdForm.get()); // set parent form + pDlgEdForm->AddChild(pCtrlObj.get()); // add child to parent form + pCtrlObj->SetUnoControlModel( xCtrlModel ); // set control model + + // set new name + OUString aOUniqueName( pCtrlObj->GetUniqueName() ); + Reference< beans::XPropertySet > xPSet( xCtrlModel , UNO_QUERY ); + xPSet->setPropertyValue( DLGED_PROP_NAME, Any(aOUniqueName) ); + + // set tabindex + Sequence< OUString > aNames_ = m_xUnoControlDialogModel->getElementNames(); + xPSet->setPropertyValue( DLGED_PROP_TABINDEX, Any(static_cast<sal_Int16>(aNames_.getLength())) ); + + if( bLocalized ) + { + Any aControlAny; + aControlAny <<= xCtrlModel; + if( bSourceIsLocalized && xStringResourcePersistence.is() ) + { + LocalizationMgr::copyResourcesForPastedEditorObject( this, + aControlAny, aOUniqueName, xStringResourcePersistence ); + } + else + { + LocalizationMgr::setControlResourceIDsForNewEditorObject + ( this, aControlAny, aOUniqueName ); + } + } + + // insert control model in editor dialog model + Any aCtrlModel; + aCtrlModel <<= xCtrlModel; + m_xUnoControlDialogModel->insertByName( aOUniqueName , aCtrlModel ); + + // insert object into drawing page + pDlgEdModel->GetPage(0)->InsertObject( pCtrlObj.get() ); + pCtrlObj->SetRectFromProps(); + pCtrlObj->UpdateStep(); + pDlgEdForm->UpdateTabOrderAndGroups(); + pCtrlObj->StartListening(); // start listening + + // mark object + SdrPageView* pPgView = pDlgEdView->GetSdrPageView(); + pDlgEdView->MarkObj( pCtrlObj.get(), pPgView, false, true); + } + + // center marked objects in dialog editor form + Point aMarkCenter = pDlgEdView->GetMarkedObjRect().Center(); + Point aFormCenter = pDlgEdForm->GetSnapRect().Center(); + Point aPoint = aFormCenter - aMarkCenter; + Size aSize( aPoint.X() , aPoint.Y() ); + pDlgEdView->MoveMarkedObj( aSize ); // update of control model properties (position + size) in NbcMove + pDlgEdView->MarkListHasChanged(); + + // dialog model changed + SetDialogModelChanged(); +} + + +void DlgEditor::Delete() +{ + if( !pDlgEdView->AreObjectsMarked() ) + return; + + // remove control models of marked objects from dialog model + const size_t nMark = pDlgEdView->GetMarkedObjectList().GetMarkCount(); + + for( size_t i = 0; i < nMark; ++i ) + { + SdrObject* pObj = pDlgEdView->GetMarkedObjectList().GetMark(i)->GetMarkedSdrObj(); + DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj); + + if ( pDlgEdObj && !dynamic_cast<DlgEdForm*>(pDlgEdObj) ) + { + // get name from property + OUString aName; + uno::Reference< beans::XPropertySet > xPSet(pDlgEdObj->GetUnoControlModel(), uno::UNO_QUERY); + if (xPSet.is()) + { + xPSet->getPropertyValue( DLGED_PROP_NAME ) >>= aName; + } + + // remove control from dialog model + Reference< css::container::XNameAccess > xNameAcc(pDlgEdObj->GetDlgEdForm()->GetUnoControlModel(), UNO_QUERY ); + if ( xNameAcc.is() && xNameAcc->hasByName(aName) ) + { + Reference< css::container::XNameContainer > xCont(xNameAcc, UNO_QUERY ); + if ( xCont.is() ) + { + if( xCont->hasByName( aName ) ) + { + Any aAny = xCont->getByName( aName ); + LocalizationMgr::deleteControlResourceIDsForDeletedEditorObject( this, aAny, aName ); + } + xCont->removeByName( aName ); + } + } + + // remove child from parent form + pDlgEdForm->RemoveChild( pDlgEdObj ); + } + } + + // update tab indices + pDlgEdForm->UpdateTabIndices(); + + pDlgEdView->BrkAction(); + + bool const bDlgMarked = UnmarkDialog(); + pDlgEdView->DeleteMarked(); + if( bDlgMarked ) + RemarkDialog(); +} + + +bool DlgEditor::IsPasteAllowed() +{ + // get clipboard + Reference< datatransfer::clipboard::XClipboard > xClipboard = GetWindow().GetClipboard(); + if ( xClipboard.is() ) + { + Reference< datatransfer::XTransferable > xTransf; + { + SolarMutexReleaser aReleaser; + // get clipboard content + xTransf = xClipboard->getContents(); + } + if (xTransf.is()) + return xTransf->isDataFlavorSupported(m_ClipboardDataFlavors[0]); + } + return false; +} + + +void DlgEditor::ShowProperties() +{ + rLayout.ShowPropertyBrowser(); +} + + +void DlgEditor::UpdatePropertyBrowserDelayed() +{ + aMarkIdle.Start(); +} + + +bool DlgEditor::IsModified() const +{ + return pDlgEdModel->IsChanged() || bDialogModelChanged; +} + + +void DlgEditor::ClearModifyFlag() +{ + pDlgEdModel->SetChanged(false); + bDialogModelChanged = false; +} + + +namespace Print +{ + tools::Long const nLeftMargin = 1700; + tools::Long const nRightMargin = 900; + tools::Long const nTopMargin = 2000; + tools::Long const nBottomMargin = 1000; + tools::Long const nBorder = 300; +} + +static void lcl_PrintHeader( Printer* pPrinter, const OUString& rTitle ) // not working yet +{ + + pPrinter->Push(); + + Size const aSz = pPrinter->GetOutputSize(); + + pPrinter->SetLineColor( COL_BLACK ); + pPrinter->SetFillColor(); + + vcl::Font aFont( pPrinter->GetFont() ); + aFont.SetWeight( WEIGHT_BOLD ); + aFont.SetAlignment( ALIGN_BOTTOM ); + pPrinter->SetFont( aFont ); + + tools::Long const nFontHeight = pPrinter->GetTextHeight(); + + // 1st border => line, 2+3 border = free space + tools::Long const nYTop = Print::nTopMargin - 3*Print::nBorder - nFontHeight; + + tools::Long const nXLeft = Print::nLeftMargin - Print::nBorder; + tools::Long const nXRight = aSz.Width() - Print::nRightMargin + Print::nBorder; + + pPrinter->DrawRect(tools::Rectangle( + Point(nXLeft, nYTop), + Size(nXRight - nXLeft, aSz.Height() - nYTop - Print::nBottomMargin + Print::nBorder) + )); + + tools::Long nY = Print::nTopMargin - 2*Print::nBorder; + Point aPos(Print::nLeftMargin, nY); + pPrinter->DrawText( aPos, rTitle ); + + nY = Print::nTopMargin - Print::nBorder; + pPrinter->DrawLine( Point( nXLeft, nY ), Point( nXRight, nY ) ); + + pPrinter->Pop(); +} + + +void DlgEditor::printPage( sal_Int32 nPage, Printer* pPrinter, const OUString& rTitle ) +{ + if( nPage == 0 ) + Print( pPrinter, rTitle ); +} + + +void DlgEditor::Print( Printer* pPrinter, const OUString& rTitle ) // not working yet +{ + MapMode aOldMap( pPrinter->GetMapMode()); + vcl::Font aOldFont( pPrinter->GetFont() ); + + MapMode aMap( MapUnit::Map100thMM ); + pPrinter->SetMapMode( aMap ); + vcl::Font aFont; + aFont.SetAlignment( ALIGN_BOTTOM ); + aFont.SetFontSize( Size( 0, 360 )); + pPrinter->SetFont( aFont ); + + Size aPaperSz = pPrinter->GetOutputSize(); + aPaperSz.AdjustWidth( -(Print::nLeftMargin + Print::nRightMargin) ); + aPaperSz.AdjustHeight( -(Print::nTopMargin + Print::nBottomMargin) ); + + lcl_PrintHeader( pPrinter, rTitle ); + + BitmapEx aDlgEx; + Size aBmpSz( pPrinter->PixelToLogic( aDlgEx.GetSizePixel() ) ); + double nPaperSzWidth = aPaperSz.Width(); + double nPaperSzHeight = aPaperSz.Height(); + double nBmpSzWidth = aBmpSz.Width(); + double nBmpSzHeight = aBmpSz.Height(); + double nScaleX = nPaperSzWidth / nBmpSzWidth; + double nScaleY = nPaperSzHeight / nBmpSzHeight; + + Size aOutputSz; + if( nBmpSzHeight * nScaleX <= nPaperSzHeight ) + { + aOutputSz.setWidth( static_cast<tools::Long>(nBmpSzWidth * nScaleX) ); + aOutputSz.setHeight( static_cast<tools::Long>(nBmpSzHeight * nScaleX) ); + } + else + { + aOutputSz.setWidth( static_cast<tools::Long>(nBmpSzWidth * nScaleY) ); + aOutputSz.setHeight( static_cast<tools::Long>(nBmpSzHeight * nScaleY) ); + } + + Point aPosOffs( + (aPaperSz.Width() / 2) - (aOutputSz.Width() / 2), + (aPaperSz.Height()/ 2) - (aOutputSz.Height() / 2)); + + aPosOffs.AdjustX(Print::nLeftMargin ); + aPosOffs.AdjustY(Print::nTopMargin ); + + pPrinter->DrawBitmapEx( aPosOffs, aOutputSz, aDlgEx ); + + pPrinter->SetMapMode( aOldMap ); + pPrinter->SetFont( aOldFont ); +} + + +bool DlgEditor::AdjustPageSize() +{ + bool bAdjustedPageSize = false; + Reference< beans::XPropertySet > xPSet( m_xUnoControlDialogModel, UNO_QUERY ); + if ( xPSet.is() ) + { + sal_Int32 nFormXIn = 0, nFormYIn = 0, nFormWidthIn = 0, nFormHeightIn = 0; + xPSet->getPropertyValue( DLGED_PROP_POSITIONX ) >>= nFormXIn; + xPSet->getPropertyValue( DLGED_PROP_POSITIONY ) >>= nFormYIn; + xPSet->getPropertyValue( DLGED_PROP_WIDTH ) >>= nFormWidthIn; + xPSet->getPropertyValue( DLGED_PROP_HEIGHT ) >>= nFormHeightIn; + + sal_Int32 nFormX, nFormY, nFormWidth, nFormHeight; + if ( pDlgEdForm && pDlgEdForm->TransformFormToSdrCoordinates( nFormXIn, nFormYIn, nFormWidthIn, nFormHeightIn, nFormX, nFormY, nFormWidth, nFormHeight ) ) + { + Size aPageSizeDelta( 400, 300 ); + aPageSizeDelta = rWindow.PixelToLogic( aPageSizeDelta, MapMode( MapUnit::Map100thMM ) ); + + sal_Int32 nNewPageWidth = nFormX + nFormWidth + aPageSizeDelta.Width(); + sal_Int32 nNewPageHeight = nFormY + nFormHeight + aPageSizeDelta.Height(); + + Size aPageSizeMin( DLGED_PAGE_WIDTH_MIN, DLGED_PAGE_HEIGHT_MIN ); + aPageSizeMin = rWindow.PixelToLogic( aPageSizeMin, MapMode( MapUnit::Map100thMM ) ); + sal_Int32 nPageWidthMin = aPageSizeMin.Width(); + sal_Int32 nPageHeightMin = aPageSizeMin.Height(); + + if ( nNewPageWidth < nPageWidthMin ) + nNewPageWidth = nPageWidthMin; + + if ( nNewPageHeight < nPageHeightMin ) + nNewPageHeight = nPageHeightMin; + + if ( pDlgEdPage ) + { + Size aPageSize = pDlgEdPage->GetSize(); + if ( nNewPageWidth != aPageSize.Width() || nNewPageHeight != aPageSize.Height() ) + { + Size aNewPageSize( nNewPageWidth, nNewPageHeight ); + pDlgEdPage->SetSize( aNewPageSize ); + pDlgEdView->SetWorkArea( tools::Rectangle( Point( 0, 0 ), aNewPageSize ) ); + bAdjustedPageSize = true; + } + } + } + } + + return bAdjustedPageSize; +} + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedclip.cxx b/basctl/source/dlged/dlgedclip.cxx new file mode 100644 index 0000000000..931f10afe5 --- /dev/null +++ b/basctl/source/dlged/dlgedclip.cxx @@ -0,0 +1,109 @@ +/* -*- 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 <dlgedclip.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp> +#include <com/sun/star/datatransfer/XMimeContentType.hpp> +#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp> + +namespace basctl +{ + +using namespace comphelper; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::clipboard; +DlgEdTransferableImpl::DlgEdTransferableImpl( const Sequence< DataFlavor >& aSeqFlavors, const Sequence< Any >& aSeqData ) +{ + m_SeqFlavors = aSeqFlavors; + m_SeqData = aSeqData; +} +DlgEdTransferableImpl::~DlgEdTransferableImpl() +{ +} +bool DlgEdTransferableImpl::compareDataFlavors( const DataFlavor& lFlavor, const DataFlavor& rFlavor ) +{ + // compare mime content types + Reference< uno::XComponentContext > xContext = getProcessComponentContext(); + Reference< datatransfer::XMimeContentTypeFactory > + xMCntTypeFactory = MimeContentTypeFactory::create(xContext); + + // compare full media types + Reference< datatransfer::XMimeContentType > xLType = xMCntTypeFactory->createMimeContentType( lFlavor.MimeType ); + Reference< datatransfer::XMimeContentType > xRType = xMCntTypeFactory->createMimeContentType( rFlavor.MimeType ); + + OUString aLFullMediaType = xLType->getFullMediaType(); + OUString aRFullMediaType = xRType->getFullMediaType(); + + bool bRet = aLFullMediaType.equalsIgnoreAsciiCase( aRFullMediaType ); + + return bRet; +} + +// XTransferable +Any SAL_CALL DlgEdTransferableImpl::getTransferData( const DataFlavor& rFlavor ) +{ + const SolarMutexGuard aGuard; + + if ( !isDataFlavorSupported( rFlavor ) ) + throw UnsupportedFlavorException(); + + Any aData; + + for ( sal_Int32 i = 0; i < m_SeqFlavors.getLength(); i++ ) + { + if ( compareDataFlavors( m_SeqFlavors[i] , rFlavor ) ) + { + aData = m_SeqData[i]; + break; + } + } + + return aData; +} +Sequence< DataFlavor > SAL_CALL DlgEdTransferableImpl::getTransferDataFlavors( ) +{ + const SolarMutexGuard aGuard; + + return m_SeqFlavors; +} +sal_Bool SAL_CALL DlgEdTransferableImpl::isDataFlavorSupported( const DataFlavor& rFlavor ) +{ + const SolarMutexGuard aGuard; + + for ( auto const & i : std::as_const(m_SeqFlavors) ) + if ( compareDataFlavors( i, rFlavor ) ) + return true; + return false; +} + +// XClipboardOwner +void SAL_CALL DlgEdTransferableImpl::lostOwnership( const Reference< XClipboard >&, const Reference< XTransferable >& ) +{ + const SolarMutexGuard aGuard; + + m_SeqFlavors = Sequence< DataFlavor >(); + m_SeqData = Sequence< Any >(); +} +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedfac.cxx b/basctl/source/dlged/dlgedfac.cxx new file mode 100644 index 0000000000..65e2965491 --- /dev/null +++ b/basctl/source/dlged/dlgedfac.cxx @@ -0,0 +1,229 @@ +/* -*- 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 <dlgedfac.hxx> +#include <dlgedobj.hxx> +#include <dlgeddef.hxx> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/ScrollBarOrientation.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/processfactory.hxx> +#include <utility> + +namespace basctl +{ + +using namespace ::com::sun::star; + + +DlgEdFactory::DlgEdFactory( css::uno::Reference< css::frame::XModel > xModel ) : mxModel(std::move( xModel )) +{ + SdrObjFactory::InsertMakeObjectHdl( LINK(this, DlgEdFactory, MakeObject) ); +} + + +DlgEdFactory::~DlgEdFactory() COVERITY_NOEXCEPT_FALSE +{ + SdrObjFactory::RemoveMakeObjectHdl( LINK(this, DlgEdFactory, MakeObject) ); +} + + +IMPL_LINK( DlgEdFactory, MakeObject, SdrObjCreatorParams, aParams, rtl::Reference<SdrObject> ) +{ + static const uno::Reference<lang::XMultiServiceFactory> xDialogSFact = [] { + uno::Reference<lang::XMultiServiceFactory> xFact; + uno::Reference< uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< container::XNameContainer > xC( xContext->getServiceManager()->createInstanceWithContext( "com.sun.star.awt.UnoControlDialogModel", xContext ), uno::UNO_QUERY ); + if (xC.is()) + xFact.set(xC, uno::UNO_QUERY); + return xFact; + }(); + + rtl::Reference<SdrObject> pNewObj; + if( (aParams.nInventor == SdrInventor::BasicDialog) && + (aParams.nObjIdentifier >= SdrObjKind::BasicDialogPushButton) && + (aParams.nObjIdentifier <= SdrObjKind::BasicDialogFormHorizontalScroll) ) + { + switch( aParams.nObjIdentifier ) + { + case SdrObjKind::BasicDialogPushButton: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlButtonModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogRadioButton: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlRadioButtonModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFormRadio: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.RadioButton", xDialogSFact ); + static_cast< DlgEdObj* >( pNewObj.get() )->MakeDataAware( mxModel ); + break; + case SdrObjKind::BasicDialogCheckbox: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlCheckBoxModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFormCheck: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.CheckBox", xDialogSFact ); + static_cast< DlgEdObj* >( pNewObj.get() )->MakeDataAware( mxModel ); + break; + case SdrObjKind::BasicDialogListbox: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlListBoxModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFormList: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.ListBox", xDialogSFact ); + static_cast< DlgEdObj* >( pNewObj.get() )->MakeDataAware( mxModel ); + break; + case SdrObjKind::BasicDialogFormCombo: + case SdrObjKind::BasicDialogCombobox: + { + rtl::Reference<DlgEdObj> pNew; + if ( aParams.nObjIdentifier == SdrObjKind::BasicDialogCombobox ) + pNew = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlComboBoxModel", xDialogSFact ); + else + { + pNew = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.ComboBox", xDialogSFact ); + pNew->MakeDataAware( mxModel ); + } + pNewObj = pNew; + try + { + uno::Reference< beans::XPropertySet > xPSet(pNew->GetUnoControlModel(), uno::UNO_QUERY); + if (xPSet.is()) + { + xPSet->setPropertyValue( DLGED_PROP_DROPDOWN, uno::Any(true)); + } + } + catch(...) + { + } + } + break; + case SdrObjKind::BasicDialogGroupBox: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlGroupBoxModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogEdit: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlEditModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFixedText: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlFixedTextModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogImageControl: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlImageControlModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogProgressbar: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlProgressBarModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogHorizontalScrollbar: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlScrollBarModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFormHorizontalScroll: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.ScrollBar", xDialogSFact ); + static_cast< DlgEdObj* >( pNewObj.get() )->MakeDataAware( mxModel ); + break; + case SdrObjKind::BasicDialogFormVerticalScroll: + case SdrObjKind::BasicDialogVerticalScrollbar: + { + rtl::Reference<DlgEdObj> pNew; + if ( aParams.nObjIdentifier == SdrObjKind::BasicDialogVerticalScrollbar ) + pNew = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlScrollBarModel", xDialogSFact ); + else + { + pNew = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.ScrollBar", xDialogSFact ); + pNew->MakeDataAware( mxModel ); + } + pNewObj = pNew; + // set vertical orientation + try + { + uno::Reference< beans::XPropertySet > xPSet(pNew->GetUnoControlModel(), uno::UNO_QUERY); + if (xPSet.is()) + { + xPSet->setPropertyValue( DLGED_PROP_ORIENTATION, uno::Any(sal_Int32(css::awt::ScrollBarOrientation::VERTICAL)) ); + } + } + catch(...) + { + } + } break; + case SdrObjKind::BasicDialogHorizontalFixedLine: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlFixedLineModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogVerticalFixedLine: + { + rtl::Reference<DlgEdObj> pNew = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlFixedLineModel", xDialogSFact ); + pNewObj = pNew; + // set vertical orientation + try + { + uno::Reference< beans::XPropertySet > xPSet(pNew->GetUnoControlModel(), uno::UNO_QUERY); + if (xPSet.is()) + { + xPSet->setPropertyValue( DLGED_PROP_ORIENTATION, uno::Any(sal_Int32(1)) ); + } + } + catch(...) + { + } + } break; + case SdrObjKind::BasicDialogDateField: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlDateFieldModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogTimeField: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlTimeFieldModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogNumericField: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlNumericFieldModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogCurencyField: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlCurrencyFieldModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFormattedField: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlFormattedFieldModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogPatternField: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlPatternFieldModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFileControl: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlFileControlModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogSpinButton: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlSpinButtonModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogFormSpin: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.form.component.SpinButton", xDialogSFact ); + static_cast< DlgEdObj* >( pNewObj.get() )->MakeDataAware( mxModel ); + break; + case SdrObjKind::BasicDialogTreeControl: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.tree.TreeControlModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogGridControl: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.grid.UnoControlGridModel", xDialogSFact ); + break; + case SdrObjKind::BasicDialogHyperlinkControl: + pNewObj = new DlgEdObj(aParams.rSdrModel, "com.sun.star.awt.UnoControlFixedHyperlinkModel", xDialogSFact ); + break; + default: + break; + + } + } + return pNewObj; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedfunc.cxx b/basctl/source/dlged/dlgedfunc.cxx new file mode 100644 index 0000000000..7f1a0388ee --- /dev/null +++ b/basctl/source/dlged/dlgedfunc.cxx @@ -0,0 +1,548 @@ +/* -*- 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 <svtools/scrolladaptor.hxx> +#include <svx/svdview.hxx> +#include <dlgedfunc.hxx> +#include <dlged.hxx> +#include <dlgedview.hxx> +#include <vcl/seleng.hxx> + +namespace basctl +{ + +IMPL_LINK_NOARG( DlgEdFunc, ScrollTimeout, Timer *, void ) +{ + vcl::Window& rWindow = rParent.GetWindow(); + Point aPos = rWindow.ScreenToOutputPixel( rWindow.GetPointerPosPixel() ); + aPos = rWindow.PixelToLogic( aPos ); + ForceScroll( aPos ); +} + +void DlgEdFunc::ForceScroll( const Point& rPos ) +{ + aScrollTimer.Stop(); + + vcl::Window& rWindow = rParent.GetWindow(); + + static const Point aDefPoint; + tools::Rectangle aOutRect( aDefPoint, rWindow.GetOutputSizePixel() ); + aOutRect = rWindow.PixelToLogic( aOutRect ); + + ScrollAdaptor* pHScroll = rParent.GetHScroll(); + ScrollAdaptor* pVScroll = rParent.GetVScroll(); + tools::Long nDeltaX = pHScroll->GetLineSize(); + tools::Long nDeltaY = pVScroll->GetLineSize(); + + if( !aOutRect.Contains( rPos ) ) + { + if( rPos.X() < aOutRect.Left() ) + nDeltaX = -nDeltaX; + else if( rPos.X() <= aOutRect.Right() ) + nDeltaX = 0; + + if( rPos.Y() < aOutRect.Top() ) + nDeltaY = -nDeltaY; + else if( rPos.Y() <= aOutRect.Bottom() ) + nDeltaY = 0; + + if( nDeltaX ) + pHScroll->SetThumbPos( pHScroll->GetThumbPos() + nDeltaX ); + if( nDeltaY ) + pVScroll->SetThumbPos( pVScroll->GetThumbPos() + nDeltaY ); + + if( nDeltaX ) + rParent.DoScroll(); + if( nDeltaY ) + rParent.DoScroll(); + } + + aScrollTimer.Start(); +} + +DlgEdFunc::DlgEdFunc (DlgEditor& rParent_) : + rParent(rParent_), aScrollTimer("basctl DlgEdFunc aScrollTimer") +{ + aScrollTimer.SetInvokeHandler( LINK( this, DlgEdFunc, ScrollTimeout ) ); + aScrollTimer.SetTimeout( SELENG_AUTOREPEAT_INTERVAL ); +} + +DlgEdFunc::~DlgEdFunc() +{ +} + +void DlgEdFunc::MouseButtonDown( const MouseEvent& ) +{ +} + +bool DlgEdFunc::MouseButtonUp( const MouseEvent& ) +{ + aScrollTimer.Stop(); + return true; +} + +void DlgEdFunc::MouseMove( const MouseEvent& ) +{ +} + +bool DlgEdFunc::KeyInput( const KeyEvent& rKEvt ) +{ + bool bReturn = false; + + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + sal_uInt16 nCode = aCode.GetCode(); + + switch ( nCode ) + { + case KEY_ESCAPE: + { + if ( rView.IsAction() ) + { + rView.BrkAction(); + bReturn = true; + } + else if ( rView.AreObjectsMarked() ) + { + const SdrHdlList& rHdlList = rView.GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + if ( pHdl ) + const_cast<SdrHdlList&>(rHdlList).ResetFocusHdl(); + else + rView.UnmarkAll(); + + bReturn = true; + } + } + break; + case KEY_TAB: + { + if ( !aCode.IsMod1() && !aCode.IsMod2() ) + { + // mark next object + if ( !rView.MarkNextObj( !aCode.IsShift() ) ) + { + // if no next object, mark first/last + rView.UnmarkAllObj(); + rView.MarkNextObj( !aCode.IsShift() ); + } + + if ( rView.AreObjectsMarked() ) + rView.MakeVisible( rView.GetAllMarkedRect(), rWindow ); + + bReturn = true; + } + else if ( aCode.IsMod1() ) + { + // selected handle + const SdrHdlList& rHdlList = rView.GetHdlList(); + const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl( !aCode.IsShift() ); + + // guarantee visibility of focused handle + if (SdrHdl* pHdl = rHdlList.GetFocusHdl()) + { + Point aHdlPosition( pHdl->GetPos() ); + tools::Rectangle aVisRect( aHdlPosition - Point( 100, 100 ), Size( 200, 200 ) ); + rView.MakeVisible( aVisRect, rWindow ); + } + + bReturn = true; + } + } + break; + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + tools::Long nX = 0; + tools::Long nY = 0; + + if ( nCode == KEY_UP ) + { + // scroll up + nX = 0; + nY = -1; + } + else if ( nCode == KEY_DOWN ) + { + // scroll down + nX = 0; + nY = 1; + } + else if ( nCode == KEY_LEFT ) + { + // scroll left + nX = -1; + nY = 0; + } + else if ( nCode == KEY_RIGHT ) + { + // scroll right + nX = 1; + nY = 0; + } + + if ( rView.AreObjectsMarked() && !aCode.IsMod1() ) + { + if ( aCode.IsMod2() ) + { + // move in 1 pixel distance + Size aPixelSize = rWindow.PixelToLogic(Size(1, 1)); + nX *= aPixelSize.Width(); + nY *= aPixelSize.Height(); + } + else + { + // move in 1 mm distance + nX *= 100; + nY *= 100; + } + + const SdrHdlList& rHdlList = rView.GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if ( pHdl == nullptr ) + { + // no handle selected + if ( rView.IsMoveAllowed() ) + { + // restrict movement to work area + const tools::Rectangle& rWorkArea = rView.GetWorkArea(); + + if ( !rWorkArea.IsEmpty() ) + { + tools::Rectangle aMarkRect( rView.GetMarkedObjRect() ); + aMarkRect.Move( nX, nY ); + + if ( !rWorkArea.Contains( aMarkRect ) ) + { + if ( aMarkRect.Left() < rWorkArea.Left() ) + nX += rWorkArea.Left() - aMarkRect.Left(); + + if ( aMarkRect.Right() > rWorkArea.Right() ) + nX -= aMarkRect.Right() - rWorkArea.Right(); + + if ( aMarkRect.Top() < rWorkArea.Top() ) + nY += rWorkArea.Top() - aMarkRect.Top(); + + if ( aMarkRect.Bottom() > rWorkArea.Bottom() ) + nY -= aMarkRect.Bottom() - rWorkArea.Bottom(); + } + } + + if ( nX != 0 || nY != 0 ) + { + rView.MoveAllMarked( Size( nX, nY ) ); + rView.MakeVisible( rView.GetAllMarkedRect(), rWindow ); + } + } + } + else if (nX || nY) + { + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + const SdrDragStat& rDragStat = rView.GetDragStat(); + + // start dragging + rView.BegDragObj(aStartPoint, nullptr, pHdl, 0); + + if (rView.IsDragObj()) + { + bool const bWasNoSnap = rDragStat.IsNoSnap(); + bool const bWasSnapEnabled = rView.IsSnapEnabled(); + + // switch snapping off + if (!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(); + if (bWasSnapEnabled) + rView.SetSnapEnabled(false); + + rView.MovAction(aEndPoint); + rView.EndDragObj(); + + // restore snap + if (!bWasNoSnap) + const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap); + if (bWasSnapEnabled) + rView.SetSnapEnabled(bWasSnapEnabled); + } + + // make moved handle visible + tools::Rectangle aVisRect(aEndPoint - Point(100, 100), Size(200, 200)); + rView.MakeVisible(aVisRect, rWindow); + } + } + else + { + // scroll page + ScrollAdaptor* pScrollBar = ( nX != 0 ) ? rParent.GetHScroll() : rParent.GetVScroll(); + if ( pScrollBar ) + { + tools::Long nRangeMin = pScrollBar->GetRangeMin(); + tools::Long nRangeMax = pScrollBar->GetRangeMax(); + tools::Long nThumbPos = pScrollBar->GetThumbPos() + ( ( nX != 0 ) ? nX : nY ) * pScrollBar->GetLineSize(); + if ( nThumbPos < nRangeMin ) + nThumbPos = nRangeMin; + if ( nThumbPos > nRangeMax ) + nThumbPos = nRangeMax; + pScrollBar->SetThumbPos( nThumbPos ); + rParent.DoScroll(); + } + } + + bReturn = true; + } + break; + default: + { + } + break; + } + + if ( bReturn ) + rWindow.ReleaseMouse(); + + return bReturn; +} + +DlgEdFuncInsert::DlgEdFuncInsert (DlgEditor& rParent_) : + DlgEdFunc(rParent_) +{ + rParent.GetView().SetCreateMode(); +} + +DlgEdFuncInsert::~DlgEdFuncInsert() +{ + rParent.GetView().SetEditMode(); +} + +void DlgEdFuncInsert::MouseButtonDown( const MouseEvent& rMEvt ) +{ + if( !rMEvt.IsLeft() ) + return; + + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + rView.SetActualWin(rWindow.GetOutDev()); + + Point aPos = rWindow.PixelToLogic( rMEvt.GetPosPixel() ); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + sal_uInt16 nDrgLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + + rWindow.CaptureMouse(); + + if ( rMEvt.IsLeft() && rMEvt.GetClicks() == 1 ) + { + SdrHdl* pHdl = rView.PickHandle(aPos); + + // if selected object was hit, drag object + if ( pHdl!=nullptr || rView.IsMarkedHit(aPos, nHitLog) ) + rView.BegDragObj(aPos, nullptr, pHdl, nDrgLog); + else if ( rView.AreObjectsMarked() ) + rView.UnmarkAll(); + + // if no action, create object + if ( !rView.IsAction() ) + rView.BegCreateObj(aPos); + } + else if ( rMEvt.IsLeft() && rMEvt.GetClicks() == 2 ) + { + // if object was hit, show property browser + if ( rView.IsMarkedHit(aPos, nHitLog) && rParent.GetMode() != DlgEditor::READONLY ) + rParent.ShowProperties(); + } +} + +bool DlgEdFuncInsert::MouseButtonUp( const MouseEvent& rMEvt ) +{ + DlgEdFunc::MouseButtonUp( rMEvt ); + + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + rView.SetActualWin(rWindow.GetOutDev()); + + rWindow.ReleaseMouse(); + + // object creation active? + if ( rView.IsCreateObj() ) + { + rView.EndCreateObj(SdrCreateCmd::ForceEnd); + + if ( !rView.AreObjectsMarked() ) + { + sal_uInt16 nHitLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + Point aPos( rWindow.PixelToLogic( rMEvt.GetPosPixel() ) ); + rView.MarkObj(aPos, nHitLog); + } + + return rView.AreObjectsMarked(); + } + else + { + if ( rView.IsDragObj() ) + rView.EndDragObj( rMEvt.IsMod1() ); + return true; + } +} + +void DlgEdFuncInsert::MouseMove( const MouseEvent& rMEvt ) +{ + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + rView.SetActualWin(rWindow.GetOutDev()); + + Point aPos = rWindow.PixelToLogic(rMEvt.GetPosPixel()); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + + if (rView.IsAction()) + { + ForceScroll(aPos); + rView.MovAction(aPos); + } + + rWindow.SetPointer( rView.GetPreferredPointer( aPos, rWindow.GetOutDev(), nHitLog ) ); +} + +DlgEdFuncSelect::DlgEdFuncSelect (DlgEditor& rParent_) : + DlgEdFunc(rParent_) +{ +} + +DlgEdFuncSelect::~DlgEdFuncSelect() +{ +} + +void DlgEdFuncSelect::MouseButtonDown( const MouseEvent& rMEvt ) +{ + // get view from parent + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + rView.SetActualWin(rWindow.GetOutDev()); + + sal_uInt16 nDrgLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + Point aMDPos = rWindow.PixelToLogic(rMEvt.GetPosPixel()); + + if ( rMEvt.IsLeft() && rMEvt.GetClicks() == 1 ) + { + SdrHdl* pHdl = rView.PickHandle(aMDPos); + + // hit selected object? + if ( pHdl!=nullptr || rView.IsMarkedHit(aMDPos, nHitLog) ) + { + rView.BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + else + { + // if not multi selection, unmark all + if ( !rMEvt.IsShift() ) + rView.UnmarkAll(); + else + { + SdrPageView* pPV; + SdrObject* pObj = rView.PickObj(aMDPos, nHitLog, pPV); + if (pObj) + { + //if (dynamic_cast<DlgEdForm*>(pObj)) + // rView.UnmarkAll(); + //else + // rParent.UnmarkDialog(); + } + } + + if ( rView.MarkObj(aMDPos, nHitLog) ) + { + // drag object + pHdl = rView.PickHandle(aMDPos); + rView.BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + else + { + // select object + rView.BegMarkObj(aMDPos); + } + } + } + else if ( rMEvt.IsLeft() && rMEvt.GetClicks() == 2 ) + { + // if object was hit, show property browser + if ( rView.IsMarkedHit(aMDPos, nHitLog) && rParent.GetMode() != DlgEditor::READONLY ) + rParent.ShowProperties(); + } +} + +bool DlgEdFuncSelect::MouseButtonUp( const MouseEvent& rMEvt ) +{ + DlgEdFunc::MouseButtonUp( rMEvt ); + + // get view from parent + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + rView.SetActualWin(rWindow.GetOutDev()); + + Point aPnt = rWindow.PixelToLogic(rMEvt.GetPosPixel()); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + + if ( rMEvt.IsLeft() ) + { + if (rView.IsDragObj()) + { + // object was dragged + rView.EndDragObj( rMEvt.IsMod1() ); + rView.ForceMarkedToAnotherPage(); + } + else if (rView.IsAction()) + { + rView.EndAction(); + } + } + + rWindow.SetPointer( rView.GetPreferredPointer( aPnt, rWindow.GetOutDev(), nHitLog ) ); + rWindow.ReleaseMouse(); + + return true; +} + +void DlgEdFuncSelect::MouseMove( const MouseEvent& rMEvt ) +{ + SdrView& rView = rParent.GetView(); + vcl::Window& rWindow = rParent.GetWindow(); + rView.SetActualWin(rWindow.GetOutDev()); + + Point aPnt = rWindow.PixelToLogic(rMEvt.GetPosPixel()); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(rWindow.PixelToLogic(Size(3, 0)).Width()); + + if ( rView.IsAction() ) + { + Point aPix = rMEvt.GetPosPixel(); + Point aPnt_ = rWindow.PixelToLogic(aPix); + + ForceScroll(aPnt_); + rView.MovAction(aPnt_); + } + + rWindow.SetPointer( rView.GetPreferredPointer( aPnt, rWindow.GetOutDev(), nHitLog ) ); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedlist.cxx b/basctl/source/dlged/dlgedlist.cxx new file mode 100644 index 0000000000..6b9cebfe89 --- /dev/null +++ b/basctl/source/dlged/dlgedlist.cxx @@ -0,0 +1,80 @@ +/* -*- 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 <dlgedlist.hxx> +#include <dlgedobj.hxx> + +namespace basctl +{ + +// DlgEdPropListenerImpl +DlgEdPropListenerImpl::DlgEdPropListenerImpl (DlgEdObj& rObj) : + rDlgEdObj(rObj) +{ +} + +DlgEdPropListenerImpl::~DlgEdPropListenerImpl() +{ +} + +// XEventListener +void SAL_CALL DlgEdPropListenerImpl::disposing( const css::lang::EventObject& ) +{ +} + +// XPropertyChangeListener +void SAL_CALL DlgEdPropListenerImpl::propertyChange( const css::beans::PropertyChangeEvent& evt ) +{ + rDlgEdObj._propertyChange( evt ); +} + +// DlgEdEvtContListenerImpl +DlgEdEvtContListenerImpl::DlgEdEvtContListenerImpl (DlgEdObj& rObj) : + rDlgEdObj(rObj) +{ +} + +DlgEdEvtContListenerImpl::~DlgEdEvtContListenerImpl() +{ +} + +// XEventListener +void SAL_CALL DlgEdEvtContListenerImpl::disposing( const css::lang::EventObject& ) +{ +} + +// XContainerListener +void SAL_CALL DlgEdEvtContListenerImpl::elementInserted(const css::container::ContainerEvent& /*Event*/) +{ + rDlgEdObj._elementInserted(); +} + +void SAL_CALL DlgEdEvtContListenerImpl::elementReplaced(const css::container::ContainerEvent& /*Event*/) +{ + rDlgEdObj._elementReplaced(); +} + +void SAL_CALL DlgEdEvtContListenerImpl::elementRemoved(const css::container::ContainerEvent& /*Event*/) +{ + rDlgEdObj._elementRemoved(); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedmod.cxx b/basctl/source/dlged/dlgedmod.cxx new file mode 100644 index 0000000000..017e4b16c7 --- /dev/null +++ b/basctl/source/dlged/dlgedmod.cxx @@ -0,0 +1,36 @@ +/* -*- 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 <dlgedmod.hxx> +#include <dlgedpage.hxx> + +namespace basctl +{ +DlgEdModel::DlgEdModel() {} + +DlgEdModel::~DlgEdModel() {} + +rtl::Reference<SdrPage> DlgEdModel::AllocPage(bool bMasterPage) +{ + return new DlgEdPage(*this, bMasterPage); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedobj.cxx b/basctl/source/dlged/dlgedobj.cxx new file mode 100644 index 0000000000..5b87393e51 --- /dev/null +++ b/basctl/source/dlged/dlgedobj.cxx @@ -0,0 +1,1705 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <cassert> + +#include <dlged.hxx> +#include <dlgeddef.hxx> +#include <dlgedlist.hxx> +#include <dlgedobj.hxx> +#include <dlgedpage.hxx> +#include <dlgedview.hxx> +#include <localizationmgr.hxx> +#include <strings.hxx> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/awt/XUnoControlContainer.hpp> +#include <com/sun/star/awt/XVclContainerPeer.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/script/XScriptEventsSupplier.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <o3tl/functional.hxx> +#include <svx/svdpagv.hxx> +#include <unotools/sharedunocomponent.hxx> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::script; + + +DlgEditor& DlgEdObj::GetDialogEditor () +{ + if (DlgEdForm* pFormThis = dynamic_cast<DlgEdForm*>(this)) + return pFormThis->GetDlgEditor(); + else + return pDlgEdForm->GetDlgEditor(); +} + +DlgEdObj::DlgEdObj(SdrModel& rSdrModel) +: SdrUnoObj(rSdrModel, OUString()) + ,bIsListening(false) +{ +} + +DlgEdObj::DlgEdObj(SdrModel& rSdrModel, DlgEdObj const & rSource) +: SdrUnoObj(rSdrModel, rSource) + ,bIsListening(false) +{ + // set parent form + pDlgEdForm = rSource.pDlgEdForm; + + // add child to parent form + pDlgEdForm->AddChild( this ); + + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( xPSet.is() ) + { + // set new name + OUString aOUniqueName( GetUniqueName() ); + Any aUniqueName; + aUniqueName <<= aOUniqueName; + xPSet->setPropertyValue( DLGED_PROP_NAME, aUniqueName ); + + Reference< container::XNameContainer > xCont( GetDlgEdForm()->GetUnoControlModel() , UNO_QUERY ); + if ( xCont.is() ) + { + // set tabindex + Sequence< OUString > aNames = xCont->getElementNames(); + xPSet->setPropertyValue( DLGED_PROP_TABINDEX, Any(static_cast<sal_Int16>(aNames.getLength())) ); + + // insert control model in dialog model + Reference< awt::XControlModel > xCtrl( xPSet , UNO_QUERY ); + xCont->insertByName( aOUniqueName, Any(xCtrl) ); + + pDlgEdForm->UpdateTabOrderAndGroups(); + } + } + + // start listening + StartListening(); +} + +DlgEdObj::DlgEdObj( + SdrModel& rSdrModel, + const OUString& rModelName, + const css::uno::Reference< css::lang::XMultiServiceFactory >& rxSFac) +: SdrUnoObj(rSdrModel, rModelName, rxSFac) + ,bIsListening(false) +{ +} + +DlgEdObj::~DlgEdObj() +{ + if ( isListening() ) + EndListening(true); +} + +namespace +{ + /* returns the DlgEdForm which the given DlgEdObj belongs to + (which might in fact be the object itself) + + Failure to obtain the form will be reported with an assertion in the non-product + version. + */ + bool lcl_getDlgEdForm( DlgEdObj* _pObject, DlgEdForm*& _out_pDlgEdForm ) + { + _out_pDlgEdForm = dynamic_cast< DlgEdForm* >( _pObject ); + if ( !_out_pDlgEdForm ) + _out_pDlgEdForm = _pObject->GetDlgEdForm(); + DBG_ASSERT( _out_pDlgEdForm, "lcl_getDlgEdForm: no form!" ); + return ( _out_pDlgEdForm != nullptr ); + } +} + +uno::Reference< awt::XControl > DlgEdObj::GetControl() const +{ + uno::Reference< awt::XControl > xControl; + if (DlgEdForm const* pForm = GetDlgEdForm()) + { + DlgEditor const& rEditor = pForm->GetDlgEditor(); + xControl = GetUnoControl(rEditor.GetView(), *rEditor.GetWindow().GetOutDev()); + } + return xControl; +} + +bool DlgEdObj::TransformSdrToControlCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ) +{ + // input position and size + Size aPos( nXIn, nYIn ); + Size aSize( nWidthIn, nHeightIn ); + + // form position + DlgEdForm* pForm = nullptr; + if ( !lcl_getDlgEdForm( this, pForm ) ) + return false; + tools::Rectangle aFormRect = pForm->GetSnapRect(); + Size aFormPos( aFormRect.Left(), aFormRect.Top() ); + + // convert 100th_mm to pixel + OutputDevice* pDevice = Application::GetDefaultDevice(); + DBG_ASSERT( pDevice, "DlgEdObj::TransformSdrToControlCoordinates: missing default device!" ); + if ( !pDevice ) + return false; + aPos = pDevice->LogicToPixel( aPos, MapMode( MapUnit::Map100thMM ) ); + aSize = pDevice->LogicToPixel( aSize, MapMode( MapUnit::Map100thMM ) ); + aFormPos = pDevice->LogicToPixel( aFormPos, MapMode( MapUnit::Map100thMM ) ); + + // subtract form position + aPos.AdjustWidth( -(aFormPos.Width()) ); + aPos.AdjustHeight( -(aFormPos.Height()) ); + + // take window borders into account + Reference< beans::XPropertySet > xPSetForm( pForm->GetUnoControlModel(), UNO_QUERY ); + DBG_ASSERT( xPSetForm.is(), "DlgEdObj::TransformFormToSdrCoordinates: no form property set!" ); + if ( !xPSetForm.is() ) + return false; + bool bDecoration = true; + xPSetForm->getPropertyValue( DLGED_PROP_DECORATION ) >>= bDecoration; + if( bDecoration ) + { + awt::DeviceInfo aDeviceInfo = pForm->getDeviceInfo(); + aPos.AdjustWidth( -(aDeviceInfo.LeftInset) ); + aPos.AdjustHeight( -(aDeviceInfo.TopInset) ); + } + + // convert pixel to logic units + aPos = pDevice->PixelToLogic(aPos, MapMode(MapUnit::MapAppFont)); + aSize = pDevice->PixelToLogic(aSize, MapMode(MapUnit::MapAppFont)); + + // set out parameters + nXOut = aPos.Width(); + nYOut = aPos.Height(); + nWidthOut = aSize.Width(); + nHeightOut = aSize.Height(); + + return true; +} + +bool DlgEdObj::TransformSdrToFormCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ) +{ + // input position and size + Size aPos( nXIn, nYIn ); + Size aSize( nWidthIn, nHeightIn ); + + // convert 100th_mm to pixel + OutputDevice* pDevice = Application::GetDefaultDevice(); + DBG_ASSERT( pDevice, "DlgEdObj::TransformSdrToFormCoordinates: missing default device!" ); + if ( !pDevice ) + return false; + aPos = pDevice->LogicToPixel( aPos, MapMode( MapUnit::Map100thMM ) ); + aSize = pDevice->LogicToPixel( aSize, MapMode( MapUnit::Map100thMM ) ); + + // take window borders into account + DlgEdForm* pForm = nullptr; + if ( !lcl_getDlgEdForm( this, pForm ) ) + return false; + + // take window borders into account + Reference< beans::XPropertySet > xPSetForm( pForm->GetUnoControlModel(), UNO_QUERY ); + DBG_ASSERT( xPSetForm.is(), "DlgEdObj::TransformFormToSdrCoordinates: no form property set!" ); + if ( !xPSetForm.is() ) + return false; + bool bDecoration = true; + xPSetForm->getPropertyValue( DLGED_PROP_DECORATION ) >>= bDecoration; + if( bDecoration ) + { + awt::DeviceInfo aDeviceInfo = pForm->getDeviceInfo(); + aSize.AdjustWidth( -(aDeviceInfo.LeftInset + aDeviceInfo.RightInset) ); + aSize.AdjustHeight( -(aDeviceInfo.TopInset + aDeviceInfo.BottomInset) ); + } + // convert pixel to logic units + aPos = pDevice->PixelToLogic(aPos, MapMode(MapUnit::MapAppFont)); + aSize = pDevice->PixelToLogic(aSize, MapMode(MapUnit::MapAppFont)); + + // set out parameters + nXOut = aPos.Width(); + nYOut = aPos.Height(); + nWidthOut = aSize.Width(); + nHeightOut = aSize.Height(); + + return true; +} + +bool DlgEdObj::TransformControlToSdrCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ) +{ + // input position and size + Size aPos( nXIn, nYIn ); + Size aSize( nWidthIn, nHeightIn ); + + // form position + DlgEdForm* pForm = nullptr; + if ( !lcl_getDlgEdForm( this, pForm ) ) + return false; + + Reference< beans::XPropertySet > xPSetForm( pForm->GetUnoControlModel(), UNO_QUERY ); + DBG_ASSERT( xPSetForm.is(), "DlgEdObj::TransformControlToSdrCoordinates: no form property set!" ); + if ( !xPSetForm.is() ) + return false; + sal_Int32 nFormX = 0, nFormY = 0; + xPSetForm->getPropertyValue( DLGED_PROP_POSITIONX ) >>= nFormX; + xPSetForm->getPropertyValue( DLGED_PROP_POSITIONY ) >>= nFormY; + Size aFormPos( nFormX, nFormY ); + + // convert logic units to pixel + OutputDevice* pDevice = Application::GetDefaultDevice(); + DBG_ASSERT( pDevice, "DlgEdObj::TransformControlToSdrCoordinates: missing default device!" ); + if ( !pDevice ) + return false; + aPos = pDevice->LogicToPixel(aPos, MapMode(MapUnit::MapAppFont)); + aSize = pDevice->LogicToPixel(aSize, MapMode(MapUnit::MapAppFont)); + aFormPos = pDevice->LogicToPixel(aFormPos, MapMode(MapUnit::MapAppFont)); + + // add form position + aPos.AdjustWidth(aFormPos.Width() ); + aPos.AdjustHeight(aFormPos.Height() ); + + // take window borders into account + bool bDecoration = true; + xPSetForm->getPropertyValue( DLGED_PROP_DECORATION ) >>= bDecoration; + if( bDecoration ) + { + awt::DeviceInfo aDeviceInfo = pForm->getDeviceInfo(); + aPos.AdjustWidth(aDeviceInfo.LeftInset ); + aPos.AdjustHeight(aDeviceInfo.TopInset ); + } + + // convert pixel to 100th_mm + aPos = pDevice->PixelToLogic( aPos, MapMode( MapUnit::Map100thMM ) ); + aSize = pDevice->PixelToLogic( aSize, MapMode( MapUnit::Map100thMM ) ); + + // set out parameters + nXOut = aPos.Width(); + nYOut = aPos.Height(); + nWidthOut = aSize.Width(); + nHeightOut = aSize.Height(); + + return true; +} + +bool DlgEdObj::TransformFormToSdrCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ) +{ + // input position and size + Size aPos( nXIn, nYIn ); + Size aSize( nWidthIn, nHeightIn ); + + // convert logic units to pixel + OutputDevice* pDevice = Application::GetDefaultDevice(); + DBG_ASSERT( pDevice, "DlgEdObj::TransformFormToSdrCoordinates: missing default device!" ); + if ( !pDevice ) + return false; + + // take window borders into account + DlgEdForm* pForm = nullptr; + if ( !lcl_getDlgEdForm( this, pForm ) ) + return false; + + aPos = pDevice->LogicToPixel(aPos, MapMode(MapUnit::MapAppFont)); + aSize = pDevice->LogicToPixel(aSize, MapMode(MapUnit::MapAppFont)); + + // take window borders into account + Reference< beans::XPropertySet > xPSetForm( pForm->GetUnoControlModel(), UNO_QUERY ); + DBG_ASSERT( xPSetForm.is(), "DlgEdObj::TransformFormToSdrCoordinates: no form property set!" ); + if ( !xPSetForm.is() ) + return false; + bool bDecoration = true; + xPSetForm->getPropertyValue( DLGED_PROP_DECORATION ) >>= bDecoration; + if( bDecoration ) + { + awt::DeviceInfo aDeviceInfo = pForm->getDeviceInfo(); + aSize.AdjustWidth(aDeviceInfo.LeftInset + aDeviceInfo.RightInset ); + aSize.AdjustHeight(aDeviceInfo.TopInset + aDeviceInfo.BottomInset ); + } + + // convert pixel to 100th_mm + aPos = pDevice->PixelToLogic( aPos, MapMode( MapUnit::Map100thMM ) ); + aSize = pDevice->PixelToLogic( aSize, MapMode( MapUnit::Map100thMM ) ); + + // set out parameters + nXOut = aPos.Width(); + nYOut = aPos.Height(); + nWidthOut = aSize.Width(); + nHeightOut = aSize.Height(); + + return true; +} + +void DlgEdObj::SetRectFromProps() +{ + // get control position and size from properties + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( !xPSet.is() ) + return; + + sal_Int32 nXIn = 0, nYIn = 0, nWidthIn = 0, nHeightIn = 0; + xPSet->getPropertyValue( DLGED_PROP_POSITIONX ) >>= nXIn; + xPSet->getPropertyValue( DLGED_PROP_POSITIONY ) >>= nYIn; + xPSet->getPropertyValue( DLGED_PROP_WIDTH ) >>= nWidthIn; + xPSet->getPropertyValue( DLGED_PROP_HEIGHT ) >>= nHeightIn; + + // transform coordinates + sal_Int32 nXOut, nYOut, nWidthOut, nHeightOut; + if ( TransformControlToSdrCoordinates( nXIn, nYIn, nWidthIn, nHeightIn, nXOut, nYOut, nWidthOut, nHeightOut ) ) + { + // set rectangle position and size + Point aPoint( nXOut, nYOut ); + Size aSize( nWidthOut, nHeightOut ); + SetSnapRect( tools::Rectangle( aPoint, aSize ) ); + } +} + +void DlgEdObj::SetPropsFromRect() +{ + // get control position and size from rectangle + tools::Rectangle aRect_ = GetSnapRect(); + sal_Int32 nXIn = aRect_.Left(); + sal_Int32 nYIn = aRect_.Top(); + sal_Int32 nWidthIn = aRect_.GetWidth(); + sal_Int32 nHeightIn = aRect_.GetHeight(); + + // transform coordinates + sal_Int32 nXOut, nYOut, nWidthOut, nHeightOut; + if ( TransformSdrToControlCoordinates( nXIn, nYIn, nWidthIn, nHeightIn, nXOut, nYOut, nWidthOut, nHeightOut ) ) + { + // set properties + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( xPSet.is() ) + { + xPSet->setPropertyValue( DLGED_PROP_POSITIONX, Any(nXOut) ); + xPSet->setPropertyValue( DLGED_PROP_POSITIONY, Any(nYOut) ); + xPSet->setPropertyValue( DLGED_PROP_WIDTH, Any(nWidthOut) ); + xPSet->setPropertyValue( DLGED_PROP_HEIGHT, Any(nHeightOut) ); + } + } +} + +void DlgEdObj::PositionAndSizeChange( const beans::PropertyChangeEvent& evt ) +{ + DBG_ASSERT( pDlgEdForm, "DlgEdObj::PositionAndSizeChange: no form!" ); + DlgEdPage& rPage = pDlgEdForm->GetDlgEditor().GetPage(); + { + Size aPageSize = rPage.GetSize(); + sal_Int32 nPageWidthIn = aPageSize.Width(); + sal_Int32 nPageHeightIn = aPageSize.Height(); + sal_Int32 nPageX, nPageY, nPageWidth, nPageHeight; + if ( TransformSdrToControlCoordinates( 0/*nPageXIn*/, 0/*nPageYIn*/, nPageWidthIn, nPageHeightIn, nPageX, nPageY, nPageWidth, nPageHeight ) ) + { + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( xPSet.is() ) + { + sal_Int32 nX = 0, nY = 0, nWidth = 0, nHeight = 0; + xPSet->getPropertyValue( DLGED_PROP_POSITIONX ) >>= nX; + xPSet->getPropertyValue( DLGED_PROP_POSITIONY ) >>= nY; + xPSet->getPropertyValue( DLGED_PROP_WIDTH ) >>= nWidth; + xPSet->getPropertyValue( DLGED_PROP_HEIGHT ) >>= nHeight; + + sal_Int32 nValue = 0; + evt.NewValue >>= nValue; + sal_Int32 nNewValue = nValue; + + if ( evt.PropertyName == DLGED_PROP_POSITIONX ) + { + if ( nNewValue + nWidth > nPageX + nPageWidth ) + nNewValue = nPageX + nPageWidth - nWidth; + if ( nNewValue < nPageX ) + nNewValue = nPageX; + } + else if ( evt.PropertyName == DLGED_PROP_POSITIONY ) + { + if ( nNewValue + nHeight > nPageY + nPageHeight ) + nNewValue = nPageY + nPageHeight - nHeight; + if ( nNewValue < nPageY ) + nNewValue = nPageY; + } + else if ( evt.PropertyName == DLGED_PROP_WIDTH ) + { + if ( nX + nNewValue > nPageX + nPageWidth ) + nNewValue = nPageX + nPageWidth - nX; + if ( nNewValue < 1 ) + nNewValue = 1; + } + else if ( evt.PropertyName == DLGED_PROP_HEIGHT ) + { + if ( nY + nNewValue > nPageY + nPageHeight ) + nNewValue = nPageY + nPageHeight - nY; + if ( nNewValue < 1 ) + nNewValue = 1; + } + + if ( nNewValue != nValue ) + { + EndListening( false ); + xPSet->setPropertyValue( evt.PropertyName, Any(nNewValue) ); + StartListening(); + } + } + } + } + + SetRectFromProps(); +} + +void DlgEdObj::NameChange( const css::beans::PropertyChangeEvent& evt ) +{ + // get old name + OUString aOldName; + evt.OldValue >>= aOldName; + + // get new name + OUString aNewName; + evt.NewValue >>= aNewName; + + if ( aNewName == aOldName ) + return; + + Reference< container::XNameAccess > xNameAcc((GetDlgEdForm()->GetUnoControlModel()), UNO_QUERY); + if ( !(xNameAcc.is() && xNameAcc->hasByName(aOldName)) ) + return; + + if (!xNameAcc->hasByName(aNewName) && !aNewName.isEmpty()) + { + // remove the control by the old name and insert the control by the new name in the container + Reference< container::XNameContainer > xCont(xNameAcc, UNO_QUERY ); + if ( xCont.is() ) + { + Reference< awt::XControlModel > xCtrl = GetUnoControlModel(); + Any aAny; + aAny <<= xCtrl; + xCont->removeByName( aOldName ); + xCont->insertByName( aNewName , aAny ); + + LocalizationMgr::renameControlResourceIDsForEditorObject( + &GetDialogEditor(), aAny, aNewName + ); + } + } + else + { + // set old name property + EndListening(false); + Reference< beans::XPropertySet > xPSet(GetUnoControlModel(), UNO_QUERY); + xPSet->setPropertyValue( DLGED_PROP_NAME, Any(aOldName) ); + StartListening(); + } +} + +sal_Int32 DlgEdObj::GetStep() const +{ + // get step property + sal_Int32 nStep = 0; + uno::Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), uno::UNO_QUERY ); + if (xPSet.is()) + { + xPSet->getPropertyValue( DLGED_PROP_STEP ) >>= nStep; + } + return nStep; +} + +void DlgEdObj::UpdateStep() +{ + sal_Int32 nCurStep = GetDlgEdForm()->GetStep(); + sal_Int32 nStep = GetStep(); + + SdrLayerAdmin& rLayerAdmin(getSdrModelFromSdrObject().GetLayerAdmin()); + SdrLayerID nHiddenLayerId = rLayerAdmin.GetLayerID( "HiddenLayer" ); + SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID( rLayerAdmin.GetControlLayerName() ); + + if( nCurStep ) + { + if ( nStep && (nStep != nCurStep) ) + { + SetLayer( nHiddenLayerId ); + } + else + { + SetLayer( nControlLayerId ); + } + } + else + { + SetLayer( nControlLayerId ); + } +} + +void DlgEdObj::TabIndexChange( const beans::PropertyChangeEvent& evt ) +{ + DlgEdForm* pForm = GetDlgEdForm(); + if ( !pForm ) + return; + + // stop listening with all children + std::vector<DlgEdObj*> aChildList = pForm->GetChildren(); + for (auto const& child : aChildList) + { + child->EndListening( false ); + } + + Reference< container::XNameAccess > xNameAcc( pForm->GetUnoControlModel() , UNO_QUERY ); + if ( xNameAcc.is() ) + { + // get sequence of control names + Sequence< OUString > aNames = xNameAcc->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nCtrls = aNames.getLength(); + + // create a map of tab indices and control names, sorted by tab index + IndexToNameMap aIndexToNameMap; + for ( sal_Int32 i = 0; i < nCtrls; ++i ) + { + // get control name + OUString aName( pNames[i] ); + + // get tab index + sal_Int16 nTabIndex = -1; + Any aCtrl = xNameAcc->getByName( aName ); + Reference< beans::XPropertySet > xPSet; + aCtrl >>= xPSet; + if ( xPSet.is() && xPSet == Reference< beans::XPropertySet >( evt.Source, UNO_QUERY ) ) + evt.OldValue >>= nTabIndex; + else if ( xPSet.is() ) + xPSet->getPropertyValue( DLGED_PROP_TABINDEX ) >>= nTabIndex; + + // insert into map + aIndexToNameMap.emplace( nTabIndex, aName ); + } + + // create a helper list of control names, sorted by tab index + std::vector< OUString > aNameList( aIndexToNameMap.size() ); + std::transform( + aIndexToNameMap.begin(), aIndexToNameMap.end(), + aNameList.begin(), + ::o3tl::select2nd< IndexToNameMap::value_type >( ) + ); + + // check tab index + sal_Int16 nOldTabIndex = 0; + evt.OldValue >>= nOldTabIndex; + sal_Int16 nNewTabIndex = 0; + evt.NewValue >>= nNewTabIndex; + if ( nNewTabIndex < 0 ) + nNewTabIndex = 0; + else if ( nNewTabIndex > nCtrls - 1 ) + nNewTabIndex = sal::static_int_cast<sal_Int16>( nCtrls - 1 ); + + // reorder helper list + OUString aCtrlName = aNameList[nOldTabIndex]; + aNameList.erase( aNameList.begin() + nOldTabIndex ); + aNameList.insert( aNameList.begin() + nNewTabIndex , aCtrlName ); + + // set new tab indices + for ( sal_Int32 i = 0; i < nCtrls; ++i ) + { + Any aCtrl = xNameAcc->getByName( aNameList[i] ); + Reference< beans::XPropertySet > xPSet; + aCtrl >>= xPSet; + if ( xPSet.is() ) + { + assert(i >= SAL_MIN_INT16); + if (i > SAL_MAX_INT16) + { + SAL_WARN("basctl", "tab " << i << " > SAL_MAX_INT16"); + continue; + } + xPSet->setPropertyValue( DLGED_PROP_TABINDEX, Any(static_cast<sal_Int16>(i)) ); + } + } + + // reorder objects in drawing page + getSdrModelFromSdrObject().GetPage(0)->SetObjectOrdNum( nOldTabIndex + 1, nNewTabIndex + 1 ); + + pForm->UpdateTabOrderAndGroups(); + } + + // start listening with all children + for (auto const& child : aChildList) + { + child->StartListening(); + } +} + +bool DlgEdObj::supportsService( OUString const & serviceName ) const +{ + bool bSupports = false; + + Reference< lang::XServiceInfo > xServiceInfo( GetUnoControlModel() , UNO_QUERY ); + // TODO: cache xServiceInfo as member? + if ( xServiceInfo.is() ) + bSupports = xServiceInfo->supportsService( serviceName ); + + return bSupports; +} + +OUString DlgEdObj::GetDefaultName() const +{ + OUString sResId; + OUString aDefaultName; + if ( supportsService( "com.sun.star.awt.UnoControlDialogModel" ) ) + { + sResId = RID_STR_CLASS_DIALOG; + } + else if ( supportsService( "com.sun.star.awt.UnoControlButtonModel" ) ) + { + sResId = RID_STR_CLASS_BUTTON; + } + else if ( supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" ) ) + { + sResId = RID_STR_CLASS_RADIOBUTTON; + } + else if ( supportsService( "com.sun.star.awt.UnoControlCheckBoxModel" ) ) + { + sResId = RID_STR_CLASS_CHECKBOX; + } + else if ( supportsService( "com.sun.star.awt.UnoControlListBoxModel" ) ) + { + sResId = RID_STR_CLASS_LISTBOX; + } + else if ( supportsService( "com.sun.star.awt.UnoControlComboBoxModel" ) ) + { + sResId = RID_STR_CLASS_COMBOBOX; + } + else if ( supportsService( "com.sun.star.awt.UnoControlGroupBoxModel" ) ) + { + sResId = RID_STR_CLASS_GROUPBOX; + } + else if ( supportsService( "com.sun.star.awt.UnoControlEditModel" ) ) + { + sResId = RID_STR_CLASS_EDIT; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFixedTextModel" ) ) + { + sResId = RID_STR_CLASS_FIXEDTEXT; + } + else if ( supportsService( "com.sun.star.awt.UnoControlImageControlModel" ) ) + { + sResId = RID_STR_CLASS_IMAGECONTROL; + } + else if ( supportsService( "com.sun.star.awt.UnoControlProgressBarModel" ) ) + { + sResId = RID_STR_CLASS_PROGRESSBAR; + } + else if ( supportsService( "com.sun.star.awt.UnoControlScrollBarModel" ) ) + { + sResId = RID_STR_CLASS_SCROLLBAR; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFixedLineModel" ) ) + { + sResId = RID_STR_CLASS_FIXEDLINE; + } + else if ( supportsService( "com.sun.star.awt.UnoControlDateFieldModel" ) ) + { + sResId = RID_STR_CLASS_DATEFIELD; + } + else if ( supportsService( "com.sun.star.awt.UnoControlTimeFieldModel" ) ) + { + sResId = RID_STR_CLASS_TIMEFIELD; + } + else if ( supportsService( "com.sun.star.awt.UnoControlNumericFieldModel" ) ) + { + sResId = RID_STR_CLASS_NUMERICFIELD; + } + else if ( supportsService( "com.sun.star.awt.UnoControlCurrencyFieldModel" ) ) + { + sResId = RID_STR_CLASS_CURRENCYFIELD; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFormattedFieldModel" ) ) + { + sResId = RID_STR_CLASS_FORMATTEDFIELD; + } + else if ( supportsService( "com.sun.star.awt.UnoControlPatternFieldModel" ) ) + { + sResId = RID_STR_CLASS_PATTERNFIELD; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFileControlModel" ) ) + { + sResId = RID_STR_CLASS_FILECONTROL; + } + else if ( supportsService( "com.sun.star.awt.tree.TreeControlModel" ) ) + { + sResId = RID_STR_CLASS_TREECONTROL; + } + else if ( supportsService( "com.sun.star.awt.grid.UnoControlGridModel" ) ) + { + sResId = RID_STR_CLASS_GRIDCONTROL; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFixedHyperlinkModel" ) ) + { + sResId = RID_STR_CLASS_HYPERLINKCONTROL; + } + else if ( supportsService( "com.sun.star.awt.UnoControlSpinButtonModel" ) ) + { + sResId = RID_STR_CLASS_SPINCONTROL; + } + else + { + sResId = RID_STR_CLASS_CONTROL; + } + + if (!sResId.isEmpty()) + aDefaultName = sResId; + + return aDefaultName; +} + +OUString DlgEdObj::GetUniqueName() const +{ + OUString aUniqueName; + uno::Reference< container::XNameAccess > xNameAcc((GetDlgEdForm()->GetUnoControlModel()), uno::UNO_QUERY); + + if ( xNameAcc.is() ) + { + sal_Int32 n = 0; + OUString aDefaultName = GetDefaultName(); + + do + { + aUniqueName = aDefaultName + OUString::number(++n); + } while (xNameAcc->hasByName(aUniqueName)); + } + + return aUniqueName; +} + +SdrInventor DlgEdObj::GetObjInventor() const +{ + return SdrInventor::BasicDialog; +} + +SdrObjKind DlgEdObj::GetObjIdentifier() const +{ + if ( supportsService( "com.sun.star.awt.UnoControlDialogModel" )) + { + return SdrObjKind::BasicDialogDialog; + } + else if ( supportsService( "com.sun.star.awt.UnoControlButtonModel" )) + { + return SdrObjKind::BasicDialogPushButton; + } + else if ( supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" )) + { + return SdrObjKind::BasicDialogRadioButton; + } + else if ( supportsService( "com.sun.star.awt.UnoControlCheckBoxModel" )) + { + return SdrObjKind::BasicDialogCheckbox; + } + else if ( supportsService( "com.sun.star.awt.UnoControlListBoxModel" )) + { + return SdrObjKind::BasicDialogListbox; + } + else if ( supportsService( "com.sun.star.awt.UnoControlComboBoxModel" )) + { + return SdrObjKind::BasicDialogCombobox; + } + else if ( supportsService( "com.sun.star.awt.UnoControlGroupBoxModel" )) + { + return SdrObjKind::BasicDialogGroupBox; + } + else if ( supportsService( "com.sun.star.awt.UnoControlEditModel" )) + { + return SdrObjKind::BasicDialogEdit; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFixedTextModel" )) + { + return SdrObjKind::BasicDialogFixedText; + } + else if ( supportsService( "com.sun.star.awt.UnoControlImageControlModel" )) + { + return SdrObjKind::BasicDialogImageControl; + } + else if ( supportsService( "com.sun.star.awt.UnoControlProgressBarModel" )) + { + return SdrObjKind::BasicDialogProgressbar; + } + else if ( supportsService( "com.sun.star.awt.UnoControlScrollBarModel" )) + { + return SdrObjKind::BasicDialogHorizontalScrollbar; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFixedLineModel" )) + { + return SdrObjKind::BasicDialogHorizontalFixedLine; + } + else if ( supportsService( "com.sun.star.awt.UnoControlDateFieldModel" )) + { + return SdrObjKind::BasicDialogDateField; + } + else if ( supportsService( "com.sun.star.awt.UnoControlTimeFieldModel" )) + { + return SdrObjKind::BasicDialogTimeField; + } + else if ( supportsService( "com.sun.star.awt.UnoControlNumericFieldModel" )) + { + return SdrObjKind::BasicDialogNumericField; + } + else if ( supportsService( "com.sun.star.awt.UnoControlCurrencyFieldModel" )) + { + return SdrObjKind::BasicDialogCurencyField; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFormattedFieldModel" )) + { + return SdrObjKind::BasicDialogFormattedField; + } + else if ( supportsService( "com.sun.star.awt.UnoControlPatternFieldModel" )) + { + return SdrObjKind::BasicDialogPatternField; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFileControlModel" )) + { + return SdrObjKind::BasicDialogFileControl; + } + else if ( supportsService( "com.sun.star.awt.tree.TreeControlModel" )) + { + return SdrObjKind::BasicDialogTreeControl; + } + else if ( supportsService( "com.sun.star.awt.grid.UnoControlGridModel" )) + { + return SdrObjKind::BasicDialogGridControl; + } + else if ( supportsService( "com.sun.star.awt.UnoControlFixedHyperlinkModel" )) + { + return SdrObjKind::BasicDialogHyperlinkControl; + } + else + { + return SdrObjKind::BasicDialogControl; + } +} + +rtl::Reference<SdrObject> DlgEdObj::CloneSdrObject(SdrModel& rTargetModel) const +{ + return new DlgEdObj(rTargetModel, *this); +} + +rtl::Reference<SdrObject> DlgEdObj::getFullDragClone() const +{ + // no need to really add the clone for dragging, it's a temporary + // object + return rtl::Reference<SdrObject>(new SdrUnoObj(getSdrModelFromSdrObject(), *this)); +} + +void DlgEdObj::NbcMove( const Size& rSize ) +{ + SdrUnoObj::NbcMove( rSize ); + + // stop listening + EndListening(false); + + // set geometry properties + SetPropsFromRect(); + + // start listening + StartListening(); + + // dialog model changed + GetDlgEdForm()->GetDlgEditor().SetDialogModelChanged(); +} + +void DlgEdObj::NbcResize(const Point& rRef, const Fraction& xFract, const Fraction& yFract) +{ + SdrUnoObj::NbcResize( rRef, xFract, yFract ); + + // stop listening + EndListening(false); + + // set geometry properties + SetPropsFromRect(); + + // start listening + StartListening(); + + // dialog model changed + GetDlgEdForm()->GetDlgEditor().SetDialogModelChanged(); +} + +bool DlgEdObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) +{ + bool bResult = SdrUnoObj::EndCreate(rStat, eCmd); + + // tdf#120674 after interactive creation, the SdrObject (this) has no SdrPage yet + // due to not being inserted. Usually this should be handled in a ::handlePageChange + // implementation. For historical reasons, the SdrPage (which is the DlgEdPage) was + // already set. For now, get it from the SdrDragStat and use it to access and set + // the local pDlgEdForm + if(!pDlgEdForm && nullptr != rStat.GetPageView()) + { + const DlgEdPage* pDlgEdPage(dynamic_cast<const DlgEdPage*>(rStat.GetPageView()->GetPage())); + + if(nullptr != pDlgEdPage) + { + // set parent form + pDlgEdForm = pDlgEdPage->GetDlgEdForm(); + } + } + + SetDefaults(); + StartListening(); + + return bResult; +} + +void DlgEdObj::SetDefaults() +{ + if ( !pDlgEdForm ) + return; + + // add child to parent form + pDlgEdForm->AddChild( this ); + + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( xPSet.is() ) + { + // get unique name + OUString aOUniqueName( GetUniqueName() ); + + // set name property + xPSet->setPropertyValue( DLGED_PROP_NAME, Any(aOUniqueName) ); + + // set labels + if ( supportsService( "com.sun.star.awt.UnoControlButtonModel" ) || + supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" ) || + supportsService( "com.sun.star.awt.UnoControlCheckBoxModel" ) || + supportsService( "com.sun.star.awt.UnoControlGroupBoxModel" ) || + supportsService( "com.sun.star.awt.UnoControlFixedTextModel" ) ) + { + xPSet->setPropertyValue( DLGED_PROP_LABEL, Any(aOUniqueName) ); + } + + // set number formats supplier for formatted field + if ( supportsService( "com.sun.star.awt.UnoControlFormattedFieldModel" ) ) + { + Reference< util::XNumberFormatsSupplier > xSupplier = GetDlgEdForm()->GetDlgEditor().GetNumberFormatsSupplier(); + if ( xSupplier.is() ) + { + xPSet->setPropertyValue( DLGED_PROP_FORMATSSUPPLIER, Any(xSupplier) ); + } + } + + // set geometry properties + SetPropsFromRect(); + + Reference< container::XNameContainer > xCont( GetDlgEdForm()->GetUnoControlModel() , UNO_QUERY ); + if ( xCont.is() ) + { + // set tabindex + Sequence< OUString > aNames = xCont->getElementNames(); + uno::Any aTabIndex; + aTabIndex <<= static_cast<sal_Int16>(aNames.getLength()); + xPSet->setPropertyValue( DLGED_PROP_TABINDEX, aTabIndex ); + + // set step + Reference< beans::XPropertySet > xPSetForm( xCont, UNO_QUERY ); + if ( xPSetForm.is() ) + { + Any aStep = xPSetForm->getPropertyValue( DLGED_PROP_STEP ); + xPSet->setPropertyValue( DLGED_PROP_STEP, aStep ); + } + + // insert control model in dialog model + Reference< awt::XControlModel > xCtrl( xPSet , UNO_QUERY ); + Any aAny; + aAny <<= xCtrl; + xCont->insertByName( aOUniqueName , aAny ); + + LocalizationMgr::setControlResourceIDsForNewEditorObject( + &GetDialogEditor(), aAny, aOUniqueName + ); + + pDlgEdForm->UpdateTabOrderAndGroups(); + } + } + + // dialog model changed + pDlgEdForm->GetDlgEditor().SetDialogModelChanged(); +} + +void DlgEdObj::StartListening() +{ + DBG_ASSERT(!isListening(), "DlgEdObj::StartListening: already listening!"); + + if (isListening()) + return; + + bIsListening = true; + + // XPropertyChangeListener + Reference< XPropertySet > xControlModel( GetUnoControlModel() , UNO_QUERY ); + if (!m_xPropertyChangeListener.is() && xControlModel.is()) + { + // create listener + m_xPropertyChangeListener = new DlgEdPropListenerImpl(*this); + + // register listener to properties + xControlModel->addPropertyChangeListener( OUString() , m_xPropertyChangeListener ); + } + + // XContainerListener + Reference< XScriptEventsSupplier > xEventsSupplier( GetUnoControlModel() , UNO_QUERY ); + if( !m_xContainerListener.is() && xEventsSupplier.is() ) + { + // create listener + m_xContainerListener = new DlgEdEvtContListenerImpl(*this); + + // register listener to script event container + Reference< XNameContainer > xEventCont = xEventsSupplier->getEvents(); + DBG_ASSERT(xEventCont.is(), "DlgEdObj::StartListening: control model has no script event container!"); + Reference< XContainer > xCont( xEventCont , UNO_QUERY ); + if (xCont.is()) + xCont->addContainerListener( m_xContainerListener ); + } +} + +void DlgEdObj::EndListening(bool bRemoveListener) +{ + DBG_ASSERT(isListening(), "DlgEdObj::EndListening: not listening currently!"); + + if (!isListening()) + return; + + bIsListening = false; + + if (!bRemoveListener) + return; + + // XPropertyChangeListener + Reference< XPropertySet > xControlModel(GetUnoControlModel(), UNO_QUERY); + if ( m_xPropertyChangeListener.is() && xControlModel.is() ) + { + // remove listener + xControlModel->removePropertyChangeListener( OUString() , m_xPropertyChangeListener ); + } + m_xPropertyChangeListener.clear(); + + // XContainerListener + Reference< XScriptEventsSupplier > xEventsSupplier( GetUnoControlModel() , UNO_QUERY ); + if( m_xContainerListener.is() && xEventsSupplier.is() ) + { + // remove listener + Reference< XNameContainer > xEventCont = xEventsSupplier->getEvents(); + DBG_ASSERT(xEventCont.is(), "DlgEdObj::EndListening: control model has no script event container!"); + Reference< XContainer > xCont( xEventCont , UNO_QUERY ); + if (xCont.is()) + xCont->removeContainerListener( m_xContainerListener ); + } + m_xContainerListener.clear(); +} + +void DlgEdObj::_propertyChange( const css::beans::PropertyChangeEvent& evt ) +{ + if (!isListening()) + return; + + DlgEdForm* pRealDlgEdForm = dynamic_cast<DlgEdForm*>(this); + if (!pRealDlgEdForm) + pRealDlgEdForm = GetDlgEdForm(); + if (!pRealDlgEdForm) + return; + DlgEditor& rDlgEditor = pRealDlgEdForm->GetDlgEditor(); + if (rDlgEditor.isInPaint()) + return; + + // dialog model changed + rDlgEditor.SetDialogModelChanged(); + + // update position and size + if ( evt.PropertyName == DLGED_PROP_POSITIONX || evt.PropertyName == DLGED_PROP_POSITIONY || + evt.PropertyName == DLGED_PROP_WIDTH || evt.PropertyName == DLGED_PROP_HEIGHT || + evt.PropertyName == DLGED_PROP_DECORATION ) + { + PositionAndSizeChange( evt ); + + if ( evt.PropertyName == DLGED_PROP_DECORATION ) + GetDialogEditor().ResetDialog(); + } + // change name of control in dialog model + else if ( evt.PropertyName == DLGED_PROP_NAME ) + { + if (!dynamic_cast<DlgEdForm*>(this)) + { + try + { + NameChange(evt); + } + catch (container::NoSuchElementException const&) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw lang::WrappedTargetRuntimeException("", nullptr, + anyEx); + } + } + } + // update step + else if ( evt.PropertyName == DLGED_PROP_STEP ) + { + UpdateStep(); + } + // change tabindex + else if ( evt.PropertyName == DLGED_PROP_TABINDEX ) + { + if (!dynamic_cast<DlgEdForm*>(this)) + TabIndexChange(evt); + } +} + +void DlgEdObj::_elementInserted() +{ + if (isListening()) + { + // dialog model changed + GetDialogEditor().SetDialogModelChanged(); + } +} + +void DlgEdObj::_elementReplaced() +{ + if (isListening()) + { + // dialog model changed + GetDialogEditor().SetDialogModelChanged(); + } +} + +void DlgEdObj::_elementRemoved() +{ + if (isListening()) + { + // dialog model changed + GetDialogEditor().SetDialogModelChanged(); + } +} + +void DlgEdObj::SetLayer(SdrLayerID nLayer) +{ + SdrLayerID nOldLayer = GetLayer(); + + if ( nLayer != nOldLayer ) + { + SdrUnoObj::SetLayer( nLayer ); + + DlgEdHint aHint( DlgEdHint::LAYERCHANGED, this ); + GetDlgEdForm()->GetDlgEditor().Broadcast( aHint ); + } +} + +DlgEdForm::DlgEdForm( + SdrModel& rSdrModel, + DlgEditor& rDlgEditor_) +: DlgEdObj(rSdrModel), + rDlgEditor(rDlgEditor_) +{ +} + +DlgEdForm::~DlgEdForm() +{ +} + +void DlgEdForm::SetRectFromProps() +{ + // get form position and size from properties + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( !xPSet.is() ) + return; + + sal_Int32 nXIn = 0, nYIn = 0, nWidthIn = 0, nHeightIn = 0; + xPSet->getPropertyValue( DLGED_PROP_POSITIONX ) >>= nXIn; + xPSet->getPropertyValue( DLGED_PROP_POSITIONY ) >>= nYIn; + xPSet->getPropertyValue( DLGED_PROP_WIDTH ) >>= nWidthIn; + xPSet->getPropertyValue( DLGED_PROP_HEIGHT ) >>= nHeightIn; + + // transform coordinates + sal_Int32 nXOut, nYOut, nWidthOut, nHeightOut; + if ( TransformFormToSdrCoordinates( nXIn, nYIn, nWidthIn, nHeightIn, nXOut, nYOut, nWidthOut, nHeightOut ) ) + { + // set rectangle position and size + Point aPoint( nXOut, nYOut ); + Size aSize( nWidthOut, nHeightOut ); + SetSnapRect( tools::Rectangle( aPoint, aSize ) ); + } +} + +void DlgEdForm::SetPropsFromRect() +{ + // get form position and size from rectangle + tools::Rectangle aRect_ = GetSnapRect(); + sal_Int32 nXIn = aRect_.Left(); + sal_Int32 nYIn = aRect_.Top(); + sal_Int32 nWidthIn = aRect_.GetWidth(); + sal_Int32 nHeightIn = aRect_.GetHeight(); + + // transform coordinates + sal_Int32 nXOut, nYOut, nWidthOut, nHeightOut; + if ( TransformSdrToFormCoordinates( nXIn, nYIn, nWidthIn, nHeightIn, nXOut, nYOut, nWidthOut, nHeightOut ) ) + { + // set properties + Reference< beans::XPropertySet > xPSet( GetUnoControlModel(), UNO_QUERY ); + if ( xPSet.is() ) + { + xPSet->setPropertyValue( DLGED_PROP_POSITIONX, Any(nXOut) ); + xPSet->setPropertyValue( DLGED_PROP_POSITIONY, Any(nYOut) ); + xPSet->setPropertyValue( DLGED_PROP_WIDTH, Any(nWidthOut) ); + xPSet->setPropertyValue( DLGED_PROP_HEIGHT, Any(nHeightOut) ); + } + } +} + +void DlgEdForm::AddChild( DlgEdObj* pDlgEdObj ) +{ + pChildren.push_back( pDlgEdObj ); +} + +void DlgEdForm::RemoveChild( DlgEdObj* pDlgEdObj ) +{ + std::erase(pChildren, pDlgEdObj); +} + +void DlgEdForm::PositionAndSizeChange( const beans::PropertyChangeEvent& evt ) +{ + DlgEditor& rEditor = GetDlgEditor(); + DlgEdPage& rPage = rEditor.GetPage(); + + sal_Int32 nPageXIn = 0; + sal_Int32 nPageYIn = 0; + Size aPageSize = rPage.GetSize(); + sal_Int32 nPageWidthIn = aPageSize.Width(); + sal_Int32 nPageHeightIn = aPageSize.Height(); + sal_Int32 nPageX, nPageY, nPageWidth, nPageHeight; + if ( TransformSdrToFormCoordinates( nPageXIn, nPageYIn, nPageWidthIn, nPageHeightIn, nPageX, nPageY, nPageWidth, nPageHeight ) ) + { + Reference< beans::XPropertySet > xPSetForm( GetUnoControlModel(), UNO_QUERY ); + if ( xPSetForm.is() ) + { + sal_Int32 nValue = 0; + evt.NewValue >>= nValue; + sal_Int32 nNewValue = nValue; + + if ( evt.PropertyName == DLGED_PROP_POSITIONX ) + { + if ( nNewValue < nPageX ) + nNewValue = nPageX; + } + else if ( evt.PropertyName == DLGED_PROP_POSITIONY ) + { + if ( nNewValue < nPageY ) + nNewValue = nPageY; + } + else if ( evt.PropertyName == DLGED_PROP_WIDTH ) + { + if ( nNewValue < 1 ) + nNewValue = 1; + } + else if ( evt.PropertyName == DLGED_PROP_HEIGHT ) + { + if ( nNewValue < 1 ) + nNewValue = 1; + } + + if ( nNewValue != nValue ) + { + EndListening( false ); + xPSetForm->setPropertyValue( evt.PropertyName, Any(nNewValue) ); + StartListening(); + } + } + } + + bool bAdjustedPageSize = rEditor.AdjustPageSize(); + SetRectFromProps(); + std::vector<DlgEdObj*> const& aChildList = GetChildren(); + + if ( bAdjustedPageSize ) + { + rEditor.InitScrollBars(); + aPageSize = rPage.GetSize(); + nPageWidthIn = aPageSize.Width(); + nPageHeightIn = aPageSize.Height(); + if ( TransformSdrToControlCoordinates( nPageXIn, nPageYIn, nPageWidthIn, nPageHeightIn, nPageX, nPageY, nPageWidth, nPageHeight ) ) + { + for (auto const& child : aChildList) + { + Reference< beans::XPropertySet > xPSet( child->GetUnoControlModel(), UNO_QUERY ); + if ( xPSet.is() ) + { + sal_Int32 nX = 0, nY = 0, nWidth = 0, nHeight = 0; + xPSet->getPropertyValue( DLGED_PROP_POSITIONX ) >>= nX; + xPSet->getPropertyValue( DLGED_PROP_POSITIONY ) >>= nY; + xPSet->getPropertyValue( DLGED_PROP_WIDTH ) >>= nWidth; + xPSet->getPropertyValue( DLGED_PROP_HEIGHT ) >>= nHeight; + + sal_Int32 nNewX = nX; + if ( nX + nWidth > nPageX + nPageWidth ) + { + nNewX = nPageX + nPageWidth - nWidth; + if ( nNewX < nPageX ) + nNewX = nPageX; + } + if ( nNewX != nX ) + { + EndListening( false ); + xPSet->setPropertyValue( DLGED_PROP_POSITIONX, Any(nNewX) ); + StartListening(); + } + + sal_Int32 nNewY = nY; + if ( nY + nHeight > nPageY + nPageHeight ) + { + nNewY = nPageY + nPageHeight - nHeight; + if ( nNewY < nPageY ) + nNewY = nPageY; + } + if ( nNewY != nY ) + { + EndListening( false ); + xPSet->setPropertyValue( DLGED_PROP_POSITIONY, Any(nNewY) ); + StartListening(); + } + } + } + } + } + + for (auto const& child : aChildList) + child->SetRectFromProps(); +} + +void DlgEdForm::UpdateStep() +{ + SdrPage* pSdrPage = getSdrPageFromSdrObject(); + + if ( pSdrPage ) + { + for (const rtl::Reference<SdrObject>& pObj : *pSdrPage) + { + DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pObj.get()); + if (pDlgEdObj && !dynamic_cast<DlgEdForm*>(pDlgEdObj)) + pDlgEdObj->UpdateStep(); + } + } +} + +void DlgEdForm::UpdateTabIndices() +{ + // stop listening with all children + for (auto const& child : pChildren) + { + child->EndListening( false ); + } + + Reference< css::container::XNameAccess > xNameAcc( GetUnoControlModel() , UNO_QUERY ); + if ( xNameAcc.is() ) + { + // get sequence of control names + Sequence< OUString > aNames = xNameAcc->getElementNames(); + const OUString* pNames = aNames.getConstArray(); + sal_Int32 nCtrls = aNames.getLength(); + + // create a map of tab indices and control names, sorted by tab index + IndexToNameMap aIndexToNameMap; + for ( sal_Int32 i = 0; i < nCtrls; ++i ) + { + // get name + OUString aName( pNames[i] ); + + // get tab index + sal_Int16 nTabIndex = -1; + Any aCtrl = xNameAcc->getByName( aName ); + Reference< css::beans::XPropertySet > xPSet; + aCtrl >>= xPSet; + if ( xPSet.is() ) + xPSet->getPropertyValue( DLGED_PROP_TABINDEX ) >>= nTabIndex; + + // insert into map + aIndexToNameMap.emplace( nTabIndex, aName ); + } + + // set new tab indices + sal_Int16 nNewTabIndex = 0; + for (auto const& indexToName : aIndexToNameMap) + { + Any aCtrl = xNameAcc->getByName( indexToName.second ); + Reference< beans::XPropertySet > xPSet; + aCtrl >>= xPSet; + if ( xPSet.is() ) + { + xPSet->setPropertyValue( DLGED_PROP_TABINDEX, Any(nNewTabIndex) ); + nNewTabIndex++; + } + } + + UpdateTabOrderAndGroups(); + } + + // start listening with all children + for (auto const& child : pChildren) + { + child->StartListening(); + } +} + +void DlgEdForm::UpdateTabOrder() +{ + // When the tabindex of a control model changes, the dialog control is + // notified about those changes. Due to #109067# (bad performance of + // dialog editor) the dialog control doesn't activate the tab order + // in design mode. When the dialog editor has reordered all + // tabindices, this method allows to activate the taborder afterwards. + + Reference< awt::XUnoControlContainer > xCont( GetControl(), UNO_QUERY ); + if ( xCont.is() ) + { + Sequence< Reference< awt::XTabController > > aSeqTabCtrls = xCont->getTabControllers(); + const Reference< awt::XTabController >* pTabCtrls = aSeqTabCtrls.getConstArray(); + sal_Int32 nCount = aSeqTabCtrls.getLength(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + pTabCtrls[i]->activateTabOrder(); + } +} + +void DlgEdForm::UpdateGroups() +{ + // The grouping of radio buttons in a dialog is done by vcl. + // In the dialog editor we have two views (=controls) for one + // radio button model. One control is owned by the dialog control, + // but not visible in design mode. The other control is owned by + // the drawing layer object. Whereas the grouping of the first + // control is done by vcl, the grouping of the control in the + // drawing layer has to be done here. + + Reference< awt::XTabControllerModel > xTabModel( GetUnoControlModel() , UNO_QUERY ); + if ( !xTabModel.is() ) + return; + + // create a global list of controls that belong to the dialog + std::vector<DlgEdObj*> aChildList = GetChildren(); + sal_uInt32 nSize = aChildList.size(); + Sequence< Reference< awt::XControl > > aSeqControls( nSize ); + for ( sal_uInt32 i = 0; i < nSize; ++i ) + aSeqControls.getArray()[i] = aChildList[i]->GetControl(); + + sal_Int32 nGroupCount = xTabModel->getGroupCount(); + for ( sal_Int32 nGroup = 0; nGroup < nGroupCount; ++nGroup ) + { + // get a list of control models that belong to this group + OUString aName; + Sequence< Reference< awt::XControlModel > > aSeqModels; + xTabModel->getGroup( nGroup, aSeqModels, aName ); + const Reference< awt::XControlModel >* pModels = aSeqModels.getConstArray(); + sal_Int32 nModelCount = aSeqModels.getLength(); + + // create a list of peers that belong to this group + Sequence< Reference< awt::XWindow > > aSeqPeers( nModelCount ); + for ( sal_Int32 nModel = 0; nModel < nModelCount; ++nModel ) + { + // for each control model find the corresponding control in the global list + const Reference< awt::XControl >* pControls = aSeqControls.getConstArray(); + sal_Int32 nControlCount = aSeqControls.getLength(); + for ( sal_Int32 nControl = 0; nControl < nControlCount; ++nControl ) + { + const Reference< awt::XControl > xCtrl( pControls[nControl] ); + if ( xCtrl.is() ) + { + Reference< awt::XControlModel > xCtrlModel( xCtrl->getModel() ); + if ( xCtrlModel.get() == pModels[nModel].get() ) + { + // get the control peer and insert into the list of peers + aSeqPeers.getArray()[ nModel ].set( xCtrl->getPeer(), UNO_QUERY ); + break; + } + } + } + } + + // set the group at the dialog peer + Reference< awt::XControl > xDlg = GetControl(); + if ( xDlg.is() ) + { + Reference< awt::XVclContainerPeer > xDlgPeer( xDlg->getPeer(), UNO_QUERY ); + if ( xDlgPeer.is() ) + xDlgPeer->setGroup( aSeqPeers ); + } + } +} + +void DlgEdForm::UpdateTabOrderAndGroups() +{ + UpdateTabOrder(); + UpdateGroups(); +} + +void DlgEdForm::NbcMove( const Size& rSize ) +{ + SdrUnoObj::NbcMove( rSize ); + + // set geometry properties of form + EndListening(false); + SetPropsFromRect(); + StartListening(); + + // set geometry properties of all children + for (auto const& child : pChildren) + { + child->EndListening(false); + child->SetPropsFromRect(); + child->StartListening(); + } + + // dialog model changed + GetDlgEditor().SetDialogModelChanged(); +} + +void DlgEdForm::NbcResize(const Point& rRef, const Fraction& xFract, const Fraction& yFract) +{ + SdrUnoObj::NbcResize( rRef, xFract, yFract ); + + // set geometry properties of form + EndListening(false); + SetPropsFromRect(); + StartListening(); + + // set geometry properties of all children + for (auto const& child : pChildren) + { + child->EndListening(false); + child->SetPropsFromRect(); + child->StartListening(); + } + + // dialog model changed + GetDlgEditor().SetDialogModelChanged(); +} + +bool DlgEdForm::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) +{ + bool bResult = SdrUnoObj::EndCreate(rStat, eCmd); + + // stop listening + EndListening(false); + + // set geometry properties + SetPropsFromRect(); + + // dialog model changed + GetDlgEditor().SetDialogModelChanged(); + + // start listening + StartListening(); + + return bResult; +} + +awt::DeviceInfo DlgEdForm::getDeviceInfo() const +{ + awt::DeviceInfo aDeviceInfo; + + DlgEditor& rEditor = GetDlgEditor(); + vcl::Window& rWindow = rEditor.GetWindow(); + + // obtain an XControl + ::utl::SharedUNOComponent< awt::XControl > xDialogControl; // ensures auto-disposal, if needed + xDialogControl.reset( GetControl(), ::utl::SharedUNOComponent< awt::XControl >::NoTakeOwnership ); + if ( !xDialogControl.is() ) + { + // don't create a temporary control all the time, this method here is called + // way too often. Instead, use a cached DeviceInfo. + // #i74065# + if ( !!mpDeviceInfo ) + return *mpDeviceInfo; + + Reference< awt::XControlContainer > xEditorControlContainer( rEditor.GetWindowControlContainer() ); + xDialogControl.reset( + GetTemporaryControlForWindow(rWindow, xEditorControlContainer), + utl::SharedUNOComponent< awt::XControl >::TakeOwnership + ); + } + + Reference< awt::XDevice > xDialogDevice; + if ( xDialogControl.is() ) + xDialogDevice.set( xDialogControl->getPeer(), UNO_QUERY ); + DBG_ASSERT( xDialogDevice.is(), "DlgEdForm::getDeviceInfo: no device!" ); + if ( xDialogDevice.is() ) + aDeviceInfo = xDialogDevice->getInfo(); + + mpDeviceInfo = aDeviceInfo; + + return aDeviceInfo; +} +void DlgEdObj::MakeDataAware( const Reference< frame::XModel >& xModel ) +{ + // Need to flesh this out, currently we will only support data-aware controls for calc + // and only handle a subset of functionality e.g. linked-cell and cell range data source. Of course later + // we need to disambiguate for writer ( and others ? ) and also support the generic form (db) bindings + // we need some more work in xmlscript to be able to handle that + Reference< lang::XMultiServiceFactory > xFac( xModel, UNO_QUERY ); + Reference< form::binding::XBindableValue > xBindable( GetUnoControlModel(), UNO_QUERY ); + Reference< form::binding::XListEntrySink > xListEntrySink( GetUnoControlModel(), UNO_QUERY ); + if ( !xFac.is() ) + return; + + //tdf#90361 and tdf#104011 CellValueBinding and CellRangeListSource are unusable + //without being initialized, so use createInstanceWithArguments with a + //dummy BoundCell and CellRange instead of createInstance. This at least results in + //the dialog editor not falling. + css::beans::NamedValue aCellValue; + aCellValue.Name = "BoundCell"; + css::table::CellAddress aCellAddress; + aCellValue.Value <<= aCellAddress; + + css::beans::NamedValue aCellRange; + aCellRange.Name = "CellRange"; + css::table::CellRangeAddress aRangeAddress; + aCellRange.Value <<= aRangeAddress; + + Sequence< Any > aArgs{ Any(aCellValue), Any(aCellRange) }; + + if ( xBindable.is() ) + { + Reference< form::binding::XValueBinding > xBinding( xFac->createInstanceWithArguments( "com.sun.star.table.CellValueBinding", aArgs ), UNO_QUERY ); + xBindable->setValueBinding( xBinding ); + } + if ( xListEntrySink.is() ) + { + Reference< form::binding::XListEntrySource > xSource( xFac->createInstanceWithArguments( "com.sun.star.table.CellRangeListSource", aArgs ), UNO_QUERY ); + xListEntrySink->setListEntrySource( xSource ); + } +} +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedpage.cxx b/basctl/source/dlged/dlgedpage.cxx new file mode 100644 index 0000000000..760f885276 --- /dev/null +++ b/basctl/source/dlged/dlgedpage.cxx @@ -0,0 +1,66 @@ +/* -*- 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 <dlgedpage.hxx> +#include <dlged.hxx> +#include <dlgedmod.hxx> +#include <dlgedobj.hxx> + +namespace basctl +{ + + +DlgEdPage::DlgEdPage(DlgEdModel& rModel, bool bMasterPage) +: SdrPage(rModel, bMasterPage) + ,pDlgEdForm(nullptr) +{ +} + +DlgEdPage::~DlgEdPage() +{ + // clear SdrObjects with broadcasting + ClearSdrObjList(); +} + +rtl::Reference<SdrPage> DlgEdPage::CloneSdrPage(SdrModel& rTargetModel) const +{ + DlgEdModel& rDlgEdModel(static_cast< DlgEdModel& >(rTargetModel)); + rtl::Reference<DlgEdPage> pClonedDlgEdPage = + new DlgEdPage( + rDlgEdModel, + IsMasterPage()); + pClonedDlgEdPage->SdrPage::lateInit(*this); + return pClonedDlgEdPage; +} + + +SdrObject* DlgEdPage::SetObjectOrdNum(size_t nOldObjNum, size_t nNewObjNum) +{ + SdrObject* pObj = SdrPage::SetObjectOrdNum( nOldObjNum, nNewObjNum ); + + DlgEdHint aHint( DlgEdHint::OBJORDERCHANGED ); + if ( pDlgEdForm ) + pDlgEdForm->GetDlgEditor().Broadcast( aHint ); + + return pObj; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/dlgedview.cxx b/basctl/source/dlged/dlgedview.cxx new file mode 100644 index 0000000000..81271d38f8 --- /dev/null +++ b/basctl/source/dlged/dlgedview.cxx @@ -0,0 +1,184 @@ +/* -*- 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 <dlgedview.hxx> +#include <dlged.hxx> +#include <dlgedpage.hxx> + +#include <svtools/scrolladaptor.hxx> +#include <vcl/canvastools.hxx> + +#include <dlgedobj.hxx> + +namespace basctl +{ + +DlgEdView::DlgEdView( + SdrModel& rSdrModel, + OutputDevice& rOut, + DlgEditor& rEditor) +: SdrView(rSdrModel, &rOut), + rDlgEditor(rEditor) +{ + SetBufferedOutputAllowed(true); + SetBufferedOverlayAllowed(true); +} + +DlgEdView::~DlgEdView() +{ +} + +void DlgEdView::MarkListHasChanged() +{ + SdrView::MarkListHasChanged(); + + DlgEdHint aHint( DlgEdHint::SELECTIONCHANGED ); + rDlgEditor.Broadcast( aHint ); + rDlgEditor.UpdatePropertyBrowserDelayed(); +} + +void DlgEdView::MakeVisible( const tools::Rectangle& rRect, vcl::Window& rWin ) +{ + // visible area + MapMode aMap( rWin.GetMapMode() ); + Point aOrg( aMap.GetOrigin() ); + Size aVisSize( rWin.GetOutDev()->GetOutputSize() ); + tools::Rectangle RectTmp( Point(-aOrg.X(),-aOrg.Y()), aVisSize ); + tools::Rectangle aVisRect( RectTmp ); + + // check, if rectangle is inside visible area + if ( aVisRect.Contains( rRect ) ) + return; + + // calculate scroll distance; the rectangle must be inside the visible area + sal_Int32 nScrollX = 0, nScrollY = 0; + + sal_Int32 nVisLeft = aVisRect.Left(); + sal_Int32 nVisRight = aVisRect.Right(); + sal_Int32 nVisTop = aVisRect.Top(); + sal_Int32 nVisBottom = aVisRect.Bottom(); + + sal_Int32 nDeltaX = rDlgEditor.GetHScroll()->GetLineSize(); + sal_Int32 nDeltaY = rDlgEditor.GetVScroll()->GetLineSize(); + + while ( rRect.Right() > nVisRight + nScrollX ) + nScrollX += nDeltaX; + + while ( rRect.Left() < nVisLeft + nScrollX ) + nScrollX -= nDeltaX; + + while ( rRect.Bottom() > nVisBottom + nScrollY ) + nScrollY += nDeltaY; + + while ( rRect.Top() < nVisTop + nScrollY ) + nScrollY -= nDeltaY; + + // don't scroll beyond the page size + Size aPageSize = rDlgEditor.GetPage().GetSize(); + sal_Int32 nPageWidth = aPageSize.Width(); + sal_Int32 nPageHeight = aPageSize.Height(); + + if ( nVisRight + nScrollX > nPageWidth ) + nScrollX = nPageWidth - nVisRight; + + if ( nVisLeft + nScrollX < 0 ) + nScrollX = -nVisLeft; + + if ( nVisBottom + nScrollY > nPageHeight ) + nScrollY = nPageHeight - nVisBottom; + + if ( nVisTop + nScrollY < 0 ) + nScrollY = -nVisTop; + + // scroll window + rWin.PaintImmediately(); + rWin.Scroll( -nScrollX, -nScrollY ); + aMap.SetOrigin( Point( aOrg.X() - nScrollX, aOrg.Y() - nScrollY ) ); + rWin.SetMapMode( aMap ); + rWin.Invalidate(); + + // update scroll bars + rDlgEditor.UpdateScrollBars(); + + DlgEdHint aHint( DlgEdHint::WINDOWSCROLLED ); + rDlgEditor.Broadcast( aHint ); +} + +static SdrObject* impLocalHitCorrection(SdrObject* pRetval, const Point& rPnt, sal_uInt16 nTol) +{ + DlgEdObj* pDlgEdObj = dynamic_cast< DlgEdObj* >(pRetval); + + if(pDlgEdObj) + { + bool bExcludeInner(false); + + if(dynamic_cast< DlgEdForm* >(pRetval) != nullptr) + { + // from DlgEdForm::CheckHit; exclude inner for DlgEdForm + bExcludeInner = true; + } + else if(pDlgEdObj->supportsService("com.sun.star.awt.UnoControlGroupBoxModel")) + { + // from DlgEdObj::CheckHit; exclude inner for group shapes + bExcludeInner = true; + } + + if(bExcludeInner) + { + // use direct model data; it's a DlgEdObj, so GetLastBoundRect() + // will access aOutRect directly + const tools::Rectangle aOuterRectangle(pDlgEdObj->GetLastBoundRect()); + + if(!aOuterRectangle.IsEmpty()) + { + basegfx::B2DRange aOuterRange = vcl::unotools::b2DRectangleFromRectangle(aOuterRectangle); + + if(nTol) + { + aOuterRange.grow(-1.0 * nTol); + } + + if(aOuterRange.isInside(basegfx::B2DPoint(rPnt.X(), rPnt.Y()))) + { + pRetval = nullptr; + } + } + } + } + + return pRetval; +} + +SdrObject* DlgEdView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const +{ + // call parent + SdrObject* pRetval = SdrView::CheckSingleSdrObjectHit(rPnt, nTol, pObj, pPV, nOptions, pMVisLay); + + if(pRetval) + { + // check hit object locally + pRetval = impLocalHitCorrection(pRetval, rPnt, nTol); + } + + return pRetval; +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/managelang.cxx b/basctl/source/dlged/managelang.cxx new file mode 100644 index 0000000000..69f366ed66 --- /dev/null +++ b/basctl/source/dlged/managelang.cxx @@ -0,0 +1,318 @@ +/* -*- 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 <basidesh.hxx> +#include <basobj.hxx> +#include <iderdll.hxx> +#include <iderid.hxx> +#include <localizationmgr.hxx> +#include <managelang.hxx> + +#include <strings.hrc> + +#include <comphelper/sequence.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/sfxsids.hrc> +#include <svtools/langtab.hxx> +#include <svx/langbox.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/settings.hxx> +#include <tools/debug.hxx> + +namespace basctl +{ + +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::resource; +using namespace ::com::sun::star::uno; + +bool localesAreEqual( const Locale& rLocaleLeft, const Locale& rLocaleRight ) +{ + bool bRet = ( rLocaleLeft.Language == rLocaleRight.Language && + rLocaleLeft.Country == rLocaleRight.Country && + rLocaleLeft.Variant == rLocaleRight.Variant ); + return bRet; +} + +ManageLanguageDialog::ManageLanguageDialog(weld::Window* pParent, std::shared_ptr<LocalizationMgr> xLMgr) + : GenericDialogController(pParent, "modules/BasicIDE/ui/managelanguages.ui", "ManageLanguagesDialog") + , m_xLocalizationMgr(std::move(xLMgr)) + , m_sDefLangStr(IDEResId(RID_STR_DEF_LANG)) + , m_sCreateLangStr(IDEResId(RID_STR_CREATE_LANG)) + , m_xLanguageLB(m_xBuilder->weld_tree_view("treeview")) + , m_xAddPB(m_xBuilder->weld_button("add")) + , m_xDeletePB(m_xBuilder->weld_button("delete")) + , m_xMakeDefPB(m_xBuilder->weld_button("default")) +{ + m_xLanguageLB->set_size_request(m_xLanguageLB->get_approximate_digit_width() * 42, + m_xLanguageLB->get_height_rows(10)); + + Init(); + FillLanguageBox(); + SelectHdl( *m_xLanguageLB ); +} + +ManageLanguageDialog::~ManageLanguageDialog() +{ + ClearLanguageBox(); +} + +void ManageLanguageDialog::Init() +{ + // get current IDE + Shell* pShell = GetShell(); + const OUString& sLibName = pShell->GetCurLibName(); + // set dialog title with library name + OUString sText = m_xDialog->get_title(); + sText = sText.replaceAll("$1", sLibName); + m_xDialog->set_title(sText); + // set handler + m_xAddPB->connect_clicked( LINK( this, ManageLanguageDialog, AddHdl ) ); + m_xDeletePB->connect_clicked( LINK( this, ManageLanguageDialog, DeleteHdl ) ); + m_xMakeDefPB->connect_clicked( LINK( this, ManageLanguageDialog, MakeDefHdl ) ); + m_xLanguageLB->connect_changed( LINK( this, ManageLanguageDialog, SelectHdl ) ); + + m_xLanguageLB->set_selection_mode(SelectionMode::Multiple); +} + +void ManageLanguageDialog::FillLanguageBox() +{ + DBG_ASSERT( m_xLocalizationMgr, "ManageLanguageDialog::FillLanguageBox(): no localization manager" ); + + if ( m_xLocalizationMgr->isLibraryLocalized() ) + { + Locale aDefaultLocale = m_xLocalizationMgr->getStringResourceManager()->getDefaultLocale(); + Sequence< Locale > aLocaleSeq = m_xLocalizationMgr->getStringResourceManager()->getLocales(); + const Locale* pLocale = aLocaleSeq.getConstArray(); + sal_Int32 i, nCount = aLocaleSeq.getLength(); + for ( i = 0; i < nCount; ++i ) + { + bool bIsDefault = localesAreEqual( aDefaultLocale, pLocale[i] ); + LanguageType eLangType = LanguageTag::convertToLanguageType( pLocale[i] ); + OUString sLanguage = SvtLanguageTable::GetLanguageString( eLangType ); + if ( bIsDefault ) + { + sLanguage += " " + m_sDefLangStr; + } + LanguageEntry* pEntry = new LanguageEntry(pLocale[i], bIsDefault); + m_xLanguageLB->append(weld::toId(pEntry), sLanguage); + } + } + else + m_xLanguageLB->append_text(m_sCreateLangStr); +} + +void ManageLanguageDialog::ClearLanguageBox() +{ + const sal_Int32 nCount = m_xLanguageLB->n_children(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + LanguageEntry* pEntry = weld::fromId<LanguageEntry*>(m_xLanguageLB->get_id(i)); + delete pEntry; + } + m_xLanguageLB->clear(); +} + +IMPL_LINK_NOARG(ManageLanguageDialog, AddHdl, weld::Button&, void) +{ + auto xDlg = std::make_shared<SetDefaultLanguageDialog>(m_xDialog.get(), m_xLocalizationMgr); + weld::DialogController::runAsync(xDlg, [xDlg,this](sal_Int32 nResult) + { + if (!nResult ) + return; + // add new locales + Sequence< Locale > aLocaleSeq = xDlg->GetLocales(); + m_xLocalizationMgr->handleAddLocales( aLocaleSeq ); + // update listbox + ClearLanguageBox(); + FillLanguageBox(); + + if (SfxBindings* pBindings = GetBindingsPtr()) + pBindings->Invalidate( SID_BASICIDE_CURRENT_LANG ); + }); +} + +IMPL_LINK_NOARG(ManageLanguageDialog, DeleteHdl, weld::Button&, void) +{ + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "modules/BasicIDE/ui/deletelangdialog.ui")); + std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("DeleteLangDialog")); + if (xQBox->run() != RET_OK) + return; + + std::vector<int> aSelection = m_xLanguageLB->get_selected_rows(); + int nCount = aSelection.size(); + int nPos = m_xLanguageLB->get_selected_index(); + // remove locales + Sequence< Locale > aLocaleSeq( nCount ); + auto aLocaleSeqRange = asNonConstRange(aLocaleSeq); + for (int i = 0; i < nCount; ++i) + { + const sal_Int32 nSelPos = aSelection[i]; + LanguageEntry* pEntry = weld::fromId<LanguageEntry*>(m_xLanguageLB->get_id(nSelPos)); + if ( pEntry ) + aLocaleSeqRange[i] = pEntry->m_aLocale; + } + m_xLocalizationMgr->handleRemoveLocales( aLocaleSeq ); + // update listbox + ClearLanguageBox(); + FillLanguageBox(); + // reset selection + nCount = m_xLanguageLB->n_children(); + if (nCount <= nPos) + nPos = nCount - 1; + m_xLanguageLB->select(nPos); + SelectHdl( *m_xLanguageLB ); +} + +IMPL_LINK_NOARG(ManageLanguageDialog, MakeDefHdl, weld::Button&, void) +{ + const sal_Int32 nPos = m_xLanguageLB->get_selected_index(); + LanguageEntry* pSelectEntry = weld::fromId<LanguageEntry*>(m_xLanguageLB->get_id(nPos)); + if (pSelectEntry && !pSelectEntry->m_bIsDefault) + { + // set new default entry + m_xLocalizationMgr->handleSetDefaultLocale( pSelectEntry->m_aLocale ); + // update Listbox + ClearLanguageBox(); + FillLanguageBox(); + // reset selection + m_xLanguageLB->select(nPos); + SelectHdl( *m_xLanguageLB ); + } +} + +IMPL_LINK_NOARG(ManageLanguageDialog, SelectHdl, weld::TreeView&, void) +{ + const sal_Int32 nCount = m_xLanguageLB->n_children(); + bool bEmpty = ( !nCount || + m_xLanguageLB->find_text(m_sCreateLangStr) != -1 ); + bool bSelect = ( m_xLanguageLB->get_selected_index() != -1 ); + bool bEnable = !bEmpty && bSelect; + + m_xDeletePB->set_sensitive(bEnable); + m_xMakeDefPB->set_sensitive(bEnable && nCount > 1 && m_xLanguageLB->count_selected_rows() == 1); +} + +// class SetDefaultLanguageDialog ----------------------------------------------- + +SetDefaultLanguageDialog::SetDefaultLanguageDialog(weld::Window* pParent, std::shared_ptr<LocalizationMgr> xLMgr) + : GenericDialogController(pParent, "modules/BasicIDE/ui/defaultlanguage.ui", "DefaultLanguageDialog") + , m_xLocalizationMgr(std::move(xLMgr)) + , m_xLanguageFT(m_xBuilder->weld_label("defaultlabel")) + , m_xLanguageLB(m_xBuilder->weld_tree_view("entries")) + , m_xCheckLangFT(m_xBuilder->weld_label("checkedlabel")) + , m_xCheckLangLB(m_xBuilder->weld_tree_view("checkedentries")) + , m_xDefinedFT(m_xBuilder->weld_label("defined")) + , m_xAddedFT(m_xBuilder->weld_label("added")) + , m_xAltTitle(m_xBuilder->weld_label("alttitle")) + , m_xLanguageCB(new SvxLanguageBox(m_xBuilder->weld_combo_box("hidden"))) +{ + m_xLanguageLB->set_size_request(-1, m_xLanguageLB->get_height_rows(10)); + m_xCheckLangLB->set_size_request(-1, m_xCheckLangLB->get_height_rows(10)); + m_xCheckLangLB->enable_toggle_buttons(weld::ColumnToggleType::Check); + + if (m_xLocalizationMgr->isLibraryLocalized()) + { + // change to "Add Interface Language" mode + m_xLanguageLB->hide(); + m_xCheckLangLB->show(); + m_xDialog->set_title(m_xAltTitle->get_label()); + m_xLanguageFT->hide(); + m_xCheckLangFT->show(); + m_xDefinedFT->hide(); + m_xAddedFT->show(); + } + + FillLanguageBox(); +} + +SetDefaultLanguageDialog::~SetDefaultLanguageDialog() +{ +} + +void SetDefaultLanguageDialog::FillLanguageBox() +{ + // fill list with all languages + m_xLanguageCB->SetLanguageList(SvxLanguageListFlags::ALL, false); + + if (m_xLocalizationMgr->isLibraryLocalized()) + { + // remove the already localized languages + Sequence< Locale > aLocaleSeq = m_xLocalizationMgr->getStringResourceManager()->getLocales(); + const Locale* pLocale = aLocaleSeq.getConstArray(); + const sal_Int32 nCountLoc = aLocaleSeq.getLength(); + for ( sal_Int32 i = 0; i < nCountLoc; ++i ) + m_xLanguageCB->remove_id(LanguageTag::convertToLanguageType(pLocale[i])); + + // fill checklistbox if not in default mode + const sal_Int32 nCountLang = m_xLanguageCB->get_count(); + for (sal_Int32 j = 0; j < nCountLang; ++j) + { + LanguageType eLang = m_xLanguageCB->get_id(j); + m_xCheckLangLB->append(); + const int nRow = m_xCheckLangLB->n_children() - 1; + m_xCheckLangLB->set_toggle(nRow, TRISTATE_FALSE); + m_xCheckLangLB->set_text(nRow, m_xLanguageCB->get_text(j), 0); + m_xCheckLangLB->set_id(nRow, OUString::number(eLang.get())); + } + m_xLanguageCB.reset(); + m_xLanguageLB.reset(); + } + else + { + const sal_Int32 nCountLang = m_xLanguageCB->get_count(); + for (sal_Int32 j = 0; j < nCountLang; ++j) + { + LanguageType eLang = m_xLanguageCB->get_id(j); + m_xLanguageLB->append(OUString::number(eLang.get()), m_xLanguageCB->get_text(j)); + } + m_xLanguageCB.reset(); + + // preselect current UI language + m_xLanguageLB->select_id(OUString::number(Application::GetSettings().GetUILanguageTag().getLanguageType().get())); + } +} + +Sequence< Locale > SetDefaultLanguageDialog::GetLocales() const +{ + bool bNotLocalized = !m_xLocalizationMgr->isLibraryLocalized(); + if (bNotLocalized) + { + LanguageType eType(m_xLanguageLB->get_selected_id().toUInt32()); + return {LanguageTag(eType).getLocale()}; + } + std::vector<Locale> aLocaleSeq; + const sal_Int32 nCount = m_xCheckLangLB->n_children(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + if (m_xCheckLangLB->get_toggle(i) == TRISTATE_TRUE) + { + LanguageType eType(m_xCheckLangLB->get_id(i).toUInt32()); + aLocaleSeq.push_back(LanguageTag::convertToLocale(eType)); + } + } + return comphelper::containerToSequence(aLocaleSeq); +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/dlged/propbrw.cxx b/basctl/source/dlged/propbrw.cxx new file mode 100644 index 0000000000..bb45d5f13e --- /dev/null +++ b/basctl/source/dlged/propbrw.cxx @@ -0,0 +1,506 @@ +/* -*- 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 <propbrw.hxx> +#include <basidesh.hxx> +#include <dlgedobj.hxx> +#include <iderid.hxx> +#include <baside3.hxx> +#include <strings.hrc> + +#include <strings.hxx> + +#include <com/sun/star/frame/Frame.hpp> +#include <com/sun/star/inspection/XObjectInspector.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/types.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/component_context.hxx> +#include <tools/debug.hxx> +#include <svx/svditer.hxx> +#include <svx/svdview.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/layout.hxx> +#include <vcl/stdtext.hxx> +#include <vcl/weld.hxx> + +#include <memory> + +namespace basctl +{ + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::comphelper; + + +void PropBrw::Update( const SfxViewShell* pShell ) +{ + Shell const* pIdeShell = dynamic_cast<Shell const*>(pShell); + OSL_ENSURE( pIdeShell || !pShell, "PropBrw::Update: invalid shell!" ); + if (pIdeShell) + ImplUpdate(pIdeShell->GetCurrentDocument(), pIdeShell->GetCurDlgView()); + else if (pShell) + ImplUpdate(nullptr, pShell->GetDrawView()); + else + ImplUpdate(nullptr, nullptr); +} + + +namespace +{ + +const tools::Long STD_WIN_SIZE_X = 300; +const tools::Long STD_WIN_SIZE_Y = 350; + +const tools::Long STD_MIN_SIZE_X = 250; +const tools::Long STD_MIN_SIZE_Y = 250; + +const tools::Long WIN_BORDER = 2; + +} // namespace + +PropBrw::PropBrw (DialogWindowLayout& rLayout_): + DockingWindow(&rLayout_), + m_xContentArea(VclPtr<VclVBox>::Create(this)), + m_bInitialStateChange(true), + m_xContextDocument(SfxViewShell::Current() ? SfxViewShell::Current()->GetCurrentDocument() : Reference<XModel>()), + pView(nullptr) +{ + Size aPropWinSize(STD_WIN_SIZE_X,STD_WIN_SIZE_Y); + SetMinOutputSizePixel(Size(STD_MIN_SIZE_X,STD_MIN_SIZE_Y)); + SetOutputSizePixel(aPropWinSize); + + // turn off WB_CLIPCHILDREN otherwise the bg won't extend "under" + // transparent children of the widget + m_xContentArea->SetControlBackground(m_xContentArea->GetSettings().GetStyleSettings().GetWindowColor()); + m_xContentArea->SetBackground(m_xContentArea->GetControlBackground()); + m_xContentArea->SetStyle(m_xContentArea->GetStyle() & ~WB_CLIPCHILDREN); + m_xContentArea->Show(); + + try + { + // create a frame wrapper for myself + m_xMeAsFrame = frame::Frame::create( comphelper::getProcessComponentContext() ); + m_xMeAsFrame->initialize(VCLUnoHelper::GetInterface(m_xContentArea)); + m_xMeAsFrame->setName( "form property browser" ); // change name! + } + catch (const Exception&) + { + OSL_FAIL("PropBrw::PropBrw: could not create/initialize my frame!"); + m_xMeAsFrame.clear(); + } + + ImplReCreateController(); +} + + +void PropBrw::ImplReCreateController() +{ + OSL_PRECOND( m_xMeAsFrame.is(), "PropBrw::ImplCreateController: no frame for myself!" ); + if ( !m_xMeAsFrame.is() ) + return; + + if ( m_xBrowserController.is() ) + ImplDestroyController(); + + try + { + Reference< XComponentContext > xOwnContext = comphelper::getProcessComponentContext(); + + // a ComponentContext for the + ::cppu::ContextEntry_Init aHandlerContextInfo[] = + { + ::cppu::ContextEntry_Init( "DialogParentWindow", Any(VCLUnoHelper::GetInterface(this))), + ::cppu::ContextEntry_Init( "ContextDocument", Any( m_xContextDocument ) ) + }; + Reference< XComponentContext > xInspectorContext( + ::cppu::createComponentContext( aHandlerContextInfo, std::size( aHandlerContextInfo ), xOwnContext ) ); + + // create a property browser controller + Reference< XMultiComponentFactory > xFactory( xInspectorContext->getServiceManager(), UNO_SET_THROW ); + static constexpr OUString s_sControllerServiceName = u"com.sun.star.awt.PropertyBrowserController"_ustr; + m_xBrowserController.set( xFactory->createInstanceWithContext( s_sControllerServiceName, xInspectorContext ), UNO_QUERY ); + if ( !m_xBrowserController.is() ) + { + vcl::Window* pWin = GetParent(); + ShowServiceNotAvailableError(pWin ? pWin->GetFrameWeld() : nullptr, s_sControllerServiceName, true); + } + else + { + Reference< XController > xAsXController( m_xBrowserController, UNO_QUERY ); + DBG_ASSERT(xAsXController.is(), "PropBrw::PropBrw: invalid controller object!"); + if (!xAsXController.is()) + { + ::comphelper::disposeComponent(m_xBrowserController); + m_xBrowserController.clear(); + } + else + { + xAsXController->attachFrame( Reference<XFrame>(m_xMeAsFrame,UNO_QUERY_THROW) ); + } + } + + Point aPropWinPos( WIN_BORDER, WIN_BORDER ); + Size aPropWinSize(STD_WIN_SIZE_X,STD_WIN_SIZE_Y); + aPropWinSize.AdjustWidth( -(2*WIN_BORDER) ); + aPropWinSize.AdjustHeight( -(2*WIN_BORDER) ); + + VclContainer::setLayoutAllocation(*m_xContentArea, aPropWinPos, aPropWinSize); + m_xContentArea->Show(); + } + catch (const Exception&) + { + OSL_FAIL("PropBrw::PropBrw: could not create/initialize the browser controller!"); + try + { + ::comphelper::disposeComponent(m_xBrowserController); + } + catch(const Exception&) + { + } + + m_xBrowserController.clear(); + } + Resize(); +} + +PropBrw::~PropBrw() +{ + disposeOnce(); +} + +void PropBrw::dispose() +{ + if ( m_xBrowserController.is() ) + ImplDestroyController(); + m_xContentArea.disposeAndClear(); + DockingWindow::dispose(); +} + + +void PropBrw::ImplDestroyController() +{ + implSetNewObject( Reference< XPropertySet >() ); + + if ( m_xMeAsFrame.is() ) + m_xMeAsFrame->setComponent( nullptr, nullptr ); + + Reference< XController > xAsXController( m_xBrowserController, UNO_QUERY ); + if ( xAsXController.is() ) + xAsXController->attachFrame( nullptr ); + + try + { + ::comphelper::disposeComponent( m_xBrowserController ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl"); + } + + m_xBrowserController.clear(); +} + +bool PropBrw::Close() +{ + ImplDestroyController(); + + return DockingWindow::Close(); +} + +Sequence< Reference< XInterface > > + PropBrw::CreateMultiSelectionSequence( const SdrMarkList& _rMarkList ) +{ + Sequence< Reference< XInterface > > aSeq; + InterfaceArray aInterfaces; + + const size_t nMarkCount = _rMarkList.GetMarkCount(); + for( size_t i = 0 ; i < nMarkCount ; ++i ) + { + SdrObject* pCurrent = _rMarkList.GetMark(i)->GetMarkedSdrObj(); + + std::optional<SdrObjListIter> oGroupIterator; + if (pCurrent->IsGroupObject()) + { + oGroupIterator.emplace(pCurrent->GetSubList()); + pCurrent = oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr; + } + + while (pCurrent) + { + if (DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(pCurrent)) + { + Reference< XInterface > xControlInterface(pDlgEdObj->GetUnoControlModel(), UNO_QUERY); + if (xControlInterface.is()) + aInterfaces.push_back(xControlInterface); + } + + // next element + pCurrent = oGroupIterator && oGroupIterator->IsMore() ? oGroupIterator->Next() : nullptr; + } + } + + sal_Int32 nCount = aInterfaces.size(); + aSeq.realloc( nCount ); + Reference< XInterface >* pInterfaces = aSeq.getArray(); + for( sal_Int32 i = 0 ; i < nCount ; i++ ) + pInterfaces[i] = aInterfaces[i]; + + return aSeq; +} + + +void PropBrw::implSetNewObjectSequence + ( const Sequence< Reference< XInterface > >& _rObjectSeq ) +{ + Reference< inspection::XObjectInspector > xObjectInspector(m_xBrowserController, UNO_QUERY); + if ( xObjectInspector.is() ) + { + xObjectInspector->inspect( _rObjectSeq ); + + OUString aText = IDEResId(RID_STR_BRWTITLE_PROPERTIES) + + IDEResId(RID_STR_BRWTITLE_MULTISELECT); + SetText( aText ); + } +} + + +void PropBrw::implSetNewObject( const Reference< XPropertySet >& _rxObject ) +{ + if ( m_xBrowserController.is() ) + { + m_xBrowserController->setPropertyValue( "IntrospectedObject", + Any( _rxObject ) + ); + + // set the new title according to the selected object + SetText( GetHeadlineName( _rxObject ) ); + } +} + + +OUString PropBrw::GetHeadlineName( const Reference< XPropertySet >& _rxObject ) +{ + OUString aName; + Reference< lang::XServiceInfo > xServiceInfo( _rxObject, UNO_QUERY ); + + if (xServiceInfo.is()) // single selection + { + OUString sResId; + aName = IDEResId(RID_STR_BRWTITLE_PROPERTIES); + + if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlDialogModel" ) ) + { + sResId = RID_STR_CLASS_DIALOG; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlButtonModel" ) ) + { + sResId = RID_STR_CLASS_BUTTON; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlRadioButtonModel" ) ) + { + sResId = RID_STR_CLASS_RADIOBUTTON; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlCheckBoxModel" ) ) + { + sResId = RID_STR_CLASS_CHECKBOX; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlListBoxModel" ) ) + { + sResId = RID_STR_CLASS_LISTBOX; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlComboBoxModel" ) ) + { + sResId = RID_STR_CLASS_COMBOBOX; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlGroupBoxModel" ) ) + { + sResId = RID_STR_CLASS_GROUPBOX; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlEditModel" ) ) + { + sResId = RID_STR_CLASS_EDIT; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlFixedTextModel" ) ) + { + sResId = RID_STR_CLASS_FIXEDTEXT; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlImageControlModel" ) ) + { + sResId = RID_STR_CLASS_IMAGECONTROL; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlProgressBarModel" ) ) + { + sResId = RID_STR_CLASS_PROGRESSBAR; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlScrollBarModel" ) ) + { + sResId = RID_STR_CLASS_SCROLLBAR; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlFixedLineModel" ) ) + { + sResId = RID_STR_CLASS_FIXEDLINE; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlDateFieldModel" ) ) + { + sResId = RID_STR_CLASS_DATEFIELD; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlTimeFieldModel" ) ) + { + sResId = RID_STR_CLASS_TIMEFIELD; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlNumericFieldModel" ) ) + { + sResId = RID_STR_CLASS_NUMERICFIELD; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlCurrencyFieldModel" ) ) + { + sResId = RID_STR_CLASS_CURRENCYFIELD; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlFormattedFieldModel" ) ) + { + sResId = RID_STR_CLASS_FORMATTEDFIELD; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlPatternFieldModel" ) ) + { + sResId = RID_STR_CLASS_PATTERNFIELD; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlFileControlModel" ) ) + { + sResId = RID_STR_CLASS_FILECONTROL; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.tree.TreeControlModel" ) ) + { + sResId = RID_STR_CLASS_TREECONTROL; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.grid.UnoControlGridModel" ) ) + { + sResId = RID_STR_CLASS_GRIDCONTROL; + } + else if ( xServiceInfo->supportsService( "com.sun.star.awt.UnoControlFixedHyperlinkModel" ) ) + { + sResId = RID_STR_CLASS_HYPERLINKCONTROL; + } + else + { + sResId = RID_STR_CLASS_CONTROL; + } + + if (!sResId.isEmpty()) + { + aName += sResId; + } + } + else if (!_rxObject.is()) // no properties + { + aName = IDEResId(RID_STR_BRWTITLE_NO_PROPERTIES); + } + + return aName; +} + +void PropBrw::ImplUpdate( const Reference< XModel >& _rxContextDocument, SdrView* pNewView ) +{ + Reference< XModel > xContextDocument( _rxContextDocument ); + + // if we should simply "empty" ourself, assume the context document didn't change + if ( !pNewView ) + { + OSL_ENSURE( !_rxContextDocument.is(), "PropBrw::ImplUpdate: no view, but a document?!" ); + xContextDocument = m_xContextDocument; + } + + if ( xContextDocument != m_xContextDocument ) + { + m_xContextDocument = xContextDocument; + ImplReCreateController(); + } + + try + { + if ( pView ) + { + EndListening(pView->GetModel()); + pView = nullptr; + } + + if ( !pNewView ) + return; + + pView = pNewView; + + // set focus on initialization + if ( m_bInitialStateChange ) + { + m_xContentArea->GrabFocus(); + m_bInitialStateChange = false; + } + + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if ( nMarkCount == 0 ) + { + EndListening(pView->GetModel()); + pView = nullptr; + implSetNewObject( nullptr ); + return; + } + + Reference< XPropertySet > xNewObject; + Sequence< Reference< XInterface > > aNewObjects; + if ( nMarkCount == 1 ) + { + if (DlgEdObj* pDlgEdObj = dynamic_cast<DlgEdObj*>(rMarkList.GetMark(0)->GetMarkedSdrObj())) + { + if ( pDlgEdObj->IsGroupObject() ) // group object + aNewObjects = CreateMultiSelectionSequence( rMarkList ); + else // single selection + xNewObject.set(pDlgEdObj->GetUnoControlModel(), css::uno::UNO_QUERY); + } + } + else if ( nMarkCount > 1 ) // multiple selection + { + aNewObjects = CreateMultiSelectionSequence( rMarkList ); + } + + if ( aNewObjects.hasElements() ) + implSetNewObjectSequence( aNewObjects ); + else + implSetNewObject( xNewObject ); + + StartListening(pView->GetModel()); + } + catch ( const PropertyVetoException& ) { /* silence */ } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("basctl"); + } +} + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/IDEComboBox.hxx b/basctl/source/inc/IDEComboBox.hxx new file mode 100644 index 0000000000..a5e7008a42 --- /dev/null +++ b/basctl/source/inc/IDEComboBox.hxx @@ -0,0 +1,271 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svl/stritem.hxx> +#include <sfx2/tbxctrl.hxx> +#include <vcl/InterimItemWindow.hxx> + +#include "doceventnotifier.hxx" +#include "scriptdocument.hxx" + +namespace basctl +{ +/*! + * @brief Manage states of macro and dialog Library ComboBox + * + * @see LibBox Class + */ +class LibBoxControl : public SfxToolBoxControl +{ +public: + /*! + * Macro for registering two methods + * + * @code + * static SfxToolBoxControl* CreateImpl(sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx) + * static void RegisterControl(sal_uInt16 nSlotId = 0, SfxModule* pMod=nullptr) + * @endcode + * @see Macro SFX_IMPL_TOOLBOX_CONTROL + */ + SFX_DECL_TOOLBOX_CONTROL(); + + /*! + * @param nSlotId -- the slot as internal operation number + * @param nId -- this item's unique id in ToolBox + * @param rTbx -- the ToolBox which contains this ComboBox + */ + LibBoxControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx); + + /*! + * Triggered if state was changed + * + * @param nSlotID -- the slot as internal operation number (not used in this place) + * @param eState -- enum value which contains ComboBox state + * @param pState -- + */ + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSlotID, SfxItemState eState, + const SfxPoolItem* pState) override; + /*! + * Create combobox of Macro and Dialog Library + * + * @param pParent -- parent window + * @return ComboBox of macro and dialog Library + */ + virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override; +}; + +/*! + * @brief Base class for all ComboBox elements. + * + * Base class for ComboBoxes which need to update their content according + * to the list of open documents. + */ +class DocListenerBox : public InterimItemWindow, public DocumentEventListener +{ +private: + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + +protected: + std::unique_ptr<weld::ComboBox> m_xWidget; + + /// @param pParent -- parent window + DocListenerBox(vcl::Window* pParent); + virtual ~DocListenerBox() override; + virtual void dispose() override; + + virtual void Select() = 0; + virtual void FillBox() = 0; + + /// key strokes the ComboBox receives + virtual bool HandleKeyInput(const KeyEvent& rKEvt); + +private: + // DocumentEventListener + virtual void onDocumentCreated(const ScriptDocument& _rDoc) override; + virtual void onDocumentOpened(const ScriptDocument& _rDoc) override; + virtual void onDocumentSave(const ScriptDocument& _rDoc) override; + virtual void onDocumentSaveDone(const ScriptDocument& _rDoc) override; + virtual void onDocumentSaveAs(const ScriptDocument& _rDoc) override; + virtual void onDocumentSaveAsDone(const ScriptDocument& _rDoc) override; + virtual void onDocumentClosed(const ScriptDocument& _rDoc) override; + virtual void onDocumentTitleChanged(const ScriptDocument& _rDoc) override; + virtual void onDocumentModeChanged(const ScriptDocument& _rDoc) override; + + DocumentEventNotifier maNotifier; + +public: + void set_sensitive(bool bSensitive); +}; + +/*! + * @brief Macros and Dialogs Library ComboBox + * + * @see LibBoxControl Class + */ +class LibBox : public DocListenerBox +{ +public: + /// @param pParent + LibBox(vcl::Window* pParent); + virtual ~LibBox() override; + virtual void dispose() override; + + /*! + * Update selection in ComboBox of macro and dialog Library + * + * @param pItem -- string that was selected + */ + void Update(const SfxStringItem* pItem); + +protected: + /// Called for setting language when user selects a language in ComboBox + virtual void Select() override; + +private: + static void ReleaseFocus(); + + /*! + * Insert name library in specified position + * + * @param rDocument -- macro or dialog + * @param eLocation -- enum value of Locations + */ + void InsertEntries(const ScriptDocument& rDocument, LibraryLocation eLocation); + + void ClearBox(); + void NotifyIDE(); + + /// Fill up the combobox + virtual void FillBox() override; + + /*! + * Handle keystrokes + * + * @param rKEvt represents key event + * @return a bool value: true if was handled, and false if there was nothing handled + */ + virtual bool HandleKeyInput(const KeyEvent& rKEvt) override; + + DECL_LINK(FocusInHdl, weld::Widget&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + + OUString maCurrentText; + bool mbIgnoreSelect; + bool mbFillBox; ///< If true, when FillBox() is called +}; + +/*! + * @brief Manage stats of Language ComboBox + * + * @see LanguageBox Class + */ +class LanguageBoxControl : public SfxToolBoxControl +{ +public: + /*! Macro for registering two methods + * + * @code + * static SfxToolBoxControl* CreateImpl(sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx) + * static void RegisterControl(sal_uInt16 nSlotId = 0, SfxModule* pMod=nullptr) + * @endcode + * @see Macro SFX_IMPL_TOOLBOX_CONTROL + */ + SFX_DECL_TOOLBOX_CONTROL(); + + /*! + * @param nSlotId -- the slot as internal operation number + * @param nId -- this item's unique id in ToolBox + * @param rTbx -- the ToolBox which contains this ComboBox + */ + LanguageBoxControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx); + + /*! + * Triggered if state was changed + * + * @param nSlotID -- the slot as internal operation number (not used in this place) + * @param eState -- enum value which contains ComboBox state + * @param pState -- + */ + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState) override; + /*! + * Create ComboBox of Language + * + * @param pParent + * @return LanguageBox ComboBox + */ + virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override; +}; + +/*! + * @brief Class language ComboBox + * + * @see LanguageBoxControl Class + */ +class LanguageBox : public DocListenerBox +{ +public: + /*! + * @param pParent + */ + LanguageBox(vcl::Window* pParent); + virtual ~LanguageBox() override; + virtual void dispose() override; + + /*! + * Update selection in ComboBox of macro and dialog Library + * + * @param pItem -- string that was selected + */ + void Update(const SfxStringItem* pItem); + +protected: + /// Called for setting language when user selects a language in ComboBox + virtual void Select() override; + + /*! + * Handle keystrokes + * + * @param rKEvt represents key event + * @return a bool value: true if was handled, and false if there was nothing handled + */ + virtual bool HandleKeyInput(const KeyEvent& rKEvt) override; + +private: + /// Delete all languages from ComboBox + void ClearBox(); + /// Switch interface of dialog to selected language + void SetLanguage(); + + /// Fill up the language combobox + virtual void FillBox() override; + + OUString msNotLocalizedStr; + OUString msDefaultLanguageStr; + OUString msCurrentText; + + bool mbIgnoreSelect; ///< do not use in this class +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/ObjectCatalog.hxx b/basctl/source/inc/ObjectCatalog.hxx new file mode 100644 index 0000000000..a5b63eef42 --- /dev/null +++ b/basctl/source/inc/ObjectCatalog.hxx @@ -0,0 +1,60 @@ +/* -*- 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 . + */ + +#pragma once + +#include "bastype2.hxx" +#include "bastypes.hxx" + +#include <vcl/weld.hxx> + +namespace basctl +{ +/*! + * @brief A docking window that contains a tree of the currently loaded macros + * + * The class creates Object Catalog window with the currently loaded macros + * in a tree structure which allows user to quickly select the necessary + * macro in BasicIDE. + */ +class ObjectCatalog : public DockingWindow +{ +public: + explicit ObjectCatalog(vcl::Window* pParent); + virtual ~ObjectCatalog() override; + virtual void dispose() override; + + /// Update the entries of Object Catalog Treelist + void UpdateEntries() { m_xTree->UpdateEntries(); } + void SetCurrentEntry(BaseWindow* pCurWin); + +private: + std::unique_ptr<weld::Label> m_xTitle; ///< Title of the Object Catalog window + std::unique_ptr<SbTreeListBox> m_xTree; ///< The Treelist of the objects in window + + /*! + * Function for resize by DockingWindow. + * It is called by DockingWindow when IsFloatingMode() changes. + */ + virtual void ToggleFloatingMode() override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/accessibledialogcontrolshape.hxx b/basctl/source/inc/accessibledialogcontrolshape.hxx new file mode 100644 index 0000000000..3af6e3da90 --- /dev/null +++ b/basctl/source/inc/accessibledialogcontrolshape.hxx @@ -0,0 +1,120 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/accessiblecomponenthelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <vcl/vclptr.hxx> + +namespace vcl { class Window; } + +namespace basctl +{ + +class DlgEdObj; +class DialogWindow; + + + +class AccessibleDialogControlShape final : public cppu::ImplInheritanceHelper< + comphelper::OAccessibleExtendedComponentHelper, + css::accessibility::XAccessible, + css::lang::XServiceInfo, + css::beans::XPropertyChangeListener> +{ + friend class AccessibleDialogWindow; + +private: + VclPtr<DialogWindow> m_pDialogWindow; + DlgEdObj* m_pDlgEdObj; + bool m_bFocused; + bool m_bSelected; + + css::awt::Rectangle m_aBounds; + css::uno::Reference< css::beans::XPropertySet > m_xControlModel; + + bool IsFocused() const; + bool IsSelected() const; + + void SetFocused (bool bFocused); + void SetSelected (bool bSelected); + + css::awt::Rectangle GetBounds() const; + void SetBounds( const css::awt::Rectangle& aBounds ); + + vcl::Window* GetWindow() const; + + OUString GetModelStringProperty( OUString const & pPropertyName ); + + void FillAccessibleStateSet( sal_Int64& rStateSet ); + + // OCommonAccessibleComponent + virtual css::awt::Rectangle implGetBounds() override; + + // XComponent + virtual void SAL_CALL disposing() override; + +public: + AccessibleDialogControlShape (DialogWindow*, DlgEdObj*); + virtual ~AccessibleDialogControlShape() override; + + // XEventListener + virtual void SAL_CALL disposing( const css::lang::EventObject& rSource ) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange( const css::beans::PropertyChangeEvent& rEvent ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XAccessible + virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override; + + // XAccessibleContext + virtual sal_Int64 SAL_CALL getAccessibleChildCount( ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override; + virtual sal_Int64 SAL_CALL getAccessibleIndexInParent( ) override; + virtual sal_Int16 SAL_CALL getAccessibleRole( ) override; + virtual OUString SAL_CALL getAccessibleDescription( ) override; + virtual OUString SAL_CALL getAccessibleName( ) override; + virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override; + virtual sal_Int64 SAL_CALL getAccessibleStateSet( ) override; + virtual css::lang::Locale SAL_CALL getLocale( ) override; + + // XAccessibleComponent + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override; + virtual void SAL_CALL grabFocus( ) override; + virtual sal_Int32 SAL_CALL getForeground( ) override; + virtual sal_Int32 SAL_CALL getBackground( ) override; + + // XAccessibleExtendedComponent + virtual css::uno::Reference< css::awt::XFont > SAL_CALL getFont( ) override; + virtual OUString SAL_CALL getTitledBorderText( ) override; + virtual OUString SAL_CALL getToolTipText( ) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/accessibledialogwindow.hxx b/basctl/source/inc/accessibledialogwindow.hxx new file mode 100644 index 0000000000..0332b98a41 --- /dev/null +++ b/basctl/source/inc/accessibledialogwindow.hxx @@ -0,0 +1,143 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <comphelper/accessiblecomponenthelper.hxx> +#include <cppuhelper/implbase.hxx> +#include <svl/lstner.hxx> +#include <tools/link.hxx> +#include <vcl/vclptr.hxx> + +class VclSimpleEvent; +class VclWindowEvent; + +namespace basctl +{ + +class DialogWindow; +class DlgEditor; +class DlgEdModel; +class DlgEdObj; +class AccessibleDialogControlShape; + + +class AccessibleDialogWindow final : public cppu::ImplInheritanceHelper< + comphelper::OAccessibleExtendedComponentHelper, + css::accessibility::XAccessible, + css::accessibility::XAccessibleSelection, + css::lang::XServiceInfo>, + public SfxListener +{ +private: + + class ChildDescriptor + { + public: + DlgEdObj* pDlgEdObj; + rtl::Reference< AccessibleDialogControlShape > mxAccessible; + + ChildDescriptor( DlgEdObj* _pDlgEdObj ); + + bool operator==( const ChildDescriptor& rDesc ); + bool operator<( const ChildDescriptor& rDesc ) const; + }; + + typedef std::vector< ChildDescriptor > AccessibleChildren; + + AccessibleChildren m_aAccessibleChildren; + VclPtr<basctl::DialogWindow> m_pDialogWindow; + DlgEdModel* m_pDlgEdModel; + + void UpdateFocused(); + void UpdateSelected(); + void UpdateBounds(); + + bool IsChildVisible( const ChildDescriptor& rDesc ); + + void InsertChild( const ChildDescriptor& rDesc ); + void RemoveChild( const ChildDescriptor& rDesc ); + void UpdateChild( const ChildDescriptor& rDesc ); + void UpdateChildren(); + void SortChildren(); + + DECL_LINK( WindowEventListener, VclWindowEvent&, void ); + + void ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ); + void FillAccessibleStateSet( sal_Int64& rStateSet ); + + // OCommonAccessibleComponent + virtual css::awt::Rectangle implGetBounds( ) override; + + // XComponent + virtual void SAL_CALL disposing() override; + +public: + AccessibleDialogWindow (basctl::DialogWindow*); + virtual ~AccessibleDialogWindow() override; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XAccessible + virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override; + + // XAccessibleContext + virtual sal_Int64 SAL_CALL getAccessibleChildCount( ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 i ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override; + virtual sal_Int64 SAL_CALL getAccessibleIndexInParent( ) override; + virtual sal_Int16 SAL_CALL getAccessibleRole( ) override; + virtual OUString SAL_CALL getAccessibleDescription( ) override; + virtual OUString SAL_CALL getAccessibleName( ) override; + virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override; + virtual sal_Int64 SAL_CALL getAccessibleStateSet( ) override; + virtual css::lang::Locale SAL_CALL getLocale( ) override; + + // XAccessibleComponent + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override; + virtual void SAL_CALL grabFocus( ) override; + virtual sal_Int32 SAL_CALL getForeground( ) override; + virtual sal_Int32 SAL_CALL getBackground( ) override; + + // XAccessibleExtendedComponent + virtual css::uno::Reference< css::awt::XFont > SAL_CALL getFont( ) override; + virtual OUString SAL_CALL getTitledBorderText( ) override; + virtual OUString SAL_CALL getToolTipText( ) override; + + // XAccessibleSelection + virtual void SAL_CALL selectAccessibleChild( sal_Int64 nChildIndex ) override; + virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int64 nChildIndex ) override; + virtual void SAL_CALL clearAccessibleSelection() override; + virtual void SAL_CALL selectAllAccessibleChildren( ) override; + virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) override; + virtual void SAL_CALL deselectAccessibleChild( sal_Int64 nChildIndex ) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/baside3.hxx b/basctl/source/inc/baside3.hxx new file mode 100644 index 0000000000..14fc68f079 --- /dev/null +++ b/basctl/source/inc/baside3.hxx @@ -0,0 +1,154 @@ +/* -*- 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 . + */ + +#pragma once + +#include "dlged.hxx" +#include "layout.hxx" +#include "bastypes.hxx" +#include "propbrw.hxx" +#include <svl/undo.hxx> +#include <memory> + +class Printer; +class StarBASIC; +class SfxItemSet; +class SfxUndoManager; +class SdrUndoAction; + +namespace basctl +{ + +class DlgEditor; +class DlgEdModel; +class DlgEdPage; +class DlgEdView; + +class DialogWindowLayout; +class ObjectCatalog; + +bool implImportDialog(weld::Window* pWin, const ScriptDocument& rDocument, const OUString& rLibName); + +class DialogWindow: public BaseWindow +{ +private: + DialogWindowLayout& m_rLayout; + std::unique_ptr<DlgEditor> m_pEditor; + std::unique_ptr<SfxUndoManager> m_pUndoMgr; // never nullptr + sal_uInt16 m_nControlSlotId; + +protected: + virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; + virtual void Resize() override; + virtual void dispose() override; + + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; + virtual void MouseMove( const MouseEvent& rMEvt ) override; + virtual void KeyInput( const KeyEvent& rKEvt ) override; + virtual void Command( const CommandEvent& rCEvt ) override; + virtual void LoseFocus() override; + + static void NotifyUndoActionHdl( std::unique_ptr<SdrUndoAction> ); + virtual void DoInit() override; + virtual void DoScroll( Scrollable* pCurScrollBar ) override; + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + void InitSettings(); + +public: + DialogWindow (DialogWindowLayout* pParent, ScriptDocument const& rDocument, const OUString& aLibName, const OUString& aName, css::uno::Reference<css::container::XNameContainer> const& xDialogModel); + + virtual void ExecuteCommand( SfxRequest& rReq ) override; + virtual void GetState( SfxItemSet& ) override; + DlgEditor& GetEditor() const { return *m_pEditor; } + css::uno::Reference< css::container::XNameContainer > const & GetDialog() const; + DlgEdModel& GetModel() const; + DlgEdPage& GetPage() const; + DlgEdView& GetView() const; + bool RenameDialog( const OUString& rNewName ); + void DisableBrowser(); + void UpdateBrowser(); + void SaveDialog(); + void ImportDialog(); + + virtual OUString GetTitle() override; + virtual EntryDescriptor CreateEntryDescriptor() override; + virtual void SetReadOnly (bool bReadOnly) override; + virtual bool IsReadOnly() override; + + virtual void StoreData() override; + virtual bool IsModified() override; + bool IsPasteAllowed(); + + virtual SfxUndoManager* GetUndoManager() override; + // return number of pages to be printed + virtual sal_Int32 countPages( Printer* pPrinter ) override; + // print page + virtual void printPage (sal_Int32 nPage, Printer*) override; + + virtual void Activating () override; + virtual void Deactivating () override; + + virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override; + + virtual OUString GetHid () const override; + virtual ItemType GetType () const override; +}; + + +// DialogWindowLayout + +class DialogWindowLayout : public Layout +{ +public: + DialogWindowLayout (vcl::Window* pParent, ObjectCatalog&); + virtual ~DialogWindowLayout() override; + virtual void dispose() override; +public: + void ShowPropertyBrowser (); + void UpdatePropertyBrowser (); + void DisablePropertyBrowser (); +public: + // Layout: + virtual void Activating (BaseWindow&) override; + virtual void Deactivating () override; + virtual void ExecuteGlobal (SfxRequest&) override; + virtual void GetState (SfxItemSet&, unsigned nWhich) override; + virtual void UpdateDebug (bool) override {}; +protected: + // Layout: + virtual void OnFirstSize (tools::Long nWidth, tools::Long nHeight) override; + +private: + // dockable windows: + // object catalog (owned by Shell) + ObjectCatalog& rObjectCatalog; + // property browser (created by this, deleted by toolkit) + VclPtr<PropBrw> pPropertyBrowser; + +private: + void AddPropertyBrowser (); +private: + friend class DialogWindow; +}; + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/basidectrlr.hxx b/basctl/source/inc/basidectrlr.hxx new file mode 100644 index 0000000000..a32e1ffd94 --- /dev/null +++ b/basctl/source/inc/basidectrlr.hxx @@ -0,0 +1,68 @@ +/* -*- 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 . + */ + +#pragma once + +#include <comphelper/broadcasthelper.hxx> +#include <comphelper/propertycontainer.hxx> +#include <comphelper/proparrhlp.hxx> +#include <sfx2/sfxbasecontroller.hxx> + +namespace basctl +{ + +class Shell; + +class Controller : + public comphelper::OMutexAndBroadcastHelper, + public comphelper::OPropertyContainer, + public comphelper::OPropertyArrayUsageHelper<Controller>, + public SfxBaseController +{ +private: + // properties + sal_Int32 m_nIconId; + +public: + Controller (Shell* pViewShell); + virtual ~Controller() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider ( ::SfxBaseController ) + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override; + +protected: + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/basidesh.hxx b/basctl/source/inc/basidesh.hxx new file mode 100644 index 0000000000..f907abe6f2 --- /dev/null +++ b/basctl/source/inc/basidesh.hxx @@ -0,0 +1,231 @@ +/* -*- 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 . + */ +#pragma once + +#include "doceventnotifier.hxx" +#include "sbxitem.hxx" +#include "ObjectCatalog.hxx" + +#include <com/sun/star/container/XContainerListener.hpp> +#include <sfx2/viewsh.hxx> +#include <svx/ifaceids.hxx> +#include <svl/srchitem.hxx> +#include <svtools/scrolladaptor.hxx> +#include <map> +#include <memory> +#include <string_view> + +class SfxViewFactory; +class SdrView; +class TabBar; +class SbxObject; +class SbModule; +class StarBASIC; + +namespace basctl +{ + +// Used to control zoom level +constexpr sal_uInt16 MIN_ZOOM_LEVEL = 50; +constexpr sal_uInt16 DEFAULT_ZOOM_LEVEL = 100; +constexpr sal_uInt16 MAX_ZOOM_LEVEL = 400; + +class Layout; +class ModulWindow; +class ModulWindowLayout; +class DialogWindow; +class DialogWindowLayout; +class TabBar; +class BaseWindow; +class LocalizationMgr; + +class Shell : + public SfxViewShell, + public DocumentEventListener +{ +public: + typedef std::map<sal_uInt16, VclPtr<BaseWindow> > WindowTable; + +private: + friend class JavaDebuggingListenerImpl; + friend class LocalizationMgr; + friend bool implImportDialog(weld::Window* pWin, const ScriptDocument& rDocument, const OUString& rLibName); // defined in baside3.cxx + + WindowTable aWindowTable; + sal_uInt16 nCurKey; + VclPtr<BaseWindow> pCurWin; + ScriptDocument m_aCurDocument; + OUString m_aCurLibName; + std::shared_ptr<LocalizationMgr> m_pCurLocalizationMgr; + + // Current value of the zoom slider + sal_uInt16 m_nCurrentZoomSliderValue; + VclPtr<ScrollAdaptor> aHScrollBar; + VclPtr<ScrollAdaptor> aVScrollBar; + VclPtr<TabBar> pTabBar; // basctl::TabBar + bool bCreatingWindow; + + // layout windows + VclPtr<ModulWindowLayout> pModulLayout; + VclPtr<DialogWindowLayout> pDialogLayout; + VclPtr<Layout> pLayout; // the active layout window + // common object catalog window + VclPtr<ObjectCatalog> aObjectCatalog; + + bool m_bAppBasicModified; + bool mbJustOpened = false; + + DocumentEventNotifier m_aNotifier; + + friend class ContainerListenerImpl; + css::uno::Reference< css::container::XContainerListener > m_xLibListener; + std::unique_ptr<SvxSearchItem> mpSearchItem; + + void Init(); + void InitTabBar(); + void InitScrollBars(); + void InitZoomLevel(); + void CheckWindows(); + void RemoveWindows( const ScriptDocument& rDocument, std::u16string_view rLibName ); + void UpdateWindows(); + static void InvalidateBasicIDESlots(); + void StoreAllWindowData( bool bPersistent = true ); + void SetMDITitle(); + void SetCurLib( const ScriptDocument& rDocument, const OUString& aLibName, bool bUpdateWindows = true , bool bCheck = true ); + void SetCurLibForLocalization( const ScriptDocument& rDocument, const OUString& aLibName ); + + DECL_LINK( TabBarHdl, ::TabBar*, void ); + + static unsigned nShellCount; + +private: + void AdjustPosSizePixel( const Point &rPos, const Size &rSize ); + virtual void OuterResizePixel( const Point &rPos, const Size &rSize ) override; + sal_uInt16 InsertWindowInTable (BaseWindow* pNewWin); + virtual bool PrepareClose( bool bUI = true ) override; + + void SetCurWindow (BaseWindow* pNewWin, bool bUpdateTabBar = false, bool bRememberAsCurrent = true); + void ManageToolbars(); + + VclPtr<ModulWindow> CreateBasWin( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName ); + VclPtr<DialogWindow> CreateDlgWin( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rDlgName ); + + VclPtr<ModulWindow> ShowActiveModuleWindow( StarBASIC const * pBasic ); + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + virtual void Activate(bool bMDI) override; + virtual void Deactivate(bool bMDI) override; + + virtual void Move() override; + virtual void ShowCursor( bool bOn = true ) override; + + // DocumentEventListener + virtual void onDocumentCreated( const ScriptDocument& _rDocument ) override; + virtual void onDocumentOpened( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSave( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveDone( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveAs( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveAsDone( const ScriptDocument& _rDocument ) override; + virtual void onDocumentClosed( const ScriptDocument& _rDocument ) override; + virtual void onDocumentTitleChanged( const ScriptDocument& _rDocument ) override; + virtual void onDocumentModeChanged( const ScriptDocument& _rDocument ) override; + +public: + SFX_DECL_INTERFACE( SVX_INTERFACE_BASIDE_VIEWSH ) + SFX_DECL_VIEWFACTORY(Shell); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + Shell(SfxViewFrame& rFrame, SfxViewShell *pOldSh); + virtual ~Shell() override; + + BaseWindow* GetCurWindow() const { return pCurWin; } + OUString const& GetCurLibName() const { return m_aCurLibName; } + const std::shared_ptr<LocalizationMgr>& GetCurLocalizationMgr() const { return m_pCurLocalizationMgr; } + + TabBar& GetTabBar() { return *pTabBar; } + WindowTable& GetWindowTable() { return aWindowTable; } + sal_uInt16 GetWindowId (BaseWindow const* pWin) const; + + SdrView* GetCurDlgView() const; + + SfxUndoManager* GetUndoManager() override; + + void SetGlobalEditorZoomLevel(sal_uInt16 nNewZoomLevel); + sal_uInt16 GetCurrentZoomSliderValue() { return m_nCurrentZoomSliderValue; } + static sal_uInt16 GetMinZoom() { return MIN_ZOOM_LEVEL; } + static sal_uInt16 GetMaxZoom() { return MAX_ZOOM_LEVEL; } + + virtual css::uno::Reference< css::view::XRenderable > GetRenderable() override; + + // virtual sal_uInt16 Print( SfxProgress &rProgress, sal_Bool bIsAPI, PrintDialog *pPrintDialog = 0 ); + virtual SfxPrinter* GetPrinter( bool bCreate = false ) override; + virtual sal_uInt16 SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL ) override; + virtual OUString GetSelectionText( bool bCompleteWords = false, bool bOnlyASample = false ) override; + virtual bool HasSelection( bool bText = true ) const override; + + void GetState( SfxItemSet& ); + void ExecuteGlobal( SfxRequest& rReq ); + void ExecuteSearch( SfxRequest& rReq ); + void ExecuteCurrent( SfxRequest& rReq ); + void ExecuteBasic( SfxRequest& rReq ); + void ExecuteDialog( SfxRequest& rReq ); + + virtual bool HasUIFeature(SfxShellFeature nFeature) const override; + + bool CallBasicErrorHdl( StarBASIC const * pBasic ); + BasicDebugFlags CallBasicBreakHdl( StarBASIC const * pBasic ); + + VclPtr<BaseWindow> FindWindow( const ScriptDocument& rDocument, std::u16string_view rLibName, std::u16string_view rName, ItemType nType, bool bFindSuspended = false ); + VclPtr<DialogWindow> FindDlgWin( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rName, bool bCreateIfNotExist = false, bool bFindSuspended = false ); + VclPtr<ModulWindow> FindBasWin( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName, bool bCreateIfNotExist = false, bool bFindSuspended = false ); + VclPtr<BaseWindow> FindApplicationWindow(); + bool NextPage( bool bPrev ); + + bool IsAppBasicModified () const { return m_bAppBasicModified; } + void SetAppBasicModified (bool bModified) { m_bAppBasicModified = bModified; } + + // For Dialog Drag&Drop in Dialog Organizer: + // (defined in moduldlg.cxx) + static void CopyDialogResources( + css::uno::Reference< css::io::XInputStreamProvider >& io_xISP, + const ScriptDocument& rSourceDoc, const OUString& rSourceLibName, const ScriptDocument& rDestDoc, + const OUString& rDestLibName, std::u16string_view rDlgName ); + + static void InvalidateControlSlots(); + + virtual css::uno::Reference< css::frame::XModel > + GetCurrentDocument() const override; + + void UpdateObjectCatalog () { aObjectCatalog->UpdateEntries(); } + + void RemoveWindow (BaseWindow* pWindow, bool bDestroy, bool bAllowChangeCurWindow = true); +}; + +} // namespace basctl + +// This typedef helps baside.sdi, +// because I don't know how to use nested names in it. +typedef basctl::Shell basctl_Shell; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/basobj.hxx b/basctl/source/inc/basobj.hxx new file mode 100644 index 0000000000..70c603d454 --- /dev/null +++ b/basctl/source/inc/basobj.hxx @@ -0,0 +1,105 @@ +/* -*- 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 . + */ +#pragma once + +#include "scriptdocument.hxx" +#include <tools/long.hxx> + +class SbMethod; +class SbModule; +class SbxVariable; +class StarBASIC; +class SfxUInt16Item; +class SfxBindings; +class SfxDispatcher; +namespace weld { class Widget; class Window; } + +namespace basctl +{ + void Organize(weld::Window* pParent, const css::uno::Reference<css::frame::XFrame>& xDocFrame, sal_Int16 tabId); + + // help methods for the general use: + SbMethod* CreateMacro( SbModule* pModule, const OUString& rMacroName ); + void RunMethod( SbMethod const * pMethod ); + + StarBASIC* FindBasic( const SbxVariable* pVar ); + void StopBasic(); + tools::Long HandleBasicError( StarBASIC const * pBasic ); + void BasicStopped( bool* pbAppWindowDisabled = nullptr, bool* pbDispatcherLocked = nullptr, sal_uInt16* pnWaitCount = nullptr, + SfxUInt16Item** ppSWActionCount = nullptr, SfxUInt16Item** ppSWLockViewCount = nullptr ); + + bool IsValidSbxName( std::u16string_view rName ); + + BasicManager* FindBasicManager( StarBASIC const * pLib ); + + SfxBindings* GetBindingsPtr(); + + SfxDispatcher* GetDispatcher (); + + void InvalidateDebuggerSlots(); + + // libraries + + css::uno::Sequence< OUString > GetMergedLibraryNames( + const css::uno::Reference< css::script::XLibraryContainer >& xModLibContainer, + const css::uno::Reference< css::script::XLibraryContainer >& xDlgLibContainer ); + + /** renames a module + + Will show an error message when renaming fails because the new name is already used. + */ + bool RenameModule( + weld::Widget* pErrorParent, const ScriptDocument& rDocument, + const OUString& rLibName, const OUString& rOldName, const OUString& rNewName ); + + // new methods for macros + + OUString ChooseMacro(weld::Window* pParent, + const css::uno::Reference< css::frame::XModel >& rxLimitToDocument, const css::uno::Reference< css::frame::XFrame >& xDocFrame, + bool bChooseOnly ); + inline OUString ChooseMacro(weld::Window* pParent, const css::uno::Reference<css::frame::XModel>& rLimitToDocument) + { return ChooseMacro(pParent, rLimitToDocument, css::uno::Reference< css::frame::XFrame >(), false/*bChooseOnly*/); } + + /// @throws css::container::NoSuchElementException + /// @throws css::uno::RuntimeException + css::uno::Sequence< OUString > GetMethodNames( + const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName ); + + bool HasMethod( + const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName, const OUString& rMethName ); + + // new methods for dialogs + + /** renames a dialog + + Will show an error message when renaming fails because the new name is already used. + + @throws css::container::ElementExistException + @throws css::container::NoSuchElementException + @throws css::uno::RuntimeException + */ + bool RenameDialog(weld::Widget* pErrorParent, const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rOldName, const OUString& rNewName); + + bool RemoveDialog( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rDlgName ); + + void MarkDocumentModified( const ScriptDocument& rDocument ); + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/bastype2.hxx b/basctl/source/inc/bastype2.hxx new file mode 100644 index 0000000000..0161797f16 --- /dev/null +++ b/basctl/source/inc/bastype2.hxx @@ -0,0 +1,278 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <memory> + +#include "doceventnotifier.hxx" + +#include <vcl/weld.hxx> +#include "sbxitem.hxx" +#include <o3tl/typed_flags_set.hxx> + +class SbModule; +class SbxVariable; + +enum class BrowseMode +{ + Modules = 0x01, + Subs = 0x02, + Dialogs = 0x04, + All = Modules | Subs | Dialogs, +}; +namespace o3tl { + template<> struct typed_flags<BrowseMode> : is_typed_flags<BrowseMode, 0x7> {}; +} + +namespace basctl +{ +using namespace ::com::sun::star::uno; + +enum EntryType +{ + OBJ_TYPE_UNKNOWN, + OBJ_TYPE_DOCUMENT, + OBJ_TYPE_LIBRARY, + OBJ_TYPE_MODULE, + OBJ_TYPE_DIALOG, + OBJ_TYPE_METHOD, + OBJ_TYPE_DOCUMENT_OBJECTS, + OBJ_TYPE_USERFORMS, + OBJ_TYPE_NORMAL_MODULES, + OBJ_TYPE_CLASS_MODULES +}; + +class Entry +{ +private: + EntryType m_eType; + +public: + explicit Entry(EntryType eType) + : m_eType(eType) + { + } + + virtual ~Entry(); + + Entry(Entry const &) = default; + Entry(Entry &&) = default; + Entry & operator =(Entry const &) = default; + Entry & operator =(Entry &&) = default; + + EntryType GetType () const { return m_eType; } +}; + +class DocumentEntry : public Entry +{ +private: + ScriptDocument m_aDocument; + LibraryLocation m_eLocation; + +public: + DocumentEntry ( + ScriptDocument aDocument, + LibraryLocation eLocation, + EntryType eType = OBJ_TYPE_DOCUMENT + ); + virtual ~DocumentEntry () override; + + ScriptDocument const& GetDocument() const { return m_aDocument; } + LibraryLocation GetLocation() const { return m_eLocation; } +}; + +class LibEntry : public DocumentEntry +{ +private: + OUString m_aLibName; + +public: + LibEntry ( + ScriptDocument const& rDocument, + LibraryLocation eLocation, + OUString aLibName + ); + virtual ~LibEntry () override; + + OUString const& GetLibName () const { return m_aLibName; } +}; + +class EntryDescriptor +{ + ScriptDocument m_aDocument; + LibraryLocation m_eLocation; + OUString m_aLibName; + OUString m_aLibSubName; // for vba entry: Document Objects, Class Modules, Forms and Normal Modules + OUString m_aName; + OUString m_aMethodName; + EntryType m_eType; + +public: + EntryDescriptor (); + EntryDescriptor ( + ScriptDocument aDocument, + LibraryLocation eLocation, + OUString aLibName, + OUString aLibSubName, + OUString aName, + EntryType eType + ); + EntryDescriptor ( + ScriptDocument aDocument, + LibraryLocation eLocation, + OUString aLibName, + OUString aLibSubName, + OUString aName, + OUString aMethodName, + EntryType eType + ); + + ScriptDocument const& GetDocument() const { return m_aDocument; } + LibraryLocation GetLocation() const { return m_eLocation; } + + const OUString& GetLibName() const { return m_aLibName; } + const OUString& GetLibSubName() const { return m_aLibSubName; } + const OUString& GetName() const { return m_aName; } + const OUString& GetMethodName() const { return m_aMethodName; } + void SetMethodName( const OUString& aMethodName ) { m_aMethodName = aMethodName; } + + EntryType GetType() const { return m_eType; } + void SetType( EntryType eType ) { m_eType = eType; } +}; + + +/* + Classification of types and pointers in the Entries: + + OBJ_TYPE_DOCUMENT DocumentEntry + OBJ_TYPE_LIBRARY Entry + OBJ_TYPE_MODULE Entry + OBJ_TYPE_DIALOG Entry + OBJ_TYPE_METHOD Entry + +*/ + +class SbTreeListBox : public DocumentEventListener +{ +private: + std::unique_ptr<weld::TreeView> m_xControl; + std::unique_ptr<weld::TreeIter> m_xScratchIter; + weld::Window* m_pTopLevel; + bool m_bFreezeOnFirstAddRemove; + BrowseMode nMode; + DocumentEventNotifier m_aNotifier; + void SetEntryBitmaps(const weld::TreeIter& rIter, const OUString& rImage); + +protected: + DECL_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool); + DECL_LINK(OpenCurrentHdl, weld::TreeView&, bool); + void ImpCreateLibEntries(const weld::TreeIter& rShellRootEntry, const ScriptDocument& rDocument, LibraryLocation eLocation); + void ImpCreateLibSubEntries(const weld::TreeIter& rLibRootEntry, const ScriptDocument& rDocument, const OUString& rLibName); + void ImpCreateLibSubEntriesInVBAMode(const weld::TreeIter& rLibRootEntry, const ScriptDocument& rDocument, const OUString& rLibName ); + void ImpCreateLibSubSubEntriesInVBAMode(const weld::TreeIter& rLibRootEntry, const ScriptDocument& rDocument, const OUString& rLibName); + bool ImpFindEntry(weld::TreeIter& rIter, std::u16string_view rText); + + // DocumentEventListener + virtual void onDocumentCreated( const ScriptDocument& _rDocument ) override; + virtual void onDocumentOpened( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSave( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveDone( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveAs( const ScriptDocument& _rDocument ) override; + virtual void onDocumentSaveAsDone( const ScriptDocument& _rDocument ) override; + virtual void onDocumentClosed( const ScriptDocument& _rDocument ) override; + virtual void onDocumentTitleChanged( const ScriptDocument& _rDocument ) override; + virtual void onDocumentModeChanged( const ScriptDocument& _rDocument ) override; + +public: + SbTreeListBox(std::unique_ptr<weld::TreeView> xControl, weld::Window* pTopLevel); + virtual ~SbTreeListBox() override; + + void ScanEntry( const ScriptDocument& rDocument, LibraryLocation eLocation ); + void ScanAllEntries(); + void UpdateEntries(); + + bool IsEntryProtected(const weld::TreeIter* pEntry); + + void SetMode( BrowseMode nM ) { nMode = nM; } + BrowseMode GetMode() const { return nMode; } + + SbModule* FindModule(const weld::TreeIter* pEntry); + SbxVariable* FindVariable(const weld::TreeIter* pEntry); + bool FindRootEntry(const ScriptDocument& rDocument, LibraryLocation eLocation, weld::TreeIter& rIter); + bool FindEntry(std::u16string_view rText, EntryType eType, weld::TreeIter& rIter); + EntryDescriptor GetEntryDescriptor(const weld::TreeIter* pEntry); + + static ItemType ConvertType (EntryType eType); + bool IsValidEntry(const weld::TreeIter& rEntry); + void AddEntry(const OUString& rText, const OUString& rImage, + const weld::TreeIter* pParent, bool bChildrenOnDemand, + std::unique_ptr<Entry>&& rUserData, + weld::TreeIter* pRet = nullptr); + + void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xControl->connect_changed(rLink); } + std::unique_ptr<weld::TreeIter> make_iterator(const weld::TreeIter* pIter = nullptr) const { return m_xControl->make_iterator(pIter); } + void copy_iterator(const weld::TreeIter& rSource, weld::TreeIter& rDest) const { m_xControl->copy_iterator(rSource, rDest); } + bool get_selected(weld::TreeIter* pIter) const { return m_xControl->get_selected(pIter); } + void select(const weld::TreeIter& rIter) { m_xControl->select(rIter); } + void unselect(const weld::TreeIter& rIter) { m_xControl->unselect(rIter); } + void remove(const weld::TreeIter& rIter) { m_xControl->remove(rIter); } + bool get_cursor(weld::TreeIter* pIter) const { return m_xControl->get_cursor(pIter); } + void set_cursor(const weld::TreeIter& rIter) { m_xControl->set_cursor(rIter); } + OUString get_text(const weld::TreeIter& rIter) const { return m_xControl->get_text(rIter); } + void set_text(const weld::TreeIter& rIter, const OUString& rText) { m_xControl->set_text(rIter, rText); } + OUString get_id(const weld::TreeIter& rIter) const { return m_xControl->get_id(rIter); } + bool get_iter_first(weld::TreeIter& rIter) const { return m_xControl->get_iter_first(rIter); } + bool iter_next_sibling(weld::TreeIter& rIter) const { return m_xControl->iter_next_sibling(rIter); } + bool iter_children(weld::TreeIter& rIter) const { return m_xControl->iter_children(rIter); } + bool iter_parent(weld::TreeIter& rIter) const { return m_xControl->iter_parent(rIter); } + int get_iter_depth(const weld::TreeIter& rIter) const { return m_xControl->get_iter_depth(rIter); } + bool get_row_expanded(const weld::TreeIter& rIter) const { return m_xControl->get_row_expanded(rIter); } + void expand_row(const weld::TreeIter& rIter) { m_xControl->expand_row(rIter); } + void set_size_request(int nWidth, int nHeight) { m_xControl->set_size_request(nWidth, nHeight); } + float get_approximate_digit_width() const { return m_xControl->get_approximate_digit_width(); } + int get_height_rows(int nRows) const { return m_xControl->get_height_rows(nRows); } + int get_iter_index_in_parent(const weld::TreeIter& rIter) const { return m_xControl->get_iter_index_in_parent(rIter); } + void connect_editing(const Link<const weld::TreeIter&, bool>& rStartLink, + const Link<const std::pair<const weld::TreeIter&, OUString>&, bool>& rEndLink) + { + m_xControl->connect_editing(rStartLink, rEndLink); + } + + void make_sorted() { m_xControl->make_sorted(); }; + + void RemoveEntry(const weld::TreeIter& rIter); + void RemoveEntry(const ScriptDocument&); + + OUString GetRootEntryName(const ScriptDocument& rDocument, LibraryLocation eLocation) const; + static OUString GetRootEntryBitmaps(const ScriptDocument& rDocument); + + void SetCurrentEntry (EntryDescriptor const &); + + weld::TreeView& get_widget() { return *m_xControl; } + +private: + LibraryType GetLibraryType() const; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/bastypes.hxx b/basctl/source/inc/bastypes.hxx new file mode 100644 index 0000000000..513fbce3a4 --- /dev/null +++ b/basctl/source/inc/bastypes.hxx @@ -0,0 +1,316 @@ +/* -*- 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 . + */ +#pragma once + +#include "scriptdocument.hxx" + +#include "sbxitem.hxx" +#include <svtools/scrolladaptor.hxx> +#include <svtools/tabbar.hxx> +#include <basic/sbdef.hxx> +#include <vcl/dockwin.hxx> +#include <vcl/weld.hxx> + +#include <string_view> +#include <unordered_map> + +class SbModule; +class SfxItemSet; +class SfxRequest; +class SvxSearchItem; +class Printer; +enum class SearchOptionFlags; +class SfxUndoManager; + +namespace weld +{ + class Widget; +} + +namespace basctl +{ + +class Layout; +class ModulWindow; +class DialogWindow; + +constexpr auto LINE_SEP_CR = 0x0D; +constexpr auto LINE_SEP = 0x0A; + +// Implementation: baside2b.cxx +sal_Int32 searchEOL( std::u16string_view rStr, sal_Int32 fromIndex ); + +// Meaning of bToBeKilled: +// While being in a reschedule-loop, I may not destroy the window. +// It must first break from the reschedule-loop to self-destroy then. +// Does unfortunately not work that way: Destroying Window with living Child! + +struct BasicStatus +{ + bool bIsRunning : 1; + bool bError : 1; + bool bIsInReschedule : 1; + BasicDebugFlags nBasicFlags; + + BasicStatus(): + bIsRunning(false), + bError(false), + bIsInReschedule(false), + nBasicFlags(BasicDebugFlags::NONE) { } +}; + + +// basctl::DockingWindow -- special docking window for the Basic IDE +// Not to be confused with ::DockingWindow from vcl. + +class DockingWindow : public ResizableDockingWindow +{ +public: + DockingWindow(vcl::Window* pParent, const OUString& rUIXMLDescription, const OUString& rID); + DockingWindow(Layout* pParent); + virtual ~DockingWindow() override; + virtual void dispose() override; + void ResizeIfDocking (Point const&, Size const&); + void ResizeIfDocking (Size const&); + Size GetDockingSize () const { return aDockingRect.GetSize(); } + void SetLayoutWindow (Layout*); +public: + void Show (bool = true); + void Hide (); + +protected: + virtual bool Docking( const Point& rPos, tools::Rectangle& rRect ) override; + virtual void EndDocking( const tools::Rectangle& rRect, bool bFloatMode ) override; + virtual void ToggleFloatingMode() override; + virtual bool PrepareToggleFloatingMode() override; + virtual void StartDocking() override; + +protected: + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + +private: + // the position and the size of the floating window + tools::Rectangle aFloatingRect; + // the position and the size of the docking window + tools::Rectangle aDockingRect; + // the parent layout window (only when docking) + VclPtr<Layout> pLayout; + // > 0: shown, <= 0: hidden, ++ by Show() and -- by Hide() + int nShowCount; + + static WinBits const StyleBits; + +private: + void DockThis (); +}; + + +// basctl::TabBar +// Not to be confused with ::TabBar from svtools. + +class TabBar : public ::TabBar +{ +protected: + virtual void MouseButtonDown( const MouseEvent& rMEvt ) override; + virtual void Command( const CommandEvent& rCEvt ) override; + + virtual TabBarAllowRenamingReturnCode AllowRenaming() override; + virtual void EndRenaming() override; + +public: + TabBar (vcl::Window* pParent); + + void Sort(); +}; + +enum BasicWindowStatus +{ + BASWIN_RUNNINGBASIC = 0x01, + BASWIN_TOBEKILLED = 0x02, + BASWIN_SUSPENDED = 0x04, + BASWIN_INRESCHEDULE = 0x08 +}; + +class EntryDescriptor; + + +// BaseWindow -- the base of both ModulWindow and DialogWindow. + +class BaseWindow : public vcl::Window +{ +private: + VclPtr<ScrollAdaptor> pShellHScrollBar; + VclPtr<ScrollAdaptor> pShellVScrollBar; + + DECL_LINK( VertScrollHdl, weld::Scrollbar&, void ); + DECL_LINK( HorzScrollHdl, weld::Scrollbar&, void ); + int nStatus; + + ScriptDocument m_aDocument; + OUString m_aLibName; + OUString m_aName; + + friend class ModulWindow; + friend class DialogWindow; + +protected: + virtual void DoScroll(Scrollable* pCurScrollBar); + +public: + BaseWindow( vcl::Window* pParent, ScriptDocument aDocument, OUString aLibName, OUString aName ); + virtual ~BaseWindow() override; + virtual void dispose() override; + + void Init(); + virtual void DoInit(); + virtual void Activating () = 0; + virtual void Deactivating () = 0; + void GrabScrollBars(ScrollAdaptor* pHScroll, ScrollAdaptor* pVScroll); + + ScrollAdaptor* GetHScrollBar() const { return pShellHScrollBar.get(); } + ScrollAdaptor* GetVScrollBar() const { return pShellVScrollBar.get(); } + void ShowShellScrollBars(bool bVisible = true); + + virtual void ExecuteCommand (SfxRequest&); + virtual void ExecuteGlobal (SfxRequest&); + virtual void GetState (SfxItemSet&) = 0; + virtual bool EventNotify( NotifyEvent& rNEvt ) override; + + virtual void StoreData(); + virtual void UpdateData(); + + // return number of pages to be printed + virtual sal_Int32 countPages( Printer* pPrinter ) = 0; + // print page + virtual void printPage( sal_Int32 nPage, Printer* pPrinter ) = 0; + + virtual OUString GetTitle(); + OUString CreateQualifiedName(); + virtual EntryDescriptor CreateEntryDescriptor() = 0; + + virtual bool IsModified(); + + virtual bool AllowUndo(); + + virtual void SetReadOnly (bool bReadOnly); + virtual bool IsReadOnly(); + void ShowReadOnlyInfoBar(); + + int GetStatus() const { return nStatus; } + void SetStatus(int n) { nStatus = n; } + void AddStatus(int n) { nStatus |= n; } + void ClearStatus(int n) { nStatus &= ~n; } + + virtual SfxUndoManager* GetUndoManager (); + + virtual SearchOptionFlags GetSearchOptions(); + virtual sal_uInt16 StartSearchAndReplace (SvxSearchItem const&, bool bFromStart = false); + + virtual void BasicStarted(); + virtual void BasicStopped(); + + bool IsSuspended() const { return nStatus & BASWIN_SUSPENDED; } + + const ScriptDocument& + GetDocument() const { return m_aDocument; } + bool IsDocument( const ScriptDocument& rDocument ) const { return rDocument == m_aDocument; } + const OUString& GetLibName() const { return m_aLibName; } + + const OUString& GetName() const { return m_aName; } + void SetName( const OUString& aName ) { m_aName = aName; } + + virtual void OnNewDocument (); + virtual OUString GetHid () const = 0; + virtual ItemType GetType () const = 0; + void InsertLibInfo () const; + bool Is (ScriptDocument const&, std::u16string_view, std::u16string_view, ItemType, bool bFindSuspended); + virtual bool HasActiveEditor () const; +}; + +class LibInfo +{ +public: + class Item; +public: + LibInfo (); + ~LibInfo (); +public: + void InsertInfo (ScriptDocument const&, OUString const& rLibName, OUString const& rCurrentName, ItemType eCurrentType); + void RemoveInfoFor (ScriptDocument const&); + Item const* GetInfo (ScriptDocument const&, OUString const& rLibName); + +private: + class Key + { + private: + ScriptDocument m_aDocument; + OUString m_aLibName; + + public: + Key (ScriptDocument , OUString aLibName); + public: + bool operator == (Key const&) const; + struct Hash + { + size_t operator () (Key const&) const; + }; + public: + const ScriptDocument& GetDocument() const { return m_aDocument; } + }; +public: + class Item + { + private: + OUString m_aCurrentName; + ItemType m_eCurrentType; + + public: + Item (OUString aCurrentName, ItemType eCurrentType); + const OUString& GetCurrentName() const { return m_aCurrentName; } + ItemType GetCurrentType() const { return m_eCurrentType; } + }; +private: + typedef std::unordered_map<Key, Item, Key::Hash> Map; + Map m_aMap; +}; + +void CutLines( OUString& rStr, sal_Int32 nStartLine, sal_Int32 nLines ); +OUString CreateMgrAndLibStr( std::u16string_view rMgrName, std::u16string_view rLibName ); +sal_uInt32 CalcLineCount( SvStream& rStream ); + +bool QueryReplaceMacro( std::u16string_view rName, weld::Widget* pParent ); +bool QueryDelMacro( std::u16string_view rName, weld::Widget* pParent ); +bool QueryDelDialog( std::u16string_view rName, weld::Widget* pParent ); +bool QueryDelModule( std::u16string_view rName, weld::Widget* pParent ); +bool QueryDelLib( std::u16string_view rName, bool bRef, weld::Widget* pParent ); +bool QueryPassword(weld::Widget* pDialogParent, const css::uno::Reference< css::script::XLibraryContainer >& xLibContainer, const OUString& rLibName, OUString& rPassword, bool bRepeat = false, bool bNewTitle = false); + +class ModuleInfoHelper +{ + ModuleInfoHelper (const ModuleInfoHelper&) = delete; + ModuleInfoHelper& operator = (const ModuleInfoHelper&) = delete; +public: + static void getObjectName( const css::uno::Reference< css::container::XNameContainer >& rLib, const OUString& rModName, OUString& rObjName ); + static sal_Int32 getModuleType( const css::uno::Reference< css::container::XNameContainer >& rLib, const OUString& rModName ); +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlged.hxx b/basctl/source/inc/dlged.hxx new file mode 100644 index 0000000000..c50faf51b3 --- /dev/null +++ b/basctl/source/inc/dlged.hxx @@ -0,0 +1,210 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/awt/XControlContainer.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> +#include <o3tl/deleter.hxx> +#include <svl/SfxBroadcaster.hxx> +#include <svl/hint.hxx> +#include <svx/svdobjkind.hxx> +#include <tools/gen.hxx> +#include <vcl/timer.hxx> +#include <vcl/idle.hxx> +#include <vcl/vclptr.hxx> +#include <vcl/window.hxx> + +#include <memory> + +class ScrollAdaptor; +class Printer; +class KeyEvent; +class MouseEvent; +class Timer; +namespace vcl { class Window; } + +namespace basctl +{ + +class DialogWindowLayout; + +constexpr auto DLGED_PAGE_WIDTH_MIN = 1280; +constexpr auto DLGED_PAGE_HEIGHT_MIN = 1024; + +// DlgEdHint + + +class DlgEdObj; + +class DlgEdHint: public SfxHint +{ +public: + enum Kind { + UNKNOWN, + WINDOWSCROLLED, + LAYERCHANGED, + OBJORDERCHANGED, + SELECTIONCHANGED, + }; + +private: + Kind eKind; + DlgEdObj* pDlgEdObj; + +public: + DlgEdHint (Kind); + DlgEdHint (Kind, DlgEdObj* pObj); + virtual ~DlgEdHint() override; + + Kind GetKind() const { return eKind; } + DlgEdObj* GetObject() const { return pDlgEdObj; } +}; + + +// DlgEditor + + +class DlgEdModel; +class DlgEdPage; +class DlgEdView; +class DlgEdForm; +class DlgEdFactory; +class DlgEdFunc; + +class DlgEditor: public SfxBroadcaster +{ +public: + enum Mode { + INSERT, + SELECT, + TEST, + READONLY, + }; + +private: + DECL_LINK(MarkTimeout, Timer *, void); + + static void Print( Printer* pPrinter, const OUString& rTitle ); + +private: + VclPtr<ScrollAdaptor> pHScroll; + VclPtr<ScrollAdaptor> pVScroll; + std::unique_ptr<DlgEdModel> pDlgEdModel; // never nullptr + DlgEdPage* pDlgEdPage; // never nullptr + std::unique_ptr<DlgEdView> pDlgEdView; // never nullptr + rtl::Reference<DlgEdForm> pDlgEdForm; // never nullptr + css::uno::Reference< css::container::XNameContainer > m_xUnoControlDialogModel; + css::uno::Reference< css::awt::XControlContainer > m_xControlContainer; + css::uno::Sequence< css::datatransfer::DataFlavor > m_ClipboardDataFlavors; + css::uno::Sequence< css::datatransfer::DataFlavor > m_ClipboardDataFlavorsResource; + css::uno::Reference< css::util::XNumberFormatsSupplier > m_xSupplier; + std::unique_ptr<DlgEdFactory, o3tl::default_delete<DlgEdFactory>> pObjFac; // never nullptr + vcl::Window& rWindow; // DialogWindow + std::unique_ptr<DlgEdFunc> pFunc; + DialogWindowLayout& rLayout; + Mode eMode; + SdrObjKind eActObj; + bool bFirstDraw; + bool bCreateOK; + tools::Rectangle aPaintRect; + bool bDialogModelChanged; + Idle aMarkIdle; + tools::Long mnPaintGuard; + css::uno::Reference< css::frame::XModel > m_xDocument; + +public: + DlgEditor ( + vcl::Window&, DialogWindowLayout&, + css::uno::Reference<css::frame::XModel> const& xModel, + css::uno::Reference<css::container::XNameContainer> const & xDialogModel + ); + virtual ~DlgEditor() override; + + vcl::Window& GetWindow() const { return rWindow; } + + /** returns the control container associated with our window + @see GetWindow + @see SetWindow + */ + css::uno::Reference< css::awt::XControlContainer > const & + GetWindowControlContainer(); + + void SetScrollBars(ScrollAdaptor* pHScroll, ScrollAdaptor* pVScroll); + void InitScrollBars(); + ScrollAdaptor* GetHScroll() const { return pHScroll; } + ScrollAdaptor* GetVScroll() const { return pVScroll; } + void DoScroll(); + void UpdateScrollBars(); + + void SetDialog (const css::uno::Reference<css::container::XNameContainer>& xUnoControlDialogModel); + void ResetDialog (); + const css::uno::Reference< css::container::XNameContainer >& GetDialog() const + {return m_xUnoControlDialogModel;} + + css::uno::Reference< css::util::XNumberFormatsSupplier > const & GetNumberFormatsSupplier(); + + DlgEdModel& GetModel() const { return *pDlgEdModel; } + DlgEdView& GetView() const { return *pDlgEdView; } + DlgEdPage& GetPage() const { return *pDlgEdPage; } + + void ShowDialog(); + + bool UnmarkDialog(); + bool RemarkDialog(); + + void SetDialogModelChanged() { bDialogModelChanged = true; } + + bool IsModified () const; + void ClearModifyFlag(); + + void MouseButtonDown( const MouseEvent& rMEvt ); + void MouseButtonUp( const MouseEvent& rMEvt ); + void MouseMove( const MouseEvent& rMEvt ); + void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect); + bool KeyInput( const KeyEvent& rKEvt ); + + void SetMode (Mode eMode); + void SetInsertObj(SdrObjKind eObj); + void CreateDefaultObject(); + Mode GetMode() const { return eMode; } + bool IsCreateOK() const { return bCreateOK; } + + void Cut(); + void Copy(); + void Paste(); + void Delete(); + bool IsPasteAllowed(); + + void ShowProperties(); + void UpdatePropertyBrowserDelayed(); + + static void printPage( sal_Int32 nPage, Printer* pPrinter, const OUString& ); + + bool AdjustPageSize(); + + bool isInPaint() const { return mnPaintGuard > 0; } +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedclip.hxx b/basctl/source/inc/dlgedclip.hxx new file mode 100644 index 0000000000..0bb7a74161 --- /dev/null +++ b/basctl/source/inc/dlgedclip.hxx @@ -0,0 +1,53 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp> +#include <cppuhelper/implbase.hxx> + +namespace basctl +{ + + +class DlgEdTransferableImpl final : public ::cppu::WeakImplHelper< css::datatransfer::XTransferable, + css::datatransfer::clipboard::XClipboardOwner > +{ +private: + css::uno::Sequence< css::datatransfer::DataFlavor > m_SeqFlavors; + css::uno::Sequence< css::uno::Any > m_SeqData; + + static bool compareDataFlavors( const css::datatransfer::DataFlavor& lFlavor, const css::datatransfer::DataFlavor& rFlavor ); + +public: + DlgEdTransferableImpl( const css::uno::Sequence< css::datatransfer::DataFlavor >& aSeqFlavors, const css::uno::Sequence< css::uno::Any >& aSeqData ); + virtual ~DlgEdTransferableImpl() override; + + // XTransferable + virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& rFlavor ) override; + virtual css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors() override; + virtual sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& rFlavor ) override; + + // XClipboardOwner + virtual void SAL_CALL lostOwnership( const css::uno::Reference< css::datatransfer::clipboard::XClipboard >& xClipboard, const css::uno::Reference< css::datatransfer::XTransferable >& xTrans ) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgeddef.hxx b/basctl/source/inc/dlgeddef.hxx new file mode 100644 index 0000000000..c10ef16cd8 --- /dev/null +++ b/basctl/source/inc/dlgeddef.hxx @@ -0,0 +1,47 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> + +namespace basctl +{ + +// control properties +#define DLGED_PROP_BACKGROUNDCOLOR "BackgroundColor" +inline constexpr OUString DLGED_PROP_DROPDOWN = u"Dropdown"_ustr; +inline constexpr OUString DLGED_PROP_FORMATSSUPPLIER = u"FormatsSupplier"_ustr; +inline constexpr OUString DLGED_PROP_HEIGHT = u"Height"_ustr; +inline constexpr OUString DLGED_PROP_LABEL = u"Label"_ustr; +inline constexpr OUString DLGED_PROP_NAME = u"Name"_ustr; +inline constexpr OUString DLGED_PROP_ORIENTATION = u"Orientation"_ustr; +inline constexpr OUString DLGED_PROP_POSITIONX = u"PositionX"_ustr; +inline constexpr OUString DLGED_PROP_POSITIONY = u"PositionY"_ustr; +inline constexpr OUString DLGED_PROP_STEP = u"Step"_ustr; +inline constexpr OUString DLGED_PROP_TABINDEX = u"TabIndex"_ustr; +#define DLGED_PROP_TEXTCOLOR "TextColor" +#define DLGED_PROP_TEXTLINECOLOR "TextLineColor" +inline constexpr OUString DLGED_PROP_WIDTH = u"Width"_ustr; +inline constexpr OUString DLGED_PROP_DECORATION = u"Decoration"_ustr; + + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedfac.hxx b/basctl/source/inc/dlgedfac.hxx new file mode 100644 index 0000000000..5e583ada17 --- /dev/null +++ b/basctl/source/inc/dlgedfac.hxx @@ -0,0 +1,43 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/svdobj.hxx> +#include <tools/link.hxx> +#include <com/sun/star/frame/XModel.hpp> + +namespace basctl +{ +// DlgEdFactory + +class DlgEdFactory +{ + const css::uno::Reference<css::frame::XModel> mxModel; + +public: + DlgEdFactory(css::uno::Reference<css::frame::XModel> xModel); + ~DlgEdFactory() COVERITY_NOEXCEPT_FALSE; + + DECL_LINK(MakeObject, SdrObjCreatorParams, rtl::Reference<SdrObject>); +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedfunc.hxx b/basctl/source/inc/dlgedfunc.hxx new file mode 100644 index 0000000000..9e20f39015 --- /dev/null +++ b/basctl/source/inc/dlgedfunc.hxx @@ -0,0 +1,80 @@ +/* -*- 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 . + */ + +#pragma once + +#include <vcl/event.hxx> +#include <vcl/timer.hxx> +#include <tools/link.hxx> +#include <tools/gen.hxx> + +namespace basctl +{ +class DlgEditor; + +// DlgEdFunc + +class DlgEdFunc /* : public LinkHdl */ +{ +protected: + DlgEditor& rParent; + Timer aScrollTimer; + + DECL_LINK(ScrollTimeout, Timer*, void); + void ForceScroll(const Point& rPos); + +public: + explicit DlgEdFunc(DlgEditor& rParent); + virtual ~DlgEdFunc(); + + virtual void MouseButtonDown(const MouseEvent& rMEvt); + virtual bool MouseButtonUp(const MouseEvent& rMEvt); + virtual void MouseMove(const MouseEvent& rMEvt); + bool KeyInput(const KeyEvent& rKEvt); +}; + +// DlgEdFuncInsert + +class DlgEdFuncInsert : public DlgEdFunc +{ +public: + explicit DlgEdFuncInsert(DlgEditor& rParent); + virtual ~DlgEdFuncInsert() override; + + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; +}; + +// DlgEdFuncSelect + +class DlgEdFuncSelect : public DlgEdFunc +{ +public: + explicit DlgEdFuncSelect(DlgEditor& rParent); + virtual ~DlgEdFuncSelect() override; + + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedlist.hxx b/basctl/source/inc/dlgedlist.hxx new file mode 100644 index 0000000000..373f38b146 --- /dev/null +++ b/basctl/source/inc/dlgedlist.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/implbase.hxx> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> + +namespace basctl +{ +class DlgEdObj; + +// DlgEdPropListenerImpl + +typedef ::cppu::WeakImplHelper<css::beans::XPropertyChangeListener> PropertyChangeListenerHelper; + +class DlgEdPropListenerImpl : public PropertyChangeListenerHelper +{ +private: + DlgEdObj& rDlgEdObj; + +public: + explicit DlgEdPropListenerImpl(DlgEdObj&); + virtual ~DlgEdPropListenerImpl() override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + + // XPropertyChangeListener + virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override; +}; + +// DlgEdEvtContListenerImpl + +typedef ::cppu::WeakImplHelper<css::container::XContainerListener> ContainerListenerHelper; + +class DlgEdEvtContListenerImpl : public ContainerListenerHelper +{ +private: + DlgEdObj& rDlgEdObj; + +public: + explicit DlgEdEvtContListenerImpl(DlgEdObj&); + virtual ~DlgEdEvtContListenerImpl() override; + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + + // XContainerListener + virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& Event) override; + virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& Event) override; + virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& Event) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedmod.hxx b/basctl/source/inc/dlgedmod.hxx new file mode 100644 index 0000000000..24a97905b1 --- /dev/null +++ b/basctl/source/inc/dlgedmod.hxx @@ -0,0 +1,45 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/svdmodel.hxx> + +namespace basctl +{ +// DlgEdModel + +class DlgEdModel : public SdrModel +{ + friend class DlgEdPage; + +private: + DlgEdModel(const DlgEdModel&) = delete; + void operator=(const DlgEdModel& rSrcModel) = delete; + +public: + DlgEdModel(); + virtual ~DlgEdModel() override; + + virtual rtl::Reference<SdrPage> AllocPage(bool bMasterPage) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedobj.hxx b/basctl/source/inc/dlgedobj.hxx new file mode 100644 index 0000000000..a8c249adec --- /dev/null +++ b/basctl/source/inc/dlgedobj.hxx @@ -0,0 +1,195 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertyChangeListener.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <svx/svdouno.hxx> + +#include <optional> + +#include <map> + +namespace basctl +{ + +typedef std::multimap< sal_Int16, OUString > IndexToNameMap; + + +class DlgEdForm; +class DlgEditor; + + +// DlgEdObj + + +class DlgEdObj: public SdrUnoObj +{ + friend class DlgEditor; + friend class DlgEdFactory; + friend class DlgEdPropListenerImpl; + friend class DlgEdForm; + +private: + bool bIsListening; + rtl::Reference<DlgEdForm> pDlgEdForm; + css::uno::Reference< css::beans::XPropertyChangeListener> m_xPropertyChangeListener; + css::uno::Reference< css::container::XContainerListener> m_xContainerListener; + +private: + DlgEditor& GetDialogEditor (); + +protected: + DlgEdObj(SdrModel& rSdrModel); + // copy constructor + DlgEdObj(SdrModel& rSdrModel, DlgEdObj const & rSource); + DlgEdObj( + SdrModel& rSdrModel, + const OUString& rModelName, + const css::uno::Reference< css::lang::XMultiServiceFactory >& rxSFac); + + // protected destructor + virtual ~DlgEdObj() override; + + virtual void NbcMove( const Size& rSize ) override; + virtual void NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override; + virtual bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) override; + + using SfxListener::StartListening; + void StartListening(); + using SfxListener::EndListening; + void EndListening(bool bRemoveListener); + bool isListening() const { return bIsListening; } + + bool TransformSdrToControlCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ); + bool TransformSdrToFormCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ); + bool TransformControlToSdrCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ); + bool TransformFormToSdrCoordinates( + sal_Int32 nXIn, sal_Int32 nYIn, sal_Int32 nWidthIn, sal_Int32 nHeightIn, + sal_Int32& nXOut, sal_Int32& nYOut, sal_Int32& nWidthOut, sal_Int32& nHeightOut ); + +public: + void SetDlgEdForm( DlgEdForm* pForm ) { pDlgEdForm = pForm; } + DlgEdForm* GetDlgEdForm() const { return pDlgEdForm.get(); } + + virtual SdrInventor GetObjInventor() const override; + virtual SdrObjKind GetObjIdentifier() const override; + + virtual rtl::Reference<SdrObject> CloneSdrObject(SdrModel& rTargetModel) const override; // not working yet + + // FullDrag support + virtual rtl::Reference<SdrObject> getFullDragClone() const override; + + bool supportsService( OUString const & serviceName ) const; + OUString GetDefaultName() const; + OUString GetUniqueName() const; + + sal_Int32 GetStep() const; + virtual void UpdateStep(); + + void SetDefaults(); + virtual void SetRectFromProps(); + virtual void SetPropsFromRect(); + + css::uno::Reference< css::awt::XControl > GetControl() const; + + virtual void PositionAndSizeChange( const css::beans::PropertyChangeEvent& evt ); + /// @throws css::container::NoSuchElementException + /// @throws css::uno::RuntimeException + void NameChange( const css::beans::PropertyChangeEvent& evt ); + /// @throws css::uno::RuntimeException + void TabIndexChange( const css::beans::PropertyChangeEvent& evt ); + + // PropertyChangeListener + /// @throws css::uno::RuntimeException + void _propertyChange(const css::beans::PropertyChangeEvent& evt); + + // ContainerListener + /// @throws css::uno::RuntimeException + void _elementInserted(); + /// @throws css::uno::RuntimeException + void _elementReplaced(); + /// @throws css::uno::RuntimeException + void _elementRemoved(); + + virtual void SetLayer(SdrLayerID nLayer) override; + void MakeDataAware( const css::uno::Reference< css::frame::XModel >& xModel ); +}; + + +// DlgEdForm + + +class DlgEdForm: public DlgEdObj +{ + friend class DlgEditor; + friend class DlgEdFactory; + +private: + DlgEditor& rDlgEditor; + std::vector<DlgEdObj*> pChildren; + + mutable ::std::optional< css::awt::DeviceInfo > mpDeviceInfo; + +private: + explicit DlgEdForm( + SdrModel& rSdrModel, + DlgEditor&); + +protected: + virtual void NbcMove( const Size& rSize ) override; + virtual void NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact) override; + virtual bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd) override; + + // protected destructor + virtual ~DlgEdForm() override; + +public: + DlgEditor& GetDlgEditor () const { return rDlgEditor; } + + void AddChild( DlgEdObj* pDlgEdObj ); + void RemoveChild( DlgEdObj* pDlgEdObj ); + std::vector<DlgEdObj*> const& GetChildren() const { return pChildren; } + + virtual void UpdateStep() override; + + virtual void SetRectFromProps() override; + virtual void SetPropsFromRect() override; + + virtual void PositionAndSizeChange( const css::beans::PropertyChangeEvent& evt ) override; + + void UpdateTabIndices(); + void UpdateTabOrder(); + void UpdateGroups(); + void UpdateTabOrderAndGroups(); + + css::awt::DeviceInfo getDeviceInfo() const; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedpage.hxx b/basctl/source/inc/dlgedpage.hxx new file mode 100644 index 0000000000..91efc12edd --- /dev/null +++ b/basctl/source/inc/dlgedpage.hxx @@ -0,0 +1,52 @@ +/* -*- 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 . + */ + +#pragma once + +#include <svx/svdpage.hxx> + +namespace basctl +{ +// DlgEdPage + +class DlgEdModel; +class DlgEdForm; + +class DlgEdPage final : public SdrPage +{ + DlgEdPage& operator=(const DlgEdPage&) = delete; + DlgEdPage(const DlgEdPage&) = delete; + + DlgEdForm* pDlgEdForm; + +public: + explicit DlgEdPage(DlgEdModel& rModel, bool bMasterPage = false); + virtual ~DlgEdPage() override; + + virtual rtl::Reference<SdrPage> CloneSdrPage(SdrModel& rTargetModel) const override; + + void SetDlgEdForm(DlgEdForm* pForm) { pDlgEdForm = pForm; } + DlgEdForm* GetDlgEdForm() const { return pDlgEdForm; } + + virtual SdrObject* SetObjectOrdNum(size_t nOldObjNum, size_t nNewObjNum) override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/dlgedview.hxx b/basctl/source/inc/dlgedview.hxx new file mode 100644 index 0000000000..195dee8403 --- /dev/null +++ b/basctl/source/inc/dlgedview.hxx @@ -0,0 +1,59 @@ +/* -*- 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 . + */ + + +#pragma once + +#include <svx/svdview.hxx> + +namespace basctl +{ + +class DlgEditor; + + +// DlgEdView + + +class DlgEdView : public SdrView +{ +private: + DlgEditor& rDlgEditor; + +public: + + DlgEdView( + SdrModel& rSdrModel, + OutputDevice& rOut, + DlgEditor& rEditor); + + virtual ~DlgEdView() override; + + virtual void MarkListHasChanged() override; + virtual void MakeVisible( const tools::Rectangle& rRect, vcl::Window& rWin ) override; + +protected: + /// override to handle HitTest for some objects specially + using SdrView::CheckSingleSdrObjectHit; + virtual SdrObject* CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const override; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/doceventnotifier.hxx b/basctl/source/inc/doceventnotifier.hxx new file mode 100644 index 0000000000..25fd2a7a84 --- /dev/null +++ b/basctl/source/inc/doceventnotifier.hxx @@ -0,0 +1,82 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/frame/XModel.hpp> + +#include <rtl/ref.hxx> + + +namespace basctl +{ + + + class ScriptDocument; + + + class SAL_NO_VTABLE DocumentEventListener + { + public: + DocumentEventListener(const DocumentEventListener&) = delete; + const DocumentEventListener& operator=(const DocumentEventListener&) = delete; + DocumentEventListener() = default; + + virtual void onDocumentCreated( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentOpened( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentSave( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentSaveDone( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentSaveAs( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentSaveAsDone( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentClosed( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentTitleChanged( const ScriptDocument& _rDocument ) = 0; + virtual void onDocumentModeChanged( const ScriptDocument& _rDocument ) = 0; + + virtual ~DocumentEventListener(); + }; + + + /** allows registering at theGlobalEventBroadcaster for global document events + */ + class DocumentEventNotifier + { + public: + /** create a notifier instance which notifies about events of all documents in the whole application + */ + DocumentEventNotifier (DocumentEventListener&); + + /** creates a notifier instance which notifies about events at a single document + */ + DocumentEventNotifier (DocumentEventListener&, css::uno::Reference<css::frame::XModel> const& rxDocument); + + ~DocumentEventNotifier(); + + public: + void dispose(); + + private: + class Impl; + rtl::Reference<Impl> m_pImpl; + }; + + +} // namespace basctl + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/docsignature.hxx b/basctl/source/inc/docsignature.hxx new file mode 100644 index 0000000000..1681807d39 --- /dev/null +++ b/basctl/source/inc/docsignature.hxx @@ -0,0 +1,74 @@ +/* -*- 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 . + */ +#pragma once + +#include <sfx2/signaturestate.hxx> +#include <vcl/weld.hxx> + +class SfxObjectShell; + +namespace basctl +{ + + + class ScriptDocument; + + /// encapsulates (actions on) the signature/state of a document + class DocumentSignature + { + public: + /** creates a DocumentSignature instance for the given document + + If the given ScriptDocument instance refers to the application, or to a document + which does not support being signed, the DocumentSignature instance is invalid afterwards. + */ + explicit DocumentSignature (ScriptDocument const&); + + /** determines whether the instance is valid + + An instance is valid if and only if it has been constructed with a document + which supports signatures. + */ + bool supportsSignatures() const; + + /** signs the scripting content inside the document + + @precond + isValid returns <TRUE/> + */ + void signScriptingContent(weld::Window* pDialogParent) const; + + /** retrieves the state of the signature of the scripting content inside the document + + If the instance is not valid, then SIGNATURESTATE_NOSIGNATURES is returned. + */ + SignatureState getScriptingSignatureState() const; + + private: + DocumentSignature() = delete; + + private: + SfxObjectShell* m_pShell; + }; + + +} // namespace basctl + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/iderid.hxx b/basctl/source/inc/iderid.hxx new file mode 100644 index 0000000000..83cbc3d0c4 --- /dev/null +++ b/basctl/source/inc/iderid.hxx @@ -0,0 +1,31 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ustring.hxx> +#include <unotools/resmgr.hxx> + +namespace basctl +{ +OUString IDEResId(TranslateId aId); + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/layout.hxx b/basctl/source/inc/layout.hxx new file mode 100644 index 0000000000..29b44896b3 --- /dev/null +++ b/basctl/source/inc/layout.hxx @@ -0,0 +1,133 @@ +/* -*- 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 . + */ + +#pragma once + +#include "bastypes.hxx" +#include <vcl/split.hxx> +#include <vcl/vclptr.hxx> + +#include <vector> + +class DockingWindow; +class SfxRequest; +class SfxItemSet; + +namespace basctl +{ +class DockingWindow; +class BaseWindow; + +// Layout -- the common base of ModulLayout and DialogLayout. +// Handles the splitting lines and the dockable windows. + +class Layout : public vcl::Window +{ +public: + void ArrangeWindows(); + + virtual void Activating(BaseWindow&); + virtual void Deactivating(); + virtual void ExecuteGlobal(SfxRequest&) {} + virtual void GetState(SfxItemSet&, unsigned nWhich) = 0; + virtual void UpdateDebug(bool bBasicStopped) = 0; + + virtual ~Layout() override; + virtual void dispose() override; + +protected: + explicit Layout(vcl::Window* pParent); + + void AddToLeft(DockingWindow* pWin, Size const& rSize) { aLeftSide.Add(pWin, rSize); } + void AddToBottom(DockingWindow* pWin, Size const& rSize) { aBottomSide.Add(pWin, rSize); } + void Remove(DockingWindow*); + bool HasSize() const { return !bFirstSize; } + + // Window: + virtual void Resize() override; + virtual void DataChanged(DataChangedEvent const& rDCEvt) override; + // new: + virtual void OnFirstSize(tools::Long nWidth, tools::Long nHeight) = 0; + +private: + // the main child window (either ModulWindow or DialogWindow) + VclPtr<BaseWindow> pChild; + + // when this window has at first (nonempty) size + bool bFirstSize; + + // horizontal or vertical split strip + class SplittedSide + { + public: + enum class Side + { + Left, + Bottom + }; + SplittedSide(Layout*, Side); + void Add(DockingWindow*, Size const&); + void Remove(DockingWindow*); + bool IsEmpty() const; + tools::Long GetSize() const; + void ArrangeIn(tools::Rectangle const&); + void dispose(); + + private: + // the layout window + Layout& rLayout; + // horizontal or vertical strip? + bool bVertical; + // lower (top or left) or higher (bottom or right) strip? + bool bLower; + // rectangle to move in + tools::Rectangle aRect; + // size (width or height) + tools::Long nSize; + // the main splitting line + VclPtr<Splitter> aSplitter; + // the dockable windows (and some data) + struct Item + { + // pointer to the dockable window + VclPtr<DockingWindow> pWin; + // starting and ending position in the strip + // They may be different from the actual window position, because + // the window may fill the space of the adjacent currently + // non-docking windows, but this change is not stored in these + // variables. These change only when the splitter lines are moved. + tools::Long nStartPos, nEndPos; + // splitter line window before the window + // (the first one is always nullptr) + VclPtr<Splitter> pSplit; + }; + std::vector<Item> vItems; + + Point MakePoint(tools::Long, tools::Long) const; + Size MakeSize(tools::Long, tools::Long) const; + static bool IsDocking(DockingWindow const&); + DECL_LINK(SplitHdl, Splitter*, void); + void CheckMarginsFor(Splitter*); + void InitSplitter(Splitter&); + } aLeftSide, aBottomSide; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/localizationmgr.hxx b/basctl/source/inc/localizationmgr.hxx new file mode 100644 index 0000000000..3e2ff0fc58 --- /dev/null +++ b/basctl/source/inc/localizationmgr.hxx @@ -0,0 +1,151 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <string_view> + +#include "scriptdocument.hxx" + +#include <com/sun/star/resource/XStringResourceManager.hpp> + +namespace basctl +{ + +class Shell; +class DlgEditor; + +class LocalizationMgr +{ + css::uno::Reference< css::resource::XStringResourceManager > m_xStringResourceManager; + + Shell* m_pShell; + + ScriptDocument m_aDocument; + OUString m_aLibName; + + css::lang::Locale m_aLocaleBeforeBasicStart; + + enum HandleResourceMode + { + SET_IDS, + RESET_IDS, + RENAME_DIALOG_IDS, + RENAME_CONTROL_IDS, + REMOVE_IDS_FROM_RESOURCE, + MOVE_RESOURCES, + COPY_RESOURCES + }; + static sal_Int32 implHandleControlResourceProperties(const css::uno::Any& rControlAny, + std::u16string_view aDialogName, + std::u16string_view aCtrlName, + const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager, + const css::uno::Reference< css::resource::XStringResourceResolver >& xSourceStringResolver, + HandleResourceMode eMode ); + + void enableResourceForAllLibraryDialogs() + { + implEnableDisableResourceForAllLibraryDialogs( SET_IDS ); + } + void disableResourceForAllLibraryDialogs() + { + implEnableDisableResourceForAllLibraryDialogs( RESET_IDS ); + } + void implEnableDisableResourceForAllLibraryDialogs( HandleResourceMode eMode ); + +public: + LocalizationMgr(Shell*, ScriptDocument , OUString aLibName, + const css::uno::Reference < css::resource::XStringResourceManager >& xStringResourceManager ); + + const css::uno::Reference< css::resource::XStringResourceManager >& getStringResourceManager() const + { + return m_xStringResourceManager; + } + + bool isLibraryLocalized(); + + void handleTranslationbar(); + + void handleAddLocales( const css::uno::Sequence + < css::lang::Locale >& aLocaleSeq ); + + void handleRemoveLocales( const css::uno::Sequence + < css::lang::Locale >& aLocaleSeq ); + + void handleSetDefaultLocale(const css::lang::Locale& rLocale); + + void handleSetCurrentLocale(const css::lang::Locale& rLocale); + + void handleBasicStarted(); + + void handleBasicStopped(); + + static void setControlResourceIDsForNewEditorObject(DlgEditor const * pEditor, + const css::uno::Any& rControlAny, std::u16string_view aCtrlName); + + static void renameControlResourceIDsForEditorObject(DlgEditor const * pEditor, + const css::uno::Any& rControlAny, std::u16string_view aNewCtrlName); + + static void deleteControlResourceIDsForDeletedEditorObject(DlgEditor const * pEditor, + const css::uno::Any& rControlAny, std::u16string_view aCtrlName); + + static void setStringResourceAtDialog( const ScriptDocument& rDocument, const OUString& aLibName, std::u16string_view aDlgName, + const css::uno::Reference< css::container::XNameContainer >& xDialogModel ); + + static void renameStringResourceIDs( const ScriptDocument& rDocument, const OUString& aLibName, std::u16string_view aDlgName, + const css::uno::Reference< css::container::XNameContainer >& xDialogModel ); + + static void removeResourceForDialog( const ScriptDocument& rDocument, const OUString& aLibName, std::u16string_view aDlgName, + const css::uno::Reference< css::container::XNameContainer >& xDialogModel ); + + static css::uno::Reference< css::resource::XStringResourceManager > + getStringResourceFromDialogLibrary( const css::uno::Reference< css::container::XNameContainer >& xDialogLib ); + + // Clipboard / Drag & Drop + static void resetResourceForDialog( + const css::uno::Reference< css::container::XNameContainer >& xDialogModel, + const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager ); + + static void setResourceIDsForDialog( + const css::uno::Reference< css::container::XNameContainer >& xDialogModel, + const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager ); + + static void copyResourcesForPastedEditorObject( DlgEditor const * pEditor, + const css::uno::Any& rControlAny, std::u16string_view aCtrlName, + const css::uno::Reference< css::resource::XStringResourceResolver >& xSourceStringResolver ); + + static void copyResourceForDroppedDialog( + const css::uno::Reference< css::container::XNameContainer >& xDialogModel, + std::u16string_view aDialogName, + const css::uno::Reference< css::resource::XStringResourceManager >& xStringResourceManager, + const css::uno::Reference< css::resource::XStringResourceResolver >& xSourceStringResolver ); + + static void copyResourceForDialog( + const css::uno::Reference< css::container::XNameContainer >& xDialogModel, + const css::uno::Reference< css::resource:: + XStringResourceResolver >& xSourceStringResolver, + const css::uno::Reference< css::resource:: + XStringResourceManager >& xTargetStringResourceManager ); +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/managelang.hxx b/basctl/source/inc/managelang.hxx new file mode 100644 index 0000000000..58dd418905 --- /dev/null +++ b/basctl/source/inc/managelang.hxx @@ -0,0 +1,98 @@ +/* -*- 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 . + */ + +#pragma once + +#include <utility> +#include <vcl/weld.hxx> + +class SvxLanguageBox; + +namespace basctl +{ + +class LocalizationMgr; + +struct LanguageEntry +{ + css::lang::Locale m_aLocale; + bool m_bIsDefault; + + LanguageEntry( css::lang::Locale _aLocale, + bool _bIsDefault ) : + m_aLocale(std::move( _aLocale )), + m_bIsDefault( _bIsDefault ) {} +}; + +extern bool localesAreEqual( const css::lang::Locale& rLocaleLeft, + const css::lang::Locale& rLocaleRight ); + +class ManageLanguageDialog : public weld::GenericDialogController +{ +private: + std::shared_ptr<LocalizationMgr> m_xLocalizationMgr; + + OUString m_sDefLangStr; + OUString m_sCreateLangStr; + + std::unique_ptr<weld::TreeView> m_xLanguageLB; + std::unique_ptr<weld::Button> m_xAddPB; + std::unique_ptr<weld::Button> m_xDeletePB; + std::unique_ptr<weld::Button> m_xMakeDefPB; + + void Init(); + void FillLanguageBox(); + void ClearLanguageBox(); + + DECL_LINK(AddHdl, weld::Button&, void); + DECL_LINK(DeleteHdl, weld::Button&, void); + DECL_LINK(MakeDefHdl, weld::Button&, void); + DECL_LINK(SelectHdl, weld::TreeView&, void); + +public: + ManageLanguageDialog(weld::Window* pParent, std::shared_ptr<LocalizationMgr> _pLMgr); + virtual ~ManageLanguageDialog() override; +}; + +class SetDefaultLanguageDialog : public weld::GenericDialogController +{ +private: + std::shared_ptr<LocalizationMgr> m_xLocalizationMgr; + + void FillLanguageBox(); + + std::unique_ptr<weld::Label> m_xLanguageFT; + std::unique_ptr<weld::TreeView> m_xLanguageLB; + std::unique_ptr<weld::Label> m_xCheckLangFT; + std::unique_ptr<weld::TreeView> m_xCheckLangLB; + std::unique_ptr<weld::Label> m_xDefinedFT; + std::unique_ptr<weld::Label> m_xAddedFT; + std::unique_ptr<weld::Label> m_xAltTitle; + std::unique_ptr<SvxLanguageBox> m_xLanguageCB; + +public: + SetDefaultLanguageDialog(weld::Window* pParent, std::shared_ptr<LocalizationMgr> xLMgr); + virtual ~SetDefaultLanguageDialog() override; + + css::uno::Sequence< css::lang::Locale > GetLocales() const; +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/propbrw.hxx b/basctl/source/inc/propbrw.hxx new file mode 100644 index 0000000000..c9ad803444 --- /dev/null +++ b/basctl/source/inc/propbrw.hxx @@ -0,0 +1,85 @@ +/* -*- 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 . + */ + +#pragma once + +#include <sal/config.h> + +#include <vector> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XFrame2.hpp> +#include <svl/lstner.hxx> +#include <svl/SfxBroadcaster.hxx> +#include <svx/svdmark.hxx> +#include "bastypes.hxx" + +class SfxBindings; +class SdrView; +class SfxViewShell; + +namespace basctl +{ + +class DialogWindowLayout; + +class PropBrw final : public DockingWindow, public SfxListener, public SfxBroadcaster +{ +private: + VclPtr<vcl::Window> m_xContentArea; + bool m_bInitialStateChange; + + css::uno::Reference< css::frame::XFrame2 > + m_xMeAsFrame; + css::uno::Reference< css::beans::XPropertySet > + m_xBrowserController; + css::uno::Reference< css::frame::XModel > + m_xContextDocument; + + SdrView* pView; + virtual bool Close() override; + + typedef std::vector< css::uno::Reference< css::uno::XInterface> > InterfaceArray; + + static css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > + CreateMultiSelectionSequence( const SdrMarkList& _rMarkList ); + void implSetNewObjectSequence( const css::uno::Sequence + < css::uno::Reference< css::uno::XInterface > >& _rObjectSeq ); + + void implSetNewObject( const css::uno::Reference< css::beans::XPropertySet >& _rxObject); + + static OUString GetHeadlineName( const css::uno::Reference< css::beans::XPropertySet >& _rxObject); + +public: + explicit PropBrw (DialogWindowLayout&); + virtual ~PropBrw() override; + virtual void dispose() override; + // note: changing the Context document to an instance other than the one given in the ctor is not supported + // currently + void Update( const SfxViewShell* pShell ); + +private: + void ImplUpdate( const css::uno::Reference< css::frame::XModel >& _rxContextDocument, SdrView* pView ); + void ImplDestroyController(); + void ImplReCreateController(); +}; + +} // namespace basctl + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/sbxitem.hxx b/basctl/source/inc/sbxitem.hxx new file mode 100644 index 0000000000..941ffd3e0e --- /dev/null +++ b/basctl/source/inc/sbxitem.hxx @@ -0,0 +1,65 @@ +/* -*- 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 . + */ +#pragma once + +#include "scriptdocument.hxx" +#include <svl/poolitem.hxx> + +namespace basctl +{ + +enum ItemType +{ + TYPE_UNKNOWN, + TYPE_SHELL, + TYPE_LIBRARY, + TYPE_MODULE, + TYPE_DIALOG, + TYPE_METHOD +}; + +class SbxItem : public SfxPoolItem +{ + const ScriptDocument m_aDocument; + const OUString m_aLibName; + const OUString m_aName; + const OUString m_aMethodName; + ItemType m_eType; + +public: + static SfxPoolItem* CreateDefault(); + SbxItem(sal_uInt16 nWhich, ScriptDocument aDocument, OUString aLibName, OUString aName, ItemType); + SbxItem(sal_uInt16 nWhich, ScriptDocument aDocument, OUString aLibName, OUString aName, OUString aMethodName, ItemType eType); + + virtual SbxItem* Clone(SfxItemPool *pPool = nullptr) const override; + virtual bool operator==(const SfxPoolItem&) const override; + + ScriptDocument const& GetDocument () const { return m_aDocument; } + OUString const& GetLibName () const { return m_aLibName; } + OUString const& GetName () const { return m_aName; } + OUString const& GetMethodName () const { return m_aMethodName; } + ItemType GetType () const { return m_eType; } +}; + +} // namespace basctl + +// For baside.sdi, because I don't know how to use nested names in it. +using basctl::SbxItem; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/basctl/source/inc/scriptdocument.hxx b/basctl/source/inc/scriptdocument.hxx new file mode 100644 index 0000000000..da7f2b50d3 --- /dev/null +++ b/basctl/source/inc/scriptdocument.hxx @@ -0,0 +1,481 @@ +/* -*- 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 . + */ + +#pragma once + +#include <com/sun/star/script/XLibraryContainer.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <com/sun/star/io/XInputStreamProvider.hpp> + +#include <memory> +#include <vector> + +class SfxListener; + +class BasicManager; + + +namespace basctl +{ + + enum LibraryContainerType + { + E_SCRIPTS, + E_DIALOGS + }; + + enum LibraryLocation + { + LIBRARY_LOCATION_UNKNOWN, + LIBRARY_LOCATION_USER, + LIBRARY_LOCATION_SHARE, + LIBRARY_LOCATION_DOCUMENT + }; + + enum class LibraryType + { + Module, + Dialog, + All + }; + + class ScriptDocument; + typedef std::vector< ScriptDocument > ScriptDocuments; + + /** encapsulates a document which contains Basic scripts and dialogs + */ + class ScriptDocument + { + private: + class Impl; + std::shared_ptr<Impl> m_pImpl; + + private: + /** creates a ScriptDocument instance which operates on the application-wide + scripts and dialogs + */ + ScriptDocument(); + + public: + enum SpecialDocument { NoDocument }; + /** creates a ScriptDocument instance which does refers to neither the application-wide, + nor a specific real document's scripts. + + This constructor might come handy when you need some kind of uninitialized + ScriptDocument, which you do not want to operate on (yet), but initialize later + by assignment. + + <member>isValid</member> will return <FALSE/> for a ScriptDocument constructed + this way. + */ + explicit ScriptDocument( SpecialDocument _eType ); + + /** creates a ScriptDocument instance which refers to a document given as + XModel + + @param _rxDocument + the document. Must not be <NULL/>. + */ + explicit ScriptDocument( const css::uno::Reference< css::frame::XModel >& _rxDocument ); + + /** returns a reference to a shared ScriptDocument instance which + operates on the application-wide scripts and dialogs + */ + static const ScriptDocument& + getApplicationScriptDocument(); + + /** returns a (newly created) ScriptDocument instance for the document to + which a given BasicManager belongs + + If the basic manager is the application's basic manager, then the (shared) + ScriptDocument instance which is responsible for the application is returned. + + @see getApplicationScriptDocument + */ + static ScriptDocument + getDocumentForBasicManager( const BasicManager* _pManager ); + + /** returns a (newly created) ScriptDocument instance for the document + with a given caption or URL + + If there is no document with the given caption, then the (shared) + ScriptDocument instance which is responsible for the application is returned. + + @see getApplicationScriptDocument + */ + static ScriptDocument + getDocumentWithURLOrCaption( std::u16string_view _rUrlOrCaption ); + + /** operation mode for getAllScriptDocuments + */ + enum ScriptDocumentList + { + /** all ScriptDocuments, including the dedicated one which represents + the application-wide scripts/dialogs. + */ + AllWithApplication, + /** real documents only, sorted lexicographically by their title (using the sys locale's default + collator) + */ + DocumentsSorted + }; + + /** returns the set of ScriptDocument instances, one for each open document which + contains Basic/Dialog containers; plus an additional instance for + the application, if desired + + Documents which are not visible - i.e. do not have a visible frame. + + @param _bIncludingApplication + <TRUE/> if the application-wide scripts/dialogs should also be represented + by a ScriptDocument + */ + static ScriptDocuments + getAllScriptDocuments( ScriptDocumentList _eListType ); + + // comparison + bool operator==( const ScriptDocument& _rhs ) const; + bool operator!=( const ScriptDocument& _rhs ) const { return !( *this == _rhs ); } + + /// retrieves a (pretty simple) hash code for the document + sal_Int32 hashCode() const; + + /** determines whether the document is actually able to contain Basic/Dialog libraries + + Note that validity does not automatically imply the document can be used for active + work. Instead, it is possible the document is closed already (or being closed currently). + In this case, isValid will return <TRUE/>, but isAlive will return <FALSE/>. + + @return + <TRUE/> if the instance refers to a document which contains Basic/Dialog libraries, + or the application as a whole, <FALSE/> otherwise. + + @see isAlive + */ + bool isValid() const; + + /** determines whether the document instance is alive + + If the instance is not valid, <FALSE/> is returned. + + If the instance refers to a real document, which is already closed, or just being closed, + the method returns <FALSE/>. + + If the instance refers to the application, <TRUE/> is returned. + + @see isValid + */ + bool isAlive() const; + + bool isInVBAMode() const; + /// returns the BasicManager associated with this instance + BasicManager* + getBasicManager() const; + + /** returns the UNO component representing the document which the instance operates on + + Must not be used when the instance operates on the application-wide + Basic/Dialog libraries. + */ + css::uno::Reference< css::frame::XModel > + getDocument() const; + + /** returns the UNO component representing the document which the instance operates on + + May be used when the instance operates on the application-wide + Basic/Dialog libraries, in this case it returns <NULL/>. + */ + css::uno::Reference< css::frame::XModel > + getDocumentOrNull() const; + + /** returns the Basic or Dialog library container of the document + + If the document is not valid, <NULL/> is returned. + */ + css::uno::Reference< css::script::XLibraryContainer > + getLibraryContainer( LibraryContainerType _eType ) const; + + /** determines whether there exists a library of the given type, with the given name + */ + bool hasLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const; + + /** returns a script or dialog library given by name + + @param _eType + the type of library to load + @param _rLibName + the name of the script library + @param _bLoadLibrary + <TRUE/> if and only if the library should be loaded. + + @throws NoSuchElementException + if there is no script library with the given name + */ + css::uno::Reference< css::container::XNameContainer > + getLibrary( LibraryContainerType _eType, const OUString& _rLibName, bool _bLoadLibrary ) const; + + /** creates a script or dialog library in the document, or returns an existing one + + If <code>_rLibName</code> denotes an existing library which does not need to be created, + then this library will automatically be loaded, and then returned. + */ + css::uno::Reference< css::container::XNameContainer > + getOrCreateLibrary( LibraryContainerType _eType, const OUString& _rLibName ) const; + + /** returns the names of the modules in a given script or dialog library of the document + */ + css::uno::Sequence< OUString > + getObjectNames( LibraryContainerType _eType, const OUString& _rLibName ) const; + + /** retrieves a name for a newly to be created module or dialog + */ + OUString createObjectName( LibraryContainerType _eType, const OUString& _rLibName ) const; + + /** loads a script or dialog library given by name, if there is such a library + */ + void loadLibraryIfExists( LibraryContainerType _eType, const OUString& _rLibrary ); + + /// retrieves the (combined) names of all script and dialog libraries + css::uno::Sequence< OUString > + getLibraryNames() const; + + /** removes a given script module from the document + + @return + <TRUE/> if and only if the removal was successful. When <FALSE/> is returned, + this will reported as assertion in a non-product build. + */ + bool removeModule( const OUString& _rLibName, const OUString& _rModuleName ) const; + + /** creates a module with the given name in the given library + @param _rLibName + the library name + @param _rModName + the name of the to-be-created module + @param _bCreateMain + determines whether or not a function Main should be created + @param _out_rNewModuleCode + the source code of the newly created module + @return + <TRUE/> if and only if the creation was successful + */ + bool createModule( const OUString& _rLibName, const OUString& _rModName, bool _bCreateMain, OUString& _out_rNewModuleCode ) const; + + /** inserts a given piece as code as module + @param _rLibName + the name of the library to insert the module into. If a library with this name does + not yet exist, it will be created. + @param _rModName + the name of the module to insert the code as. Must denote a name which is not yet + used in the module library. + @param _rModuleCode + the code of the new module + @return + <TRUE/> if and only if the insertion was successful. + */ + bool insertModule( const OUString& _rLibName, const OUString& _rModName, const OUString& _rModuleCode ) const; + + /** updates a given module with new code + @param _rLibName + the name of the library the modules lives in. Must denote an existing module library. + @param _rModName + the name of the module to update. Must denote an existing module in the given library. + @param _rModuleCode + the new module code. + @return + <TRUE/> if and only if the insertion was successful. + */ + bool updateModule( const OUString& _rLibName, const OUString& _rModName, const OUString& _rModuleCode ) const; + + /// determines whether a module with the given name exists in the given library + bool hasModule( const OUString& _rLibName, const OUString& _rModName ) const; + + /** retrieves a module's source + @param _rLibName + the library name where the module is located + @param _rModName + the module name + @param _out_rModuleSource + takes the module's source upon successful return + @return + <TRUE/> if and only if the code could be successfully retrieved, <FALSE/> otherwise + */ + bool getModule( const OUString& _rLibName, const OUString& _rModName, OUString& _rModuleSource ) const; + + /** renames a module + @param _rLibName + the library where the module lives in. Must denote an existing library. + @param _rOldName + the old module name. Must denote an existing module. + @param _rNewName + the new module name + @return + <TRUE/> if and only if renaming was successful. + */ + bool renameModule( const OUString& _rLibName, const OUString& _rOldName, const OUString& _rNewName ) const; + + /** removes a given dialog from the document + + @return + <TRUE/> if and only if the removal was successful. When <FALSE/> is returned, + this will reported as assertion in a non-product build. + */ + bool removeDialog( const OUString& _rLibName, const OUString& _rDialogName ) const; + + /// determines whether a dialog with the given name exists in the given library + bool hasDialog( const OUString& _rLibName, const OUString& _rDialogName ) const; + + /** retrieves a dialog + @param _rLibName + the library name where the module is located + @param _rDialogName + the dialog's name + @param _out_rDialogSource + takes the provider for the dialog's description, upon successful return + @return + <TRUE/> if and only if the dialog could be successfully retrieved, <FALSE/> otherwise + */ + bool getDialog( + const OUString& _rLibName, + const OUString& _rDialogName, + css::uno::Reference< css::io::XInputStreamProvider >& _out_rDialogProvider + ) const; + + /** renames a dialog + @param _rLibName + the library where the dialog lives in. Must denote an existing library. + @param _rOldName + the old dialog name. Must denote an existing dialog. + @param _rNewName + the new dialog name + @param _rxExistingDialogModel + the existing model of the dialog, if already loaded in the IDE + @return + <TRUE/> if and only if renaming was successful. + */ + bool renameDialog( + const OUString& _rLibName, + const OUString& _rOldName, + const OUString& _rNewName, + const css::uno::Reference< css::container::XNameContainer >& _rxExistingDialogModel + ) const; + + /** create a dialog + @param _rLibName + the library name where the module is located + @param _rDialogName + the dialog's name + @param _out_rDialogSource + takes the provider for the dialog's description, upon successful return + @return + <TRUE/> if and only if the dialog could be successfully retrieved, <FALSE/> otherwise + */ + bool createDialog( + const OUString& _rLibName, + const OUString& _rDialogName, + css::uno::Reference< css::io::XInputStreamProvider >& _out_rDialogProvider + ) const; + + /** inserts a given dialog into a given library + + @param _rLibName + the name of the library to insert the dialog into. If a library with this name does + not yet exist, it will be created. + @param _rModName + the name of the dialog to insert. Must denote a name which is not yet + used in the dialog library. + @param _rDialogProvider + the provider of the dialog's description + @return + <TRUE/> if and only if the insertion was successful. + */ + bool insertDialog( + const OUString& _rLibName, + const OUString& _rDialogName, + const css::uno::Reference< css::io::XInputStreamProvider >& _rDialogProvider + ) const; + + /** determines whether the document is read-only + + cannot be called if the document operates on the application-wide scripts + */ + bool isReadOnly() const; + + /** determines whether the ScriptDocument instance operates on the whole application, + as opposed to a real document + */ + bool isApplication() const; + + /** determines whether the ScriptDocument instance operates on a real document, + as opposed to the whole application + */ + bool isDocument() const { return isValid() && !isApplication(); } + + /** marks the document as modified + @precond + the instance operates on a real document, not on the application + @see isDocument + */ + void setDocumentModified() const; + + /** determines whether the document is modified + @precond + the instance operates on a real document, not on the application + @see isDocument + */ + bool isDocumentModified() const; + + /** saves the document, if the instance refers to a real document + @precond + <code>isApplication</code> returns <FALSE/> + */ + void saveDocument( + const css::uno::Reference< css::task::XStatusIndicator >& _rxStatusIndicator + ) const; + + /// returns the location of a library given by name + LibraryLocation + getLibraryLocation( const OUString& _rLibName ) const; + + /// returns the title for the document + OUString getTitle( LibraryLocation _eLocation, LibraryType _eType = LibraryType::All ) const; + + /** returns the title of the document + + to be used for valid documents only + */ + OUString getTitle() const; + + /** determines whether the document is currently the one-and-only application-wide active document + */ + bool isActive() const; + + /** determines whether macro execution for this document is allowed + + only to be called for real documents (->isDocument) + */ + bool allowMacros() const; + }; + + +} // namespace basctl + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |