diff options
Diffstat (limited to 'basctl/source/basicide')
48 files changed, 22311 insertions, 0 deletions
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: */ |