summaryrefslogtreecommitdiffstats
path: root/basctl/source/basicide/baside2b.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /basctl/source/basicide/baside2b.cxx
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'basctl/source/basicide/baside2b.cxx')
-rw-r--r--basctl/source/basicide/baside2b.cxx2985
1 files changed, 2985 insertions, 0 deletions
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: */