Bump version to 24.04.3.4
[LibreOffice.git] / basctl / source / basicide / baside2b.cxx
blob33cbbc4787ffe032c05e51e3894babb35b315583
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cassert>
23 #include <string_view>
25 #include <helpids.h>
26 #include <iderid.hxx>
27 #include <strings.hrc>
28 #include <bitmaps.hlst>
30 #include "baside2.hxx"
31 #include "brkdlg.hxx"
32 #include <basidesh.hxx>
33 #include <basobj.hxx>
34 #include <iderdll.hxx>
36 #include <basic/sbmeth.hxx>
37 #include <basic/sbuno.hxx>
38 #include <com/sun/star/beans/XMultiPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
40 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
41 #include <com/sun/star/script/XLibraryContainer2.hpp>
42 #include <comphelper/string.hxx>
43 #include <comphelper/diagnose_ex.hxx>
44 #include <o3tl/string_view.hxx>
45 #include <officecfg/Office/Common.hxx>
46 #include <sfx2/dispatch.hxx>
47 #include <sfx2/progress.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <tools/debug.hxx>
50 #include <utility>
51 #include <vcl/image.hxx>
52 #include <vcl/weld.hxx>
53 #include <vcl/weldutils.hxx>
54 #include <svl/urihelper.hxx>
55 #include <svx/svxids.hrc>
56 #include <vcl/commandevent.hxx>
57 #include <vcl/xtextedt.hxx>
58 #include <vcl/textview.hxx>
59 #include <vcl/txtattr.hxx>
60 #include <vcl/settings.hxx>
61 #include <vcl/ptrstyle.hxx>
62 #include <vcl/event.hxx>
63 #include <vcl/svapp.hxx>
64 #include <vcl/taskpanelist.hxx>
65 #include <vcl/help.hxx>
66 #include <cppuhelper/implbase.hxx>
67 #include <vector>
68 #include <com/sun/star/reflection/theCoreReflection.hpp>
69 #include <unotools/charclass.hxx>
70 #include "textwindowpeer.hxx"
71 #include "uiobject.hxx"
72 #include <basegfx/utils/zoomtools.hxx>
73 #include <svl/itemset.hxx>
75 namespace basctl
78 using namespace ::com::sun::star;
79 using namespace ::com::sun::star::uno;
81 namespace
84 sal_uInt16 const NoMarker = 0xFFFF;
85 tools::Long const nBasePad = 2;
86 tools::Long const nCursorPad = 5;
88 tools::Long nVirtToolBoxHeight; // inited in WatchWindow, used in Stackwindow
90 // Returns pBase converted to SbxVariable if valid and is not an SbxMethod.
91 SbxVariable* IsSbxVariable (SbxBase* pBase)
93 if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pBase))
94 if (!dynamic_cast<SbxMethod*>(pVar))
95 return pVar;
96 return nullptr;
99 Image GetImage(const OUString& rId)
101 return Image(StockImage::Yes, rId);
104 int const nScrollLine = 12;
105 int const nScrollPage = 60;
106 int const DWBORDER = 3;
108 std::u16string_view const cSuffixes = u"%&!#@$";
110 } // namespace
114 * Helper functions to get/set text in TextEngine using
115 * the stream interface.
117 * get/setText() only supports tools Strings limited to 64K).
119 OUString getTextEngineText (ExtTextEngine& rEngine)
121 SvMemoryStream aMemStream;
122 aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
123 aMemStream.SetLineDelimiter( LINEEND_LF );
124 rEngine.Write( aMemStream );
125 std::size_t nSize = aMemStream.Tell();
126 OUString aText( static_cast<const char*>(aMemStream.GetData()),
127 nSize, RTL_TEXTENCODING_UTF8 );
128 return aText;
131 void setTextEngineText (ExtTextEngine& rEngine, std::u16string_view aStr)
133 rEngine.SetText(OUString());
134 OString aUTF8Str = OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 );
135 SvMemoryStream aMemStream( const_cast<char *>(aUTF8Str.getStr()), aUTF8Str.getLength(),
136 StreamMode::READ );
137 aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
138 aMemStream.SetLineDelimiter( LINEEND_LF );
139 rEngine.Read(aMemStream);
142 namespace
145 void lcl_DrawIDEWindowFrame(DockingWindow const * pWin, vcl::RenderContext& rRenderContext)
147 if (pWin->IsFloatingMode())
148 return;
150 Size aSz(pWin->GetOutputSizePixel());
151 const Color aOldLineColor(rRenderContext.GetLineColor());
152 rRenderContext.SetLineColor(COL_WHITE);
153 // White line on top
154 rRenderContext.DrawLine(Point(0, 0), Point(aSz.Width(), 0));
155 // Black line at bottom
156 rRenderContext.SetLineColor(COL_BLACK);
157 rRenderContext.DrawLine(Point(0, aSz.Height() - 1),
158 Point(aSz.Width(), aSz.Height() - 1));
159 rRenderContext.SetLineColor(aOldLineColor);
162 void lcl_SeparateNameAndIndex( const OUString& rVName, OUString& rVar, OUString& rIndex )
164 rVar = rVName;
165 rIndex.clear();
166 sal_Int32 nIndexStart = rVar.indexOf( '(' );
167 if ( nIndexStart != -1 )
169 sal_Int32 nIndexEnd = rVar.indexOf( ')', nIndexStart );
170 if (nIndexEnd != -1)
172 rIndex = rVar.copy(nIndexStart + 1, nIndexEnd - nIndexStart - 1);
173 rVar = rVar.copy(0, nIndexStart);
174 rVar = comphelper::string::stripEnd(rVar, ' ');
175 rIndex = comphelper::string::strip(rIndex, ' ');
179 if ( !rVar.isEmpty() )
181 sal_uInt16 nLastChar = rVar.getLength()-1;
182 if ( cSuffixes.find(rVar[ nLastChar ] ) != std::u16string_view::npos )
183 rVar = rVar.replaceAt( nLastChar, 1, u"" );
185 if ( !rIndex.isEmpty() )
187 sal_uInt16 nLastChar = rIndex.getLength()-1;
188 if ( cSuffixes.find(rIndex[ nLastChar ] ) != std::u16string_view::npos )
189 rIndex = rIndex.replaceAt( nLastChar, 1, u"" );
193 } // namespace
196 // EditorWindow
199 class EditorWindow::ChangesListener:
200 public cppu::WeakImplHelper< beans::XPropertiesChangeListener >
202 public:
203 explicit ChangesListener(EditorWindow & editor): editor_(editor) {}
205 private:
206 virtual ~ChangesListener() override {}
208 virtual void SAL_CALL disposing(lang::EventObject const &) override
210 std::unique_lock g(editor_.mutex_);
211 editor_.notifier_.clear();
214 virtual void SAL_CALL propertiesChange(
215 Sequence< beans::PropertyChangeEvent > const &) override
217 SolarMutexGuard g;
218 editor_.ImplSetFont();
221 EditorWindow & editor_;
224 class EditorWindow::ProgressInfo : public SfxProgress
226 public:
227 ProgressInfo (SfxObjectShell* pObjSh, OUString const& rText, sal_uInt32 nRange) :
228 SfxProgress(pObjSh, rText, nRange),
229 nCurState(0)
232 void StepProgress ()
234 SetState(++nCurState);
237 private:
238 sal_uInt32 nCurState;
241 EditorWindow::EditorWindow (vcl::Window* pParent, ModulWindow* pModulWindow) :
242 Window(pParent, WB_BORDER),
243 rModulWindow(*pModulWindow),
244 nCurTextWidth(0),
245 m_nSetSourceInBasicId(nullptr),
246 aHighlighter(HighlighterLanguage::Basic),
247 aSyntaxIdle( "basctl EditorWindow aSyntaxIdle" ),
248 bHighlighting(false),
249 bDoSyntaxHighlight(true),
250 bDelayHighlight(true),
251 pCodeCompleteWnd(VclPtr<CodeCompleteWindow>::Create(this))
253 set_id("EditorWindow");
254 const Wallpaper aBackground(rModulWindow.GetLayout().GetSyntaxBackgroundColor());
255 SetBackground(aBackground);
256 GetWindow(GetWindowType::Border)->SetBackground(aBackground);
257 SetPointer( PointerStyle::Text );
258 SetHelpId( HID_BASICIDE_EDITORWINDOW );
260 listener_ = new ChangesListener(*this);
261 Reference< beans::XMultiPropertySet > n(
262 officecfg::Office::Common::Font::SourceViewFont::get(),
263 UNO_QUERY_THROW);
265 std::unique_lock g(mutex_);
266 notifier_ = n;
269 // The zoom level applied to the editor window is the zoom slider value in the shell
270 nCurrentZoomLevel = GetShell()->GetCurrentZoomSliderValue();
272 const Sequence<OUString> aPropertyNames{"FontHeight", "FontName"};
273 n->addPropertiesChangeListener(aPropertyNames, listener_);
277 EditorWindow::~EditorWindow()
279 disposeOnce();
282 void EditorWindow::dispose()
284 if (m_nSetSourceInBasicId)
286 Application::RemoveUserEvent(m_nSetSourceInBasicId);
287 m_nSetSourceInBasicId = nullptr;
290 Reference< beans::XMultiPropertySet > n;
292 std::unique_lock g(mutex_);
293 n = notifier_;
295 if (n.is()) {
296 n->removePropertiesChangeListener(listener_);
299 aSyntaxIdle.Stop();
301 if ( pEditEngine )
303 EndListening( *pEditEngine );
304 pEditEngine->RemoveView(pEditView.get());
306 pCodeCompleteWnd.disposeAndClear();
307 vcl::Window::dispose();
310 OUString EditorWindow::GetWordAtCursor()
312 OUString aWord;
314 if ( pEditView )
316 TextEngine* pTextEngine = pEditView->GetTextEngine();
317 if ( pTextEngine )
319 // check first, if the cursor is at a help URL
320 const TextSelection& rSelection = pEditView->GetSelection();
321 const TextPaM& rSelStart = rSelection.GetStart();
322 const TextPaM& rSelEnd = rSelection.GetEnd();
323 OUString aText = pTextEngine->GetText( rSelEnd.GetPara() );
324 CharClass aClass( ::comphelper::getProcessComponentContext() , Application::GetSettings().GetLanguageTag() );
325 sal_Int32 nSelStart = rSelStart.GetIndex();
326 sal_Int32 nSelEnd = rSelEnd.GetIndex();
327 sal_Int32 nLength = aText.getLength();
328 sal_Int32 nStart = 0;
329 sal_Int32 nEnd = nLength;
330 while ( nStart < nLength )
332 OUString aURL( URIHelper::FindFirstURLInText( aText, nStart, nEnd, aClass ) );
333 INetURLObject aURLObj( aURL );
334 if ( aURLObj.GetProtocol() == INetProtocol::VndSunStarHelp
335 && nSelStart >= nStart && nSelStart <= nEnd && nSelEnd >= nStart && nSelEnd <= nEnd )
337 aWord = aURL;
338 break;
340 nStart = nEnd;
341 nEnd = nLength;
344 // Not the selected range, but at the CursorPosition,
345 // if a word is partially selected.
346 if ( aWord.isEmpty() )
347 aWord = pTextEngine->GetWord( rSelEnd );
349 // Can be empty when full word selected, as Cursor behind it
350 if ( aWord.isEmpty() && pEditView->HasSelection() )
351 aWord = pTextEngine->GetWord( rSelStart );
355 return aWord;
358 void EditorWindow::RequestHelp( const HelpEvent& rHEvt )
360 bool bDone = false;
362 // Should have been activated at some point
363 if ( pEditEngine )
365 if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
367 OUString aKeyword = GetWordAtCursor();
368 Application::GetHelp()->SearchKeyword( aKeyword );
369 bDone = true;
371 else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
373 OUString aHelpText;
374 tools::Rectangle aHelpRect;
375 if ( StarBASIC::IsRunning() )
377 Point aWindowPos = rHEvt.GetMousePosPixel();
378 aWindowPos = ScreenToOutputPixel( aWindowPos );
379 Point aDocPos = GetEditView()->GetDocPos( aWindowPos );
380 TextPaM aCursor = GetEditView()->GetTextEngine()->GetPaM(aDocPos);
381 TextPaM aStartOfWord;
382 OUString aWord = GetEditView()->GetTextEngine()->GetWord( aCursor, &aStartOfWord );
383 if ( !aWord.isEmpty() && !comphelper::string::isdigitAsciiString(aWord) )
385 sal_uInt16 nLastChar = aWord.getLength() - 1;
386 if ( cSuffixes.find(aWord[ nLastChar ] ) != std::u16string_view::npos )
387 aWord = aWord.replaceAt( nLastChar, 1, u"" );
388 SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord );
389 if (SbxVariable const* pVar = IsSbxVariable(pSBX))
391 SbxDataType eType = pVar->GetType();
392 if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
393 // might cause a crash e. g. at the selections-object
394 // Type == Object does not mean pVar == Object!
395 ; // aHelpText = ((SbxObject*)pVar)->GetClassName();
396 else if ( eType & SbxARRAY )
397 ; // aHelpText = "{...}";
398 else if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxEMPTY) )
400 aHelpText = pVar->GetName();
401 if ( aHelpText.isEmpty() ) // name is not copied with the passed parameters
402 aHelpText = aWord;
403 aHelpText += "=" + pVar->GetOUString();
406 if ( !aHelpText.isEmpty() )
408 tools::Rectangle aStartWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aStartOfWord));
409 TextPaM aEndOfWord(aStartOfWord.GetPara(), aStartOfWord.GetIndex() + aWord.getLength());
410 tools::Rectangle aEndWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aEndOfWord));
411 aHelpRect = aStartWordRect.GetUnion(aEndWordRect);
413 Point aTopLeft = GetEditView()->GetWindowPos(aHelpRect.TopLeft());
414 aTopLeft = GetEditView()->GetWindow()->OutputToScreenPixel(aTopLeft);
416 aHelpRect.SetPos(aTopLeft);
420 Help::ShowQuickHelp( this, aHelpRect, aHelpText, QuickHelpFlags::NONE);
421 bDone = true;
425 if ( !bDone )
426 Window::RequestHelp( rHEvt );
430 void EditorWindow::Resize()
432 // ScrollBars, etc. happens in Adjust...
433 if ( !pEditView )
434 return;
436 tools::Long nVisY = pEditView->GetStartDocPos().Y();
438 pEditView->ShowCursor();
439 Size aOutSz( GetOutputSizePixel() );
440 tools::Long nMaxVisAreaStart = pEditView->GetTextEngine()->GetTextHeight() - aOutSz.Height();
441 if ( nMaxVisAreaStart < 0 )
442 nMaxVisAreaStart = 0;
443 if ( pEditView->GetStartDocPos().Y() > nMaxVisAreaStart )
445 Point aStartDocPos( pEditView->GetStartDocPos() );
446 aStartDocPos.setY( nMaxVisAreaStart );
447 pEditView->SetStartDocPos( aStartDocPos );
448 pEditView->ShowCursor();
449 rModulWindow.GetBreakPointWindow().GetCurYOffset() = aStartDocPos.Y();
450 rModulWindow.GetLineNumberWindow().GetCurYOffset() = aStartDocPos.Y();
452 InitScrollBars();
453 if ( nVisY != pEditView->GetStartDocPos().Y() )
454 Invalidate();
458 void EditorWindow::MouseMove( const MouseEvent &rEvt )
460 if ( pEditView )
461 pEditView->MouseMove( rEvt );
465 void EditorWindow::MouseButtonUp( const MouseEvent &rEvt )
467 if ( pEditView )
469 pEditView->MouseButtonUp( rEvt );
470 if (SfxBindings* pBindings = GetBindingsPtr())
472 pBindings->Invalidate( SID_BASICIDE_STAT_POS );
473 pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
478 void EditorWindow::MouseButtonDown( const MouseEvent &rEvt )
480 GrabFocus();
481 if (!pEditView)
482 return;
483 pEditView->MouseButtonDown(rEvt);
484 if( pCodeCompleteWnd->IsVisible() )
486 if (pEditView->GetSelection() != pCodeCompleteWnd->GetTextSelection())
488 //selection changed, code complete window should be hidden
489 pCodeCompleteWnd->HideAndRestoreFocus();
494 void EditorWindow::Command( const CommandEvent& rCEvt )
496 if ( !pEditView )
497 return;
499 pEditView->Command( rCEvt );
500 if ( ( rCEvt.GetCommand() == CommandEventId::Wheel ) ||
501 ( rCEvt.GetCommand() == CommandEventId::StartAutoScroll ) ||
502 ( rCEvt.GetCommand() == CommandEventId::AutoScroll ) )
504 const CommandWheelData* pData = rCEvt.GetWheelData();
506 // Check if it is a Ctrl+Wheel zoom command
507 if (pData && pData->IsMod1())
509 const sal_uInt16 nOldZoom = GetCurrentZoom();
510 sal_uInt16 nNewZoom;
511 if( pData->GetDelta() < 0 )
512 nNewZoom = std::max<sal_uInt16>(basctl::Shell::GetMinZoom(),
513 basegfx::zoomtools::zoomOut(nOldZoom));
514 else
515 nNewZoom = std::min<sal_uInt16>(basctl::Shell::GetMaxZoom(),
516 basegfx::zoomtools::zoomIn(nOldZoom));
517 GetShell()->SetGlobalEditorZoomLevel(nNewZoom);
519 else
520 HandleScrollCommand(rCEvt, &rModulWindow.GetEditHScrollBar(), &rModulWindow.GetEditVScrollBar());
522 else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) {
523 SfxDispatcher* pDispatcher = GetDispatcher();
524 if ( pDispatcher )
526 SfxDispatcher::ExecutePopup();
528 if( pCodeCompleteWnd->IsVisible() ) // hide the code complete window
529 pCodeCompleteWnd->ClearAndHide();
533 bool EditorWindow::ImpCanModify()
535 bool bCanModify = true;
536 if ( StarBASIC::IsRunning() && rModulWindow.GetBasicStatus().bIsRunning )
538 // If in Trace-mode, abort the trace or refuse input
539 // Remove markers in the modules in Notify at Basic::Stopped
540 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
541 VclMessageType::Question, VclButtonsType::OkCancel,
542 IDEResId(RID_STR_WILLSTOPPRG)));
543 if (xQueryBox->run() == RET_OK)
545 rModulWindow.GetBasicStatus().bIsRunning = false;
546 StopBasic();
548 else
549 bCanModify = false;
551 return bCanModify;
554 void EditorWindow::KeyInput( const KeyEvent& rKEvt )
556 if ( !pEditView ) // Happens in Win95
557 return;
559 bool const bWasModified = pEditEngine->IsModified();
560 // see if there is an accelerator to be processed first
561 SfxViewShell *pVS( SfxViewShell::Current());
562 bool bDone = pVS && pVS->KeyInput( rKEvt );
564 if (pCodeCompleteWnd->IsVisible() && CodeCompleteOptions::IsCodeCompleteOn())
566 pCodeCompleteWnd->HandleKeyInput(rKEvt);
567 if( rKEvt.GetKeyCode().GetCode() == KEY_UP
568 || rKEvt.GetKeyCode().GetCode() == KEY_DOWN
569 || rKEvt.GetKeyCode().GetCode() == KEY_TAB
570 || rKEvt.GetKeyCode().GetCode() == KEY_POINT)
571 return;
574 if( (rKEvt.GetKeyCode().GetCode() == KEY_SPACE ||
575 rKEvt.GetKeyCode().GetCode() == KEY_TAB ||
576 rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) && CodeCompleteOptions::IsAutoCorrectOn() )
578 HandleAutoCorrect();
581 if( rKEvt.GetCharCode() == '"' && CodeCompleteOptions::IsAutoCloseQuotesOn() )
582 {//autoclose double quotes
583 HandleAutoCloseDoubleQuotes();
586 if( rKEvt.GetCharCode() == '(' && CodeCompleteOptions::IsAutoCloseParenthesisOn() )
587 {//autoclose parenthesis
588 HandleAutoCloseParen();
591 if( rKEvt.GetKeyCode().GetCode() == KEY_RETURN && CodeCompleteOptions::IsProcedureAutoCompleteOn() )
592 {//autoclose implementation
593 HandleProcedureCompletion();
596 if( rKEvt.GetKeyCode().GetCode() == KEY_POINT && CodeCompleteOptions::IsCodeCompleteOn() )
598 HandleCodeCompletion();
600 if ( !bDone && ( !TextEngine::DoesKeyChangeText( rKEvt ) || ImpCanModify() ) )
602 if ( ( rKEvt.GetKeyCode().GetCode() == KEY_TAB ) && !rKEvt.GetKeyCode().IsMod1() &&
603 !rKEvt.GetKeyCode().IsMod2() && !GetEditView()->IsReadOnly() )
605 TextSelection aSel( pEditView->GetSelection() );
606 if ( aSel.GetStart().GetPara() != aSel.GetEnd().GetPara() )
608 bDelayHighlight = false;
609 if ( !rKEvt.GetKeyCode().IsShift() )
610 pEditView->IndentBlock();
611 else
612 pEditView->UnindentBlock();
613 bDelayHighlight = true;
614 bDone = true;
617 if ( !bDone )
618 bDone = pEditView->KeyInput( rKEvt );
620 if ( !bDone )
622 Window::KeyInput( rKEvt );
624 else
626 if (SfxBindings* pBindings = GetBindingsPtr())
628 pBindings->Invalidate( SID_BASICIDE_STAT_POS );
629 pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
630 if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR )
632 pBindings->Update( SID_BASICIDE_STAT_POS );
633 pBindings->Update( SID_BASICIDE_STAT_TITLE );
635 if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ||
636 rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM )
638 // If the module is read-only, warn that it can't be edited
639 if ( rModulWindow.IsReadOnly() )
640 rModulWindow.ShowReadOnlyInfoBar();
642 if ( !bWasModified && pEditEngine->IsModified() )
644 pBindings->Invalidate( SID_SAVEDOC );
645 pBindings->Invalidate( SID_DOC_MODIFIED );
646 pBindings->Invalidate( SID_UNDO );
648 if ( rKEvt.GetKeyCode().GetCode() == KEY_INSERT )
649 pBindings->Invalidate( SID_ATTR_INSERT );
654 void EditorWindow::HandleAutoCorrect()
656 TextSelection aSel = GetEditView()->GetSelection();
657 const sal_uInt32 nLine = aSel.GetStart().GetPara();
658 const sal_Int32 nIndex = aSel.GetStart().GetIndex();
659 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
660 const OUString& sActSubName = GetActualSubName( nLine ); // the actual procedure
662 std::vector<HighlightPortion> aPortions;
663 aHighlighter.getHighlightPortions( aLine, aPortions );
665 if( aPortions.empty() )
666 return;
668 HighlightPortion& r = aPortions.back();
669 if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
670 {//cursor is not standing at the end of the line
671 for (auto const& portion : aPortions)
673 if( portion.nEnd == nIndex )
675 r = portion;
676 break;
681 OUString sStr = aLine.copy( r.nBegin, r.nEnd - r.nBegin );
682 //if WS or empty string: stop, nothing to do
683 if( ( r.tokenType == TokenType::Whitespace ) || sStr.isEmpty() )
684 return;
685 //create the appropriate TextSelection, and update the cache
686 TextPaM aStart( nLine, r.nBegin );
687 TextPaM aEnd( nLine, r.nBegin + sStr.getLength() );
688 TextSelection sTextSelection( aStart, aEnd );
689 rModulWindow.UpdateModule();
690 rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse( aCodeCompleteCache );
691 // correct the last entered keyword
692 if( r.tokenType == TokenType::Keywords )
694 sStr = sStr.toAsciiLowerCase();
695 if( !SbModule::GetKeywordCase(sStr).isEmpty() )
696 // if it is a keyword, get its correct case
697 sStr = SbModule::GetKeywordCase(sStr);
698 else
699 // else capitalize first letter/select the correct one, and replace
700 sStr = sStr.replaceAt( 0, 1, OUString(sStr[0]).toAsciiUpperCase() );
702 pEditEngine->ReplaceText( sTextSelection, sStr );
703 pEditView->SetSelection( aSel );
705 if( r.tokenType != TokenType::Identifier )
706 return;
708 // correct variables
709 if( !aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName ).isEmpty() )
711 sStr = aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName );
712 pEditEngine->ReplaceText( sTextSelection, sStr );
713 pEditView->SetSelection( aSel );
715 else
717 //autocorrect procedures
718 SbxArray* pArr = rModulWindow.GetSbModule()->GetMethods().get();
719 for (sal_uInt32 i = 0; i < pArr->Count(); ++i)
721 if (pArr->Get(i)->GetName().equalsIgnoreAsciiCase(sStr))
723 sStr = pArr->Get(i)->GetName(); //if found, get the correct case
724 pEditEngine->ReplaceText( sTextSelection, sStr );
725 pEditView->SetSelection( aSel );
726 return;
732 TextSelection EditorWindow::GetLastHighlightPortionTextSelection() const
733 {//creates a text selection from the highlight portion on the cursor
734 const sal_uInt32 nLine = GetEditView()->GetSelection().GetStart().GetPara();
735 const sal_Int32 nIndex = GetEditView()->GetSelection().GetStart().GetIndex();
736 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
737 std::vector<HighlightPortion> aPortions;
738 aHighlighter.getHighlightPortions( aLine, aPortions );
740 assert(!aPortions.empty());
741 HighlightPortion& r = aPortions.back();
742 if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
743 {//cursor is not standing at the end of the line
744 for (auto const& portion : aPortions)
746 if( portion.nEnd == nIndex )
748 r = portion;
749 break;
754 if( aPortions.empty() )
755 return TextSelection();
757 std::u16string_view sStr = aLine.subView( r.nBegin, r.nEnd - r.nBegin );
758 TextPaM aStart( nLine, r.nBegin );
759 TextPaM aEnd( nLine, r.nBegin + sStr.size() );
760 return TextSelection( aStart, aEnd );
763 void EditorWindow::HandleAutoCloseParen()
765 TextSelection aSel = GetEditView()->GetSelection();
766 const sal_uInt32 nLine = aSel.GetStart().GetPara();
767 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
769 if( aLine.getLength() > 0 && aLine[aSel.GetEnd().GetIndex()-1] != '(' )
771 GetEditView()->InsertText(")");
772 //leave the cursor on its place: inside the parenthesis
773 TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
774 GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
778 void EditorWindow::HandleAutoCloseDoubleQuotes()
780 TextSelection aSel = GetEditView()->GetSelection();
781 const sal_uInt32 nLine = aSel.GetStart().GetPara();
782 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
784 std::vector<HighlightPortion> aPortions;
785 aHighlighter.getHighlightPortions( aLine, aPortions );
787 if( aPortions.empty() )
788 return;
790 if( aLine.getLength() > 0 && !aLine.endsWith("\"") && (aPortions.back().tokenType != TokenType::String) )
792 GetEditView()->InsertText("\"");
793 //leave the cursor on its place: inside the two double quotes
794 TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
795 GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
799 void EditorWindow::HandleProcedureCompletion()
802 TextSelection aSel = GetEditView()->GetSelection();
803 const sal_uInt32 nLine = aSel.GetStart().GetPara();
804 OUString aLine( pEditEngine->GetText( nLine ) );
806 OUString sProcType;
807 OUString sProcName;
808 bool bFoundName = GetProcedureName(aLine, sProcType, sProcName);
809 if (!bFoundName)
810 return;
812 OUString sText("\nEnd ");
813 aSel = GetEditView()->GetSelection();
814 if( sProcType.equalsIgnoreAsciiCase("function") )
815 sText += "Function\n";
816 if( sProcType.equalsIgnoreAsciiCase("sub") )
817 sText += "Sub\n";
819 if( nLine+1 == pEditEngine->GetParagraphCount() )
821 pEditView->InsertText( sText );//append to the end
822 GetEditView()->SetSelection(aSel);
824 else
826 for( sal_uInt32 i = nLine+1; i < pEditEngine->GetParagraphCount(); ++i )
827 {//searching forward for end token, or another sub/function definition
828 OUString aCurrLine = pEditEngine->GetText( i );
829 std::vector<HighlightPortion> aCurrPortions;
830 aHighlighter.getHighlightPortions( aCurrLine, aCurrPortions );
832 if( aCurrPortions.size() >= 3 )
833 {//at least 3 tokens: (sub|function) whitespace identifier...
834 HighlightPortion& r = aCurrPortions.front();
835 std::u16string_view sStr = aCurrLine.subView(r.nBegin, r.nEnd - r.nBegin);
837 if( r.tokenType == TokenType::Keywords )
839 if( o3tl::equalsIgnoreAsciiCase(sStr, u"sub") || o3tl::equalsIgnoreAsciiCase(sStr, u"function") )
841 pEditView->InsertText( sText );//append to the end
842 GetEditView()->SetSelection(aSel);
843 break;
845 if( o3tl::equalsIgnoreAsciiCase(sStr, u"end") )
846 break;
853 bool EditorWindow::GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const
855 std::vector<HighlightPortion> aPortions;
856 aHighlighter.getHighlightPortions(rLine, aPortions);
858 if( aPortions.empty() )
859 return false;
861 bool bFoundType = false;
862 bool bFoundName = false;
864 for (auto const& portion : aPortions)
866 std::u16string_view sTokStr = rLine.substr(portion.nBegin, portion.nEnd - portion.nBegin);
868 if( portion.tokenType == TokenType::Keywords && ( o3tl::equalsIgnoreAsciiCase(sTokStr, u"sub")
869 || o3tl::equalsIgnoreAsciiCase(sTokStr, u"function")) )
871 rProcType = sTokStr;
872 bFoundType = true;
874 if( portion.tokenType == TokenType::Identifier && bFoundType )
876 rProcName = sTokStr;
877 bFoundName = true;
878 break;
882 if( !bFoundType || !bFoundName )
883 return false;// no sub/function keyword or there is no identifier
885 return true;
889 void EditorWindow::HandleCodeCompletion()
891 rModulWindow.UpdateModule();
892 rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse(aCodeCompleteCache);
893 TextSelection aSel = GetEditView()->GetSelection();
894 const sal_uInt32 nLine = aSel.GetStart().GetPara();
895 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
896 std::vector< OUString > aVect; //vector to hold the base variable+methods for the nested reflection
898 std::vector<HighlightPortion> aPortions;
899 aLine = aLine.copy(0, aSel.GetEnd().GetIndex());
900 aHighlighter.getHighlightPortions( aLine, aPortions );
901 if( aPortions.empty() )
902 return;
904 //use the syntax highlighter to grab out nested reflection calls, eg. aVar.aMethod("aa").aOtherMethod ..
905 for( std::vector<HighlightPortion>::reverse_iterator i(
906 aPortions.rbegin());
907 i != aPortions.rend(); ++i)
909 if( i->tokenType == TokenType::Whitespace ) // a whitespace: stop; if there is no ws, it goes to the beginning of the line
910 break;
911 if( i->tokenType == TokenType::Identifier || i->tokenType == TokenType::Keywords ) // extract the identifiers(methods, base variable)
912 /* an example: Dim aLocVar2 as com.sun.star.beans.PropertyValue
913 * here, aLocVar2.Name, and PropertyValue's Name field is treated as a keyword(?!)
914 * */
915 aVect.insert( aVect.begin(), aLine.copy(i->nBegin, i->nEnd - i->nBegin) );
918 if( aVect.empty() )//nothing to do
919 return;
921 OUString sBaseName = aVect[aVect.size()-1];//variable name
922 OUString sVarType = aCodeCompleteCache.GetVarType( sBaseName );
924 if( !sVarType.isEmpty() && CodeCompleteOptions::IsAutoCorrectOn() )
925 {//correct variable name, if autocorrection on
926 const OUString& sStr = aCodeCompleteCache.GetCorrectCaseVarName( sBaseName, GetActualSubName(nLine) );
927 if( !sStr.isEmpty() )
929 TextPaM aStart(nLine, aSel.GetStart().GetIndex() - sStr.getLength() );
930 TextSelection sTextSelection(aStart, TextPaM(nLine, aSel.GetStart().GetIndex()));
931 pEditEngine->ReplaceText( sTextSelection, sStr );
932 pEditView->SetSelection( aSel );
936 UnoTypeCodeCompletetor aTypeCompletor( aVect, sVarType );
938 if( !aTypeCompletor.CanCodeComplete() )
939 return;
941 std::vector< OUString > aEntryVect;//entries to be inserted into the list
942 std::vector< OUString > aFieldVect = aTypeCompletor.GetXIdlClassFields();//fields
943 aEntryVect.insert(aEntryVect.end(), aFieldVect.begin(), aFieldVect.end() );
944 if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
945 {// if extended types on, reflect classes, else just the structs (XIdlClass without methods)
946 std::vector< OUString > aMethVect = aTypeCompletor.GetXIdlClassMethods();//methods
947 aEntryVect.insert(aEntryVect.end(), aMethVect.begin(), aMethVect.end() );
949 if( !aEntryVect.empty() )
950 SetupAndShowCodeCompleteWnd( aEntryVect, aSel );
953 void EditorWindow::SetupAndShowCodeCompleteWnd( const std::vector< OUString >& aEntryVect, TextSelection aSel )
955 // clear the listbox
956 pCodeCompleteWnd->ClearListBox();
957 // fill the listbox
958 for(const auto & l : aEntryVect)
960 pCodeCompleteWnd->InsertEntry( l );
962 // show it
963 pCodeCompleteWnd->Show();
964 pCodeCompleteWnd->ResizeAndPositionListBox();
965 pCodeCompleteWnd->SelectFirstEntry();
966 // correct text selection, and set it
967 ++aSel.GetStart().GetIndex();
968 ++aSel.GetEnd().GetIndex();
969 pCodeCompleteWnd->SetTextSelection( aSel );
970 //give the focus to the EditView
971 pEditView->GetWindow()->GrabFocus();
974 void EditorWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
976 if (!pEditEngine) // We need it now at latest
977 CreateEditEngine();
979 pEditView->Paint(rRenderContext, rRect);
982 void EditorWindow::LoseFocus()
984 // tdf#114258 wait until the next event loop cycle to do this so it doesn't
985 // happen during a mouse down/up selection in the treeview whose contents
986 // this may update
987 if (!m_nSetSourceInBasicId)
988 m_nSetSourceInBasicId = Application::PostUserEvent(LINK(this, EditorWindow, SetSourceInBasicHdl));
989 Window::LoseFocus();
992 IMPL_LINK_NOARG(EditorWindow, SetSourceInBasicHdl, void*, void)
994 m_nSetSourceInBasicId = nullptr;
995 SetSourceInBasic();
998 void EditorWindow::SetSourceInBasic()
1000 if ( pEditEngine && pEditEngine->IsModified()
1001 && !GetEditView()->IsReadOnly() ) // Added for #i60626, otherwise
1002 // any read only bug in the text engine could lead to a crash later
1004 if ( !StarBASIC::IsRunning() ) // Not at runtime!
1006 rModulWindow.UpdateModule();
1011 // Returns the position of the last character of any of the following
1012 // EOL char combinations: CR, CR/LF, LF, return -1 if no EOL is found
1013 sal_Int32 searchEOL( std::u16string_view rStr, sal_Int32 fromIndex )
1015 size_t iLF = rStr.find( LINE_SEP, fromIndex );
1016 if( iLF != std::u16string_view::npos )
1017 return iLF;
1019 size_t iCR = rStr.find( LINE_SEP_CR, fromIndex );
1020 return iCR == std::u16string_view::npos ? -1 : iCR;
1023 void EditorWindow::CreateEditEngine()
1025 if (pEditEngine)
1026 return;
1028 pEditEngine.reset(new ExtTextEngine);
1029 pEditView.reset(new TextView(pEditEngine.get(), this));
1030 pEditView->SetAutoIndentMode(true);
1031 pEditEngine->SetUpdateMode(false);
1032 pEditEngine->InsertView(pEditView.get());
1034 ImplSetFont();
1036 aSyntaxIdle.SetInvokeHandler( LINK( this, EditorWindow, SyntaxTimerHdl ) );
1038 bool bWasDoSyntaxHighlight = bDoSyntaxHighlight;
1039 bDoSyntaxHighlight = false; // too slow for large texts...
1040 OUString aOUSource(rModulWindow.GetModule());
1041 sal_Int32 nLines = 0;
1042 sal_Int32 nIndex = -1;
1045 nLines++;
1046 nIndex = searchEOL( aOUSource, nIndex+1 );
1048 while (nIndex >= 0);
1050 // nLines*4: SetText+Formatting+DoHighlight+Formatting
1051 // it could be cut down on one formatting but you would wait even longer
1052 // for the text then if the source code is long...
1053 pProgress.reset(new ProgressInfo(GetShell()->GetViewFrame().GetObjectShell(),
1054 IDEResId(RID_STR_GENERATESOURCE),
1055 nLines * 4));
1056 setTextEngineText(*pEditEngine, aOUSource);
1058 pEditView->SetStartDocPos(Point(0, 0));
1059 pEditView->SetSelection(TextSelection());
1060 rModulWindow.GetBreakPointWindow().GetCurYOffset() = 0;
1061 rModulWindow.GetLineNumberWindow().GetCurYOffset() = 0;
1062 pEditEngine->SetUpdateMode(true);
1063 rModulWindow.PaintImmediately(); // has only been invalidated at UpdateMode = true
1065 pEditView->ShowCursor();
1067 StartListening(*pEditEngine);
1069 aSyntaxIdle.Stop();
1070 bDoSyntaxHighlight = bWasDoSyntaxHighlight;
1072 for (sal_Int32 nLine = 0; nLine < nLines; nLine++)
1073 aSyntaxLineTable.insert(nLine);
1074 ForceSyntaxTimeout();
1076 pProgress.reset();
1078 pEditEngine->SetModified( false );
1079 pEditEngine->EnableUndo( true );
1081 InitScrollBars();
1083 if (SfxBindings* pBindings = GetBindingsPtr())
1085 pBindings->Invalidate(SID_BASICIDE_STAT_POS);
1086 pBindings->Invalidate(SID_BASICIDE_STAT_TITLE);
1089 DBG_ASSERT(rModulWindow.GetBreakPointWindow().GetCurYOffset() == 0, "CreateEditEngine: breakpoints moved?");
1091 // set readonly mode for readonly libraries
1092 ScriptDocument aDocument(rModulWindow.GetDocument());
1093 OUString aOULibName(rModulWindow.GetLibName());
1094 Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY );
1095 if (xModLibContainer.is()
1096 && xModLibContainer->hasByName(aOULibName)
1097 && xModLibContainer->isLibraryReadOnly(aOULibName))
1099 rModulWindow.SetReadOnly(true);
1102 if (aDocument.isDocument() && aDocument.isReadOnly())
1103 rModulWindow.SetReadOnly(true);
1106 void EditorWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1108 TextHint const* pTextHint = dynamic_cast<TextHint const*>(&rHint);
1109 if (!pTextHint)
1110 return;
1112 TextHint const& rTextHint = *pTextHint;
1113 if( rTextHint.GetId() == SfxHintId::TextViewScrolled )
1115 rModulWindow.GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() );
1116 rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1117 rModulWindow.GetBreakPointWindow().DoScroll
1118 ( rModulWindow.GetBreakPointWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1119 rModulWindow.GetLineNumberWindow().DoScroll
1120 ( rModulWindow.GetLineNumberWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1122 else if( rTextHint.GetId() == SfxHintId::TextHeightChanged )
1124 if ( pEditView->GetStartDocPos().Y() )
1126 tools::Long nOutHeight = GetOutputSizePixel().Height();
1127 tools::Long nTextHeight = pEditEngine->GetTextHeight();
1128 if ( nTextHeight < nOutHeight )
1129 pEditView->Scroll( 0, pEditView->GetStartDocPos().Y() );
1131 rModulWindow.GetLineNumberWindow().Invalidate();
1134 SetScrollBarRanges();
1136 else if( rTextHint.GetId() == SfxHintId::TextFormatted )
1139 const tools::Long nWidth = pEditEngine->CalcTextWidth();
1140 if ( nWidth != nCurTextWidth )
1142 nCurTextWidth = nWidth;
1143 rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1) );
1144 rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1146 tools::Long nPrevTextWidth = nCurTextWidth;
1147 nCurTextWidth = pEditEngine->CalcTextWidth();
1148 if ( nCurTextWidth != nPrevTextWidth )
1149 SetScrollBarRanges();
1151 else if( rTextHint.GetId() == SfxHintId::TextParaInserted )
1153 ParagraphInsertedDeleted( rTextHint.GetValue(), true );
1154 DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1156 else if( rTextHint.GetId() == SfxHintId::TextParaRemoved )
1158 ParagraphInsertedDeleted( rTextHint.GetValue(), false );
1160 else if( rTextHint.GetId() == SfxHintId::TextParaContentChanged )
1162 DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1164 else if( rTextHint.GetId() == SfxHintId::TextViewSelectionChanged )
1166 if (SfxBindings* pBindings = GetBindingsPtr())
1168 pBindings->Invalidate( SID_CUT );
1169 pBindings->Invalidate( SID_COPY );
1174 OUString EditorWindow::GetActualSubName( sal_uInt32 nLine )
1176 SbxArrayRef pMethods = rModulWindow.GetSbModule()->GetMethods();
1177 for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
1179 SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i));
1180 if( pMeth )
1182 sal_uInt16 l1,l2;
1183 pMeth->GetLineRange(l1,l2);
1184 if( (l1 <= nLine+1) && (nLine+1 <= l2) )
1186 return pMeth->GetName();
1190 return OUString();
1193 void EditorWindow::SetScrollBarRanges()
1195 // extra method, not InitScrollBars, because for EditEngine events too
1196 if ( !pEditEngine )
1197 return;
1199 rModulWindow.GetEditVScrollBar().SetRange( Range( 0, pEditEngine->GetTextHeight()-1 ) );
1200 rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1 ) );
1203 void EditorWindow::InitScrollBars()
1205 if (!pEditEngine)
1206 return;
1208 SetScrollBarRanges();
1209 Size aOutSz(GetOutputSizePixel());
1210 rModulWindow.GetEditVScrollBar().SetVisibleSize(aOutSz.Height());
1211 rModulWindow.GetEditVScrollBar().SetPageSize(aOutSz.Height() * 8 / 10);
1212 rModulWindow.GetEditVScrollBar().SetLineSize(GetTextHeight());
1213 rModulWindow.GetEditVScrollBar().SetThumbPos(pEditView->GetStartDocPos().Y());
1214 rModulWindow.GetEditVScrollBar().Show();
1216 rModulWindow.GetEditHScrollBar().SetVisibleSize(aOutSz.Width());
1217 rModulWindow.GetEditHScrollBar().SetPageSize(aOutSz.Width() * 8 / 10);
1218 rModulWindow.GetEditHScrollBar().SetLineSize(GetTextWidth( "x" ));
1219 rModulWindow.GetEditHScrollBar().SetThumbPos(pEditView->GetStartDocPos().X());
1220 rModulWindow.GetEditHScrollBar().Show();
1223 void EditorWindow::ImpDoHighlight( sal_uInt32 nLine )
1225 if ( !bDoSyntaxHighlight )
1226 return;
1228 OUString aLine( pEditEngine->GetText( nLine ) );
1229 bool const bWasModified = pEditEngine->IsModified();
1230 pEditEngine->RemoveAttribs( nLine );
1231 std::vector<HighlightPortion> aPortions;
1232 aHighlighter.getHighlightPortions( aLine, aPortions );
1234 for (auto const& portion : aPortions)
1236 Color const aColor = rModulWindow.GetLayout().GetSyntaxColor(portion.tokenType);
1237 pEditEngine->SetAttrib(TextAttribFontColor(aColor), nLine, portion.nBegin, portion.nEnd);
1240 pEditEngine->SetModified(bWasModified);
1243 void EditorWindow::ChangeFontColor( Color aColor )
1245 if (pEditEngine)
1247 vcl::Font aFont(pEditEngine->GetFont());
1248 aFont.SetColor(aColor);
1249 pEditEngine->SetFont(aFont);
1253 void EditorWindow::UpdateSyntaxHighlighting ()
1255 if (pEditEngine)
1257 const sal_uInt32 nCount = pEditEngine->GetParagraphCount();
1258 for (sal_uInt32 i = 0; i < nCount; ++i)
1259 DoDelayedSyntaxHighlight(i);
1263 void EditorWindow::ImplSetFont()
1265 // Get default font name and height defined in the Options dialog
1266 OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString()));
1267 if (sFontName.isEmpty())
1269 vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED,
1270 Application::GetSettings().GetUILanguageTag().getLanguageType(),
1271 GetDefaultFontFlags::NONE, GetOutDev()));
1272 sFontName = aTmpFont.GetFamilyName();
1274 sal_uInt16 nDefaultFontHeight = officecfg::Office::Common::Font::SourceViewFont::FontHeight::get();
1276 // Calculate font size considering zoom level
1277 sal_uInt16 nNewFontHeight = nDefaultFontHeight * (static_cast<float>(nCurrentZoomLevel) / 100);
1278 Size aFontSize(0, nNewFontHeight);
1280 vcl::Font aFont(sFontName, aFontSize);
1281 aFont.SetColor(rModulWindow.GetLayout().GetFontColor());
1282 SetPointFont(*GetOutDev(), aFont); // FIXME RenderContext
1283 aFont = GetFont();
1285 rModulWindow.GetBreakPointWindow().SetFont(aFont);
1286 rModulWindow.GetLineNumberWindow().SetFont(aFont);
1287 rModulWindow.Invalidate();
1289 if (pEditEngine)
1291 bool const bModified = pEditEngine->IsModified();
1292 pEditEngine->SetFont(aFont);
1293 pEditEngine->SetModified(bModified);
1296 // Update controls
1297 if (SfxBindings* pBindings = GetBindingsPtr())
1299 pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM );
1300 pBindings->Invalidate( SID_ATTR_ZOOMSLIDER );
1304 void EditorWindow::SetEditorZoomLevel(sal_uInt16 nNewZoomLevel)
1306 if (nCurrentZoomLevel == nNewZoomLevel)
1307 return;
1309 if (nNewZoomLevel < MIN_ZOOM_LEVEL || nNewZoomLevel > MAX_ZOOM_LEVEL)
1310 return;
1312 nCurrentZoomLevel = nNewZoomLevel;
1313 ImplSetFont();
1316 void EditorWindow::DoSyntaxHighlight( sal_uInt32 nPara )
1318 // because of the DelayedSyntaxHighlight it's possible
1319 // that this line does not exist anymore!
1320 if ( nPara < pEditEngine->GetParagraphCount() )
1322 // unfortunately I'm not sure that exactly this line does Modified()...
1323 if ( pProgress )
1324 pProgress->StepProgress();
1325 ImpDoHighlight( nPara );
1329 void EditorWindow::DoDelayedSyntaxHighlight( sal_uInt32 nPara )
1331 // line is only added to list, processed in TimerHdl
1332 // => don't manipulate breaks while EditEngine is formatting
1333 if ( pProgress )
1334 pProgress->StepProgress();
1336 if ( !bHighlighting && bDoSyntaxHighlight )
1338 if ( bDelayHighlight )
1340 aSyntaxLineTable.insert( nPara );
1341 aSyntaxIdle.Start();
1343 else
1344 DoSyntaxHighlight( nPara );
1348 IMPL_LINK_NOARG(EditorWindow, SyntaxTimerHdl, Timer *, void)
1350 DBG_ASSERT( pEditView, "Not yet a View, but Syntax-Highlight?!" );
1352 bool const bWasModified = pEditEngine->IsModified();
1353 //pEditEngine->SetUpdateMode(false);
1355 bHighlighting = true;
1356 for (auto const& syntaxLine : aSyntaxLineTable)
1358 DoSyntaxHighlight(syntaxLine);
1361 // #i45572#
1362 if ( pEditView )
1363 pEditView->ShowCursor( false );
1365 pEditEngine->SetModified( bWasModified );
1367 aSyntaxLineTable.clear();
1368 bHighlighting = false;
1371 void EditorWindow::ParagraphInsertedDeleted( sal_uInt32 nPara, bool bInserted )
1373 if ( pProgress )
1374 pProgress->StepProgress();
1376 if ( !bInserted && ( nPara == TEXT_PARA_ALL ) )
1378 rModulWindow.GetBreakPoints().reset();
1379 rModulWindow.GetBreakPointWindow().Invalidate();
1380 rModulWindow.GetLineNumberWindow().Invalidate();
1382 else
1384 rModulWindow.GetBreakPoints().AdjustBreakPoints( static_cast<sal_uInt16>(nPara)+1, bInserted );
1386 tools::Long nLineHeight = GetTextHeight();
1387 Size aSz = rModulWindow.GetBreakPointWindow().GetOutDev()->GetOutputSize();
1388 tools::Rectangle aInvRect( Point( 0, 0 ), aSz );
1389 tools::Long nY = nPara*nLineHeight - rModulWindow.GetBreakPointWindow().GetCurYOffset();
1390 aInvRect.SetTop( nY );
1391 rModulWindow.GetBreakPointWindow().Invalidate( aInvRect );
1393 Size aLnSz(rModulWindow.GetLineNumberWindow().GetWidth(),
1394 GetOutputSizePixel().Height() - 2 * DWBORDER);
1395 rModulWindow.GetLineNumberWindow().SetPosSizePixel(Point(DWBORDER + 19, DWBORDER), aLnSz);
1396 rModulWindow.GetLineNumberWindow().Invalidate();
1400 void EditorWindow::CreateProgress( const OUString& rText, sal_uInt32 nRange )
1402 DBG_ASSERT( !pProgress, "ProgressInfo exists already" );
1403 pProgress.reset(new ProgressInfo(
1404 GetShell()->GetViewFrame().GetObjectShell(),
1405 rText,
1406 nRange
1410 void EditorWindow::DestroyProgress()
1412 pProgress.reset();
1415 void EditorWindow::ForceSyntaxTimeout()
1417 aSyntaxIdle.Stop();
1418 aSyntaxIdle.Invoke();
1421 FactoryFunction EditorWindow::GetUITestFactory() const
1423 return EditorWindowUIObject::create;
1427 // BreakPointWindow
1429 BreakPointWindow::BreakPointWindow (vcl::Window* pParent, ModulWindow* pModulWindow)
1430 : Window(pParent, WB_BORDER)
1431 , rModulWindow(*pModulWindow)
1432 , nCurYOffset(0) // memorize nCurYOffset and not take it from EditEngine
1433 , nMarkerPos(NoMarker)
1434 , bErrorMarker(false)
1436 setBackgroundColor(GetSettings().GetStyleSettings().GetFieldColor());
1437 SetHelpId(HID_BASICIDE_BREAKPOINTWINDOW);
1440 void BreakPointWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1442 if (SyncYOffset())
1443 return;
1445 Size const aOutSz = rRenderContext.GetOutputSize();
1446 tools::Long const nLineHeight = rRenderContext.GetTextHeight();
1448 Image const aBrk[2] =
1450 GetImage(RID_BMP_BRKDISABLED),
1451 GetImage(RID_BMP_BRKENABLED)
1454 Size const aBmpSz = rRenderContext.PixelToLogic(aBrk[1].GetSizePixel());
1455 Point const aBmpOff((aOutSz.Width() - aBmpSz.Width()) / 2,
1456 (nLineHeight - aBmpSz.Height()) / 2);
1458 for (size_t i = 0, n = GetBreakPoints().size(); i < n; ++i)
1460 BreakPoint& rBrk = GetBreakPoints().at(i);
1461 sal_uInt16 const nLine = rBrk.nLine - 1;
1462 size_t const nY = nLine*nLineHeight - nCurYOffset;
1463 rRenderContext.DrawImage(Point(0, nY) + aBmpOff, aBrk[rBrk.bEnabled]);
1466 ShowMarker(rRenderContext);
1469 void BreakPointWindow::ShowMarker(vcl::RenderContext& rRenderContext)
1471 if (nMarkerPos == NoMarker)
1472 return;
1474 Size const aOutSz = GetOutDev()->GetOutputSize();
1475 tools::Long const nLineHeight = GetTextHeight();
1477 Image aMarker = GetImage(bErrorMarker ? RID_BMP_ERRORMARKER : RID_BMP_STEPMARKER);
1479 Size aMarkerSz(aMarker.GetSizePixel());
1480 aMarkerSz = rRenderContext.PixelToLogic(aMarkerSz);
1481 Point aMarkerOff(0, 0);
1482 aMarkerOff.setX( (aOutSz.Width() - aMarkerSz.Width()) / 2 );
1483 aMarkerOff.setY( (nLineHeight - aMarkerSz.Height()) / 2 );
1485 tools::Long nY = nMarkerPos * nLineHeight - nCurYOffset;
1486 Point aPos(0, nY);
1487 aPos += aMarkerOff;
1489 rRenderContext.DrawImage(aPos, aMarker);
1492 void BreakPointWindow::DoScroll( tools::Long nVertScroll )
1494 nCurYOffset -= nVertScroll;
1495 Window::Scroll( 0, nVertScroll );
1498 void BreakPointWindow::SetMarkerPos( sal_uInt16 nLine, bool bError )
1500 if ( SyncYOffset() )
1501 PaintImmediately();
1503 nMarkerPos = nLine;
1504 bErrorMarker = bError;
1505 Invalidate();
1508 void BreakPointWindow::SetNoMarker ()
1510 SetMarkerPos(NoMarker);
1513 BreakPoint* BreakPointWindow::FindBreakPoint( const Point& rMousePos )
1515 size_t nLineHeight = GetTextHeight();
1516 nLineHeight = nLineHeight > 0 ? nLineHeight : 1;
1517 size_t nYPos = rMousePos.Y() + nCurYOffset;
1519 for ( size_t i = 0, n = GetBreakPoints().size(); i < n ; ++i )
1521 BreakPoint& rBrk = GetBreakPoints().at( i );
1522 sal_uInt16 nLine = rBrk.nLine-1;
1523 size_t nY = nLine*nLineHeight;
1524 if ( ( nYPos > nY ) && ( nYPos < ( nY + nLineHeight ) ) )
1525 return &rBrk;
1527 return nullptr;
1530 void BreakPointWindow::MouseButtonDown( const MouseEvent& rMEvt )
1532 if ( rMEvt.GetClicks() == 2 )
1534 Point aMousePos( PixelToLogic( rMEvt.GetPosPixel() ) );
1535 tools::Long nLineHeight = GetTextHeight();
1536 if(nLineHeight)
1538 tools::Long nYPos = aMousePos.Y() + nCurYOffset;
1539 tools::Long nLine = nYPos / nLineHeight + 1;
1540 rModulWindow.ToggleBreakPoint( static_cast<sal_uInt16>(nLine) );
1541 Invalidate();
1546 void BreakPointWindow::Command( const CommandEvent& rCEvt )
1548 if ( rCEvt.GetCommand() != CommandEventId::ContextMenu )
1549 return;
1551 Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) );
1552 tools::Rectangle aRect(aPos, Size(1, 1));
1553 weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
1555 std::unique_ptr<weld::Builder> xUIBuilder(Application::CreateBuilder(pPopupParent, "modules/BasicIDE/ui/breakpointmenus.ui"));
1557 Point aEventPos( PixelToLogic( aPos ) );
1558 BreakPoint* pBrk = rCEvt.IsMouseEvent() ? FindBreakPoint( aEventPos ) : nullptr;
1559 if ( pBrk )
1561 // test if break point is enabled...
1562 std::unique_ptr<weld::Menu> xBrkPropMenu = xUIBuilder->weld_menu("breakmenu");
1563 xBrkPropMenu->set_active("active", pBrk->bEnabled);
1564 OUString sCommand = xBrkPropMenu->popup_at_rect(pPopupParent, aRect);
1565 if (sCommand == "active")
1567 pBrk->bEnabled = !pBrk->bEnabled;
1568 rModulWindow.UpdateBreakPoint( *pBrk );
1569 Invalidate();
1571 else if (sCommand == "properties")
1573 BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1574 aBrkDlg.SetCurrentBreakPoint( *pBrk );
1575 aBrkDlg.run();
1576 Invalidate();
1579 else
1581 std::unique_ptr<weld::Menu> xBrkListMenu = xUIBuilder->weld_menu("breaklistmenu");
1582 OUString sCommand = xBrkListMenu->popup_at_rect(pPopupParent, aRect);
1583 if (sCommand == "manage")
1585 BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1586 aBrkDlg.run();
1587 Invalidate();
1592 bool BreakPointWindow::SyncYOffset()
1594 TextView* pView = rModulWindow.GetEditView();
1595 if ( pView )
1597 tools::Long nViewYOffset = pView->GetStartDocPos().Y();
1598 if ( nCurYOffset != nViewYOffset )
1600 nCurYOffset = nViewYOffset;
1601 Invalidate();
1602 return true;
1605 return false;
1608 // virtual
1609 void BreakPointWindow::DataChanged(DataChangedEvent const & rDCEvt)
1611 Window::DataChanged(rDCEvt);
1612 if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
1613 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
1615 Color aColor(GetSettings().GetStyleSettings().GetFieldColor());
1616 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
1617 if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFieldColor())
1619 setBackgroundColor(aColor);
1620 Invalidate();
1625 void BreakPointWindow::setBackgroundColor(Color aColor)
1627 SetBackground(Wallpaper(aColor));
1630 namespace {
1632 struct WatchItem
1634 OUString maName;
1635 OUString maDisplayName;
1636 SbxObjectRef mpObject;
1637 std::vector<OUString> maMemberList;
1639 SbxDimArrayRef mpArray;
1640 int nDimLevel; // 0 = Root
1641 int nDimCount;
1642 std::vector<sal_Int32> vIndices;
1644 WatchItem* mpArrayParentItem;
1646 explicit WatchItem (OUString aName):
1647 maName(std::move(aName)),
1648 nDimLevel(0),
1649 nDimCount(0),
1650 mpArrayParentItem(nullptr)
1653 void clearWatchItem ()
1655 maMemberList.clear();
1658 WatchItem* GetRootItem();
1659 SbxDimArray* GetRootArray();
1664 WatchWindow::WatchWindow(Layout* pParent)
1665 : DockingWindow(pParent, "modules/BasicIDE/ui/dockingwatch.ui", "DockingWatch")
1666 , m_nUpdateWatchesId(nullptr)
1668 m_xTitleArea = m_xBuilder->weld_container("titlearea");
1670 nVirtToolBoxHeight = m_xTitleArea->get_preferred_size().Height();
1672 m_xTitle = m_xBuilder->weld_label("title");
1673 m_xTitle->set_label(IDEResId(RID_STR_REMOVEWATCH));
1675 m_xEdit = m_xBuilder->weld_entry("edit");
1676 m_xRemoveWatchButton = m_xBuilder->weld_button("remove");
1677 m_xTreeListBox = m_xBuilder->weld_tree_view("treeview");
1679 m_xEdit->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1680 m_xEdit->set_help_id(HID_BASICIDE_WATCHWINDOW_EDIT);
1681 m_xEdit->set_size_request(LogicToPixel(Size(80, 0), MapMode(MapUnit::MapAppFont)).Width(), -1);
1682 m_xEdit->connect_activate(LINK( this, WatchWindow, ActivateHdl));
1683 m_xEdit->connect_key_press(LINK( this, WatchWindow, KeyInputHdl));
1684 m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1686 m_xRemoveWatchButton->set_sensitive(false);
1687 m_xRemoveWatchButton->connect_clicked(LINK( this, WatchWindow, ButtonHdl));
1688 m_xRemoveWatchButton->set_help_id(HID_BASICIDE_REMOVEWATCH);
1689 m_xRemoveWatchButton->set_tooltip_text(IDEResId(RID_STR_REMOVEWATCHTIP));
1691 m_xTreeListBox->set_help_id(HID_BASICIDE_WATCHWINDOW_LIST);
1692 m_xTreeListBox->connect_editing(LINK(this, WatchWindow, EditingEntryHdl),
1693 LINK(this, WatchWindow, EditedEntryHdl));
1694 m_xTreeListBox->connect_changed( LINK( this, WatchWindow, TreeListHdl ) );
1695 m_xTreeListBox->connect_expanding(LINK(this, WatchWindow, RequestingChildrenHdl));
1697 // VarTabWidth, ValueTabWidth, TypeTabWidth
1698 std::vector<int> aWidths { 220, 100, 1250 };
1699 std::vector<bool> aEditables { false, true, false };
1700 m_xTreeListBox->set_column_fixed_widths(aWidths);
1701 m_xTreeListBox->set_column_editables(aEditables);
1703 SetText(IDEResId(RID_STR_WATCHNAME));
1705 SetHelpId( HID_BASICIDE_WATCHWINDOW );
1707 // make watch window keyboard accessible
1708 GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1711 WatchWindow::~WatchWindow()
1713 disposeOnce();
1716 void WatchWindow::dispose()
1718 if (m_nUpdateWatchesId)
1720 Application::RemoveUserEvent(m_nUpdateWatchesId);
1721 m_nUpdateWatchesId = nullptr;
1724 // Destroy user data
1725 m_xTreeListBox->all_foreach([this](weld::TreeIter& rEntry){
1726 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
1727 delete pItem;
1728 return false;
1731 m_xTitle.reset();
1732 m_xEdit.reset();
1733 m_xRemoveWatchButton.reset();
1734 m_xTitleArea.reset();
1735 m_xTreeListBox.reset();
1736 GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1737 DockingWindow::dispose();
1740 void WatchWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1742 lcl_DrawIDEWindowFrame(this, rRenderContext);
1745 void WatchWindow::Resize()
1747 Size aSz = GetOutputSizePixel();
1748 Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1750 if ( aBoxSz.Width() < 4 )
1751 aBoxSz.setWidth( 0 );
1752 if ( aBoxSz.Height() < 4 )
1753 aBoxSz.setHeight( 0 );
1755 m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1757 Invalidate();
1760 WatchItem* WatchItem::GetRootItem()
1762 WatchItem* pItem = mpArrayParentItem;
1763 while( pItem )
1765 if( pItem->mpArray.is() )
1766 break;
1767 pItem = pItem->mpArrayParentItem;
1769 return pItem;
1772 SbxDimArray* WatchItem::GetRootArray()
1774 WatchItem* pRootItem = GetRootItem();
1775 SbxDimArray* pRet = nullptr;
1776 if( pRootItem )
1777 pRet = pRootItem->mpArray.get();
1778 return pRet;
1781 void WatchWindow::AddWatch( const OUString& rVName )
1783 OUString aVar, aIndex;
1784 lcl_SeparateNameAndIndex( rVName, aVar, aIndex );
1785 WatchItem* pWatchItem = new WatchItem(aVar);
1787 OUString sId(weld::toId(pWatchItem));
1788 std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
1789 m_xTreeListBox->insert(nullptr, -1, &aVar, &sId, nullptr, nullptr, false, xRet.get());
1790 m_xTreeListBox->set_text(*xRet, "", 1);
1791 m_xTreeListBox->set_text(*xRet, "", 2);
1793 m_xTreeListBox->set_cursor(*xRet);
1794 m_xTreeListBox->select(*xRet);
1795 m_xTreeListBox->scroll_to_row(*xRet);
1796 m_xRemoveWatchButton->set_sensitive(true);
1798 UpdateWatches(false);
1801 void WatchWindow::RemoveSelectedWatch()
1803 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeListBox->make_iterator();
1804 bool bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1805 if (bEntry)
1807 m_xTreeListBox->remove(*xEntry);
1808 bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1809 if (bEntry)
1810 m_xEdit->set_text(weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xEntry))->maName);
1811 else
1812 m_xEdit->set_text(OUString());
1813 if ( !m_xTreeListBox->n_children() )
1814 m_xRemoveWatchButton->set_sensitive(false);
1818 IMPL_STATIC_LINK_NOARG(WatchWindow, ButtonHdl, weld::Button&, void)
1820 if (SfxDispatcher* pDispatcher = GetDispatcher())
1821 pDispatcher->Execute(SID_BASICIDE_REMOVEWATCH);
1824 IMPL_LINK_NOARG(WatchWindow, TreeListHdl, weld::TreeView&, void)
1826 std::unique_ptr<weld::TreeIter> xCurEntry = m_xTreeListBox->make_iterator();
1827 bool bCurEntry = m_xTreeListBox->get_cursor(xCurEntry.get());
1828 if (!bCurEntry)
1829 return;
1830 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xCurEntry));
1831 if (!pItem)
1832 return;
1833 m_xEdit->set_text(pItem->maName);
1836 IMPL_LINK_NOARG(WatchWindow, ActivateHdl, weld::Entry&, bool)
1838 OUString aCurText(m_xEdit->get_text());
1839 if (!aCurText.isEmpty())
1841 AddWatch(aCurText);
1842 m_xEdit->select_region(0, -1);
1844 return true;
1847 IMPL_LINK(WatchWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1849 bool bHandled = false;
1851 sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode();
1852 if (nKeyCode == KEY_ESCAPE)
1854 m_xEdit->set_text(OUString());
1855 bHandled = true;
1858 return bHandled;
1861 // StackWindow
1862 StackWindow::StackWindow(Layout* pParent)
1863 : DockingWindow(pParent, "modules/BasicIDE/ui/dockingstack.ui", "DockingStack")
1865 m_xTitle = m_xBuilder->weld_label("title");
1866 m_xTitle->set_label(IDEResId(RID_STR_STACK));
1868 m_xTitle->set_size_request(-1, nVirtToolBoxHeight); // so the two title areas are the same height
1870 m_xTreeListBox = m_xBuilder->weld_tree_view("stack");
1872 m_xTreeListBox->set_help_id(HID_BASICIDE_STACKWINDOW_LIST);
1873 m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_STACKNAME));
1874 m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1875 m_xTreeListBox->append_text(OUString());
1877 SetText(IDEResId(RID_STR_STACKNAME));
1879 SetHelpId( HID_BASICIDE_STACKWINDOW );
1881 // make stack window keyboard accessible
1882 GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1885 StackWindow::~StackWindow()
1887 disposeOnce();
1890 void StackWindow::dispose()
1892 GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1893 m_xTitle.reset();
1894 m_xTreeListBox.reset();
1895 DockingWindow::dispose();
1898 void StackWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1900 lcl_DrawIDEWindowFrame(this, rRenderContext);
1903 void StackWindow::Resize()
1905 Size aSz = GetOutputSizePixel();
1906 Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1908 if ( aBoxSz.Width() < 4 )
1909 aBoxSz.setWidth( 0 );
1910 if ( aBoxSz.Height() < 4 )
1911 aBoxSz.setHeight( 0 );
1913 m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1915 Invalidate();
1918 void StackWindow::UpdateCalls()
1920 m_xTreeListBox->freeze();
1921 m_xTreeListBox->clear();
1923 if (StarBASIC::IsRunning())
1925 ErrCode eOld = SbxBase::GetError();
1926 m_xTreeListBox->set_selection_mode(SelectionMode::Single);
1928 sal_Int32 nScope = 0;
1929 SbMethod* pMethod = StarBASIC::GetActiveMethod( nScope );
1930 while ( pMethod )
1932 OUStringBuffer aEntry( OUString::number(nScope ));
1933 if ( aEntry.getLength() < 2 )
1934 aEntry.insert(0, " ");
1935 aEntry.append(": " + pMethod->GetName());
1936 SbxArray* pParams = pMethod->GetParameters();
1937 SbxInfo* pInfo = pMethod->GetInfo();
1938 if ( pParams )
1940 aEntry.append("(");
1941 // 0 is the sub's name...
1942 for (sal_uInt32 nParam = 1; nParam < pParams->Count(); nParam++)
1944 SbxVariable* pVar = pParams->Get(nParam);
1945 assert(pVar && "Parameter?!");
1946 if ( !pVar->GetName().isEmpty() )
1948 aEntry.append(pVar->GetName());
1950 else if ( pInfo )
1952 assert(nParam <= std::numeric_limits<sal_uInt16>::max());
1953 const SbxParamInfo* pParam = pInfo->GetParam( sal::static_int_cast<sal_uInt16>(nParam) );
1954 if ( pParam )
1956 aEntry.append(pParam->aName);
1959 aEntry.append("=");
1960 SbxDataType eType = pVar->GetType();
1961 if( eType & SbxARRAY )
1963 aEntry.append("...");
1965 else if( eType != SbxOBJECT )
1967 aEntry.append(pVar->GetOUString());
1969 if (nParam < (pParams->Count() - 1))
1971 aEntry.append(", ");
1974 aEntry.append(")");
1976 m_xTreeListBox->append_text(aEntry.makeStringAndClear());
1977 nScope++;
1978 pMethod = StarBASIC::GetActiveMethod( nScope );
1981 SbxBase::ResetError();
1982 if( eOld != ERRCODE_NONE )
1983 SbxBase::SetError( eOld );
1985 else
1987 m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1988 m_xTreeListBox->append_text(OUString());
1991 m_xTreeListBox->thaw();
1994 ComplexEditorWindow::ComplexEditorWindow( ModulWindow* pParent ) :
1995 Window( pParent, WB_3DLOOK | WB_CLIPCHILDREN ),
1996 aBrkWindow(VclPtr<BreakPointWindow>::Create(this, pParent)),
1997 aLineNumberWindow(VclPtr<LineNumberWindow>::Create(this, pParent)),
1998 aEdtWindow(VclPtr<EditorWindow>::Create(this, pParent)),
1999 aEWVScrollBar(VclPtr<ScrollAdaptor>::Create(this, false)),
2000 aEWHScrollBar(VclPtr<ScrollAdaptor>::Create(this, true))
2002 aEdtWindow->Show();
2003 aBrkWindow->Show();
2005 aEWVScrollBar->SetLineSize(nScrollLine);
2006 aEWVScrollBar->SetPageSize(nScrollPage);
2007 aEWVScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2008 aEWVScrollBar->Show();
2010 aEWHScrollBar->SetLineSize(nScrollLine);
2011 aEWHScrollBar->SetPageSize(nScrollPage);
2012 aEWHScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2013 aEWHScrollBar->Show();
2016 ComplexEditorWindow::~ComplexEditorWindow()
2018 disposeOnce();
2021 void ComplexEditorWindow::dispose()
2023 aBrkWindow.disposeAndClear();
2024 aLineNumberWindow.disposeAndClear();
2025 aEdtWindow.disposeAndClear();
2026 aEWVScrollBar.disposeAndClear();
2027 aEWHScrollBar.disposeAndClear();
2028 vcl::Window::dispose();
2031 void ComplexEditorWindow::Resize()
2033 Size aOutSz = GetOutputSizePixel();
2034 Size aSz(aOutSz);
2035 aSz.AdjustWidth( -(2*DWBORDER) );
2036 aSz.AdjustHeight( -(2*DWBORDER) );
2037 tools::Long nBrkWidth = 20;
2038 tools::Long nSBWidth = aEWVScrollBar->GetSizePixel().Width();
2039 tools::Long nSBHeight = aEWHScrollBar->GetSizePixel().Height();
2041 Size aBrkSz(nBrkWidth, aSz.Height() - nSBHeight);
2043 if (aLineNumberWindow->IsVisible())
2045 Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height() - nSBHeight);
2046 Size aEWSz(aSz.Width() - nBrkWidth - aLineNumberWindow->GetWidth() - nSBWidth, aSz.Height() - nSBHeight);
2047 aBrkWindow->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBrkSz);
2048 aLineNumberWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aLnSz);
2049 aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth + aLnSz.Width(), DWBORDER), aEWSz);
2051 else
2053 Size aEWSz(aSz.Width() - nBrkWidth - nSBWidth, aSz.Height() - nSBHeight);
2054 aBrkWindow->SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz );
2055 aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aEWSz);
2058 aEWVScrollBar->SetPosSizePixel(Point(aOutSz.Width() - DWBORDER - nSBWidth, DWBORDER),
2059 Size(nSBWidth, aSz.Height() - nSBHeight));
2060 aEWHScrollBar->SetPosSizePixel(Point(DWBORDER, aOutSz.Height() - DWBORDER - nSBHeight),
2061 Size(aSz.Width() - nSBWidth, nSBHeight));
2064 IMPL_LINK_NOARG(ComplexEditorWindow, ScrollHdl, weld::Scrollbar&, void)
2066 if (aEdtWindow->GetEditView())
2068 tools::Long nXDiff = aEdtWindow->GetEditView()->GetStartDocPos().X() - aEWHScrollBar->GetThumbPos();
2069 tools::Long nYDiff = aEdtWindow->GetEditView()->GetStartDocPos().Y() - aEWVScrollBar->GetThumbPos();
2070 aEdtWindow->GetEditView()->Scroll(nXDiff, nYDiff);
2071 aBrkWindow->DoScroll( nYDiff );
2072 aLineNumberWindow->DoScroll( nYDiff );
2073 aEdtWindow->GetEditView()->ShowCursor(false);
2074 aEWVScrollBar->SetThumbPos( aEdtWindow->GetEditView()->GetStartDocPos().Y() );
2078 void ComplexEditorWindow::DataChanged(DataChangedEvent const & rDCEvt)
2080 Window::DataChanged(rDCEvt);
2081 if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
2082 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
2084 Color aColor(GetSettings().GetStyleSettings().GetFaceColor());
2085 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
2086 if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFaceColor())
2088 SetBackground(Wallpaper(aColor));
2089 Invalidate();
2094 void ComplexEditorWindow::SetLineNumberDisplay(bool b)
2096 aLineNumberWindow->Show(b);
2097 Resize();
2100 uno::Reference< awt::XVclWindowPeer >
2101 EditorWindow::GetComponentInterface(bool bCreate)
2103 uno::Reference< awt::XVclWindowPeer > xPeer(
2104 Window::GetComponentInterface(false));
2105 if (!xPeer.is() && bCreate)
2107 // Make sure edit engine and view are available:
2108 if (!pEditEngine)
2109 CreateEditEngine();
2111 xPeer = createTextWindowPeer(*GetEditView());
2112 SetComponentInterface(xPeer);
2114 return xPeer;
2117 static sal_uInt32 getCorrectedPropCount(SbxArray* p)
2119 sal_uInt32 nPropCount = p->Count();
2120 if (nPropCount >= 3 && p->Get(nPropCount - 1)->GetName().equalsIgnoreAsciiCase("Dbg_Methods")
2121 && p->Get(nPropCount - 2)->GetName().equalsIgnoreAsciiCase("Dbg_Properties")
2122 && p->Get(nPropCount - 3)->GetName().equalsIgnoreAsciiCase("Dbg_SupportedInterfaces"))
2124 nPropCount -= 3;
2126 return nPropCount;
2129 IMPL_LINK(WatchWindow, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool)
2131 if( !StarBASIC::IsRunning() )
2132 return true;
2134 if (m_xTreeListBox->iter_has_child(rParent))
2135 return true;
2137 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rParent));
2138 std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
2140 SbxDimArray* pArray = pItem->mpArray.get();
2141 SbxDimArray* pRootArray = pItem->GetRootArray();
2142 bool bArrayIsRootArray = false;
2143 if( !pArray && pRootArray )
2145 pArray = pRootArray;
2146 bArrayIsRootArray = true;
2149 SbxObject* pObj = pItem->mpObject.get();
2150 if( pObj )
2152 createAllObjectProperties( pObj );
2153 SbxArray* pProps = pObj->GetProperties();
2154 const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2155 pItem->maMemberList.reserve(nPropCount);
2157 for( sal_uInt32 i = 0 ; i < nPropCount ; ++i )
2159 SbxVariable* pVar = pProps->Get(i);
2161 pItem->maMemberList.push_back(pVar->GetName());
2162 OUString const& rName = pItem->maMemberList.back();
2164 WatchItem* pWatchItem = new WatchItem(rName);
2165 OUString sId(weld::toId(pWatchItem));
2167 m_xTreeListBox->insert(&rParent, -1, &rName, &sId, nullptr, nullptr, false, xRet.get());
2168 m_xTreeListBox->set_text(*xRet, "", 1);
2169 m_xTreeListBox->set_text(*xRet, "", 2);
2172 if (nPropCount > 0 && !m_nUpdateWatchesId)
2174 m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2177 else if( pArray )
2179 sal_uInt16 nElementCount = 0;
2181 // Loop through indices of current level
2182 int nParentLevel = bArrayIsRootArray ? pItem->nDimLevel : 0;
2183 int nThisLevel = nParentLevel + 1;
2184 sal_Int32 nMin, nMax;
2185 if (pArray->GetDim(nThisLevel, nMin, nMax))
2187 for (sal_Int32 i = nMin; i <= nMax; i++)
2189 WatchItem* pChildItem = new WatchItem(pItem->maName);
2191 // Copy data and create name
2193 OUStringBuffer aIndexStr = "(";
2194 pChildItem->mpArrayParentItem = pItem;
2195 pChildItem->nDimLevel = nThisLevel;
2196 pChildItem->nDimCount = pItem->nDimCount;
2197 pChildItem->vIndices.resize(pChildItem->nDimCount);
2198 sal_Int32 j;
2199 for (j = 0; j < nParentLevel; j++)
2201 sal_Int32 n = pChildItem->vIndices[j] = pItem->vIndices[j];
2202 aIndexStr.append( OUString::number(n) + "," );
2204 pChildItem->vIndices[nParentLevel] = i;
2205 aIndexStr.append( OUString::number(i) + ")" );
2207 OUString aDisplayName;
2208 WatchItem* pArrayRootItem = pChildItem->GetRootItem();
2209 if (pArrayRootItem && pArrayRootItem->mpArrayParentItem)
2210 aDisplayName = pItem->maDisplayName;
2211 else
2212 aDisplayName = pItem->maName;
2213 aDisplayName += aIndexStr;
2214 pChildItem->maDisplayName = aDisplayName;
2216 OUString sId(weld::toId(pChildItem));
2218 m_xTreeListBox->insert(&rParent, -1, &aDisplayName, &sId, nullptr, nullptr, false,
2219 xRet.get());
2220 m_xTreeListBox->set_text(*xRet, "", 1);
2221 m_xTreeListBox->set_text(*xRet, "", 2);
2223 nElementCount++;
2226 if (nElementCount > 0 && !m_nUpdateWatchesId)
2228 m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2232 return true;
2235 IMPL_LINK_NOARG(WatchWindow, ExecuteUpdateWatches, void*, void)
2237 m_nUpdateWatchesId = nullptr;
2238 UpdateWatches();
2241 SbxBase* WatchWindow::ImplGetSBXForEntry(const weld::TreeIter& rEntry, bool& rbArrayElement)
2243 SbxBase* pSBX = nullptr;
2244 rbArrayElement = false;
2246 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2247 OUString aVName( pItem->maName );
2249 std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeListBox->make_iterator(&rEntry);
2250 bool bParentEntry = m_xTreeListBox->iter_parent(*xParentEntry);
2251 WatchItem* pParentItem = bParentEntry ? weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xParentEntry)) : nullptr;
2252 if( pParentItem )
2254 SbxObject* pObj = pParentItem->mpObject.get();
2255 SbxDimArray* pArray;
2256 if( pObj )
2258 pSBX = pObj->Find( aVName, SbxClassType::DontCare );
2259 if (SbxVariable const* pVar = IsSbxVariable(pSBX))
2261 // Force getting value
2262 SbxValues aRes;
2263 aRes.eType = SbxVOID;
2264 pVar->Get( aRes );
2267 // Array?
2268 else if( (pArray = pItem->GetRootArray()) != nullptr )
2270 rbArrayElement = true;
2271 if( pParentItem->nDimLevel + 1 == pParentItem->nDimCount )
2272 pSBX = pArray->Get(pItem->vIndices.empty() ? nullptr : &*pItem->vIndices.begin());
2275 else
2277 pSBX = StarBASIC::FindSBXInCurrentScope( aVName );
2279 return pSBX;
2282 IMPL_LINK(WatchWindow, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
2284 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rIter));
2286 bool bEdit = false;
2287 if (StarBASIC::IsRunning() && StarBASIC::GetActiveMethod() && !SbxBase::IsError())
2289 // No out of scope entries
2290 bool bArrayElement;
2291 SbxBase* pSbx = ImplGetSBXForEntry(rIter, bArrayElement);
2292 if (IsSbxVariable(pSbx) || bArrayElement)
2294 // Accept no objects and only end nodes of arrays for editing
2295 if( !pItem->mpObject.is() && ( !pItem->mpArray.is() || pItem->nDimLevel == pItem->nDimCount ) )
2297 aEditingRes = m_xTreeListBox->get_text(rIter, 1);
2298 aEditingRes = comphelper::string::strip(aEditingRes, ' ');
2299 bEdit = true;
2304 return bEdit;
2307 IMPL_LINK(WatchWindow, EditedEntryHdl, const IterString&, rIterString, bool)
2309 const weld::TreeIter& rIter = rIterString.first;
2310 OUString aResult = comphelper::string::strip(rIterString.second, ' ');
2312 sal_uInt16 nResultLen = aResult.getLength();
2313 sal_Unicode cFirst = aResult[0];
2314 sal_Unicode cLast = aResult[ nResultLen - 1 ];
2315 if( cFirst == '\"' && cLast == '\"' )
2316 aResult = aResult.copy( 1, nResultLen - 2 );
2318 if (aResult == aEditingRes)
2319 return false;
2321 bool bArrayElement;
2322 SbxBase* pSBX = ImplGetSBXForEntry(rIter, bArrayElement);
2324 if (SbxVariable* pVar = IsSbxVariable(pSBX))
2326 SbxDataType eType = pVar->GetType();
2327 if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxOBJECT)
2328 && ( eType & SbxARRAY ) == 0 )
2330 // If the type is variable, the conversion of the SBX does not matter,
2331 // else the string is converted.
2332 pVar->PutStringExt( aResult );
2336 if ( SbxBase::IsError() )
2338 SbxBase::ResetError();
2341 UpdateWatches();
2343 // The text should never be taken/copied 1:1,
2344 // as the UpdateWatches will be lost
2345 return false;
2348 namespace
2351 void implCollapseModifiedObjectEntry(const weld::TreeIter& rParent, weld::TreeView& rTree)
2353 rTree.collapse_row(rParent);
2355 std::unique_ptr<weld::TreeIter> xDeleteEntry = rTree.make_iterator(&rParent);
2357 while (rTree.iter_children(*xDeleteEntry))
2359 implCollapseModifiedObjectEntry(*xDeleteEntry, rTree);
2361 WatchItem* pItem = weld::fromId<WatchItem*>(rTree.get_id(*xDeleteEntry));
2362 delete pItem;
2363 rTree.remove(*xDeleteEntry);
2364 rTree.copy_iterator(rParent, *xDeleteEntry);
2368 OUString implCreateTypeStringForDimArray( WatchItem* pItem, SbxDataType eType )
2370 OUString aRetStr = getBasicTypeName( eType );
2372 SbxDimArray* pArray = pItem->mpArray.get();
2373 if( !pArray )
2374 pArray = pItem->GetRootArray();
2375 if( pArray )
2377 int nDimLevel = pItem->nDimLevel;
2378 int nDims = pItem->nDimCount;
2379 if( nDimLevel < nDims )
2381 aRetStr += "(";
2382 for( int i = nDimLevel ; i < nDims ; i++ )
2384 sal_Int32 nMin, nMax;
2385 pArray->GetDim(sal::static_int_cast<sal_Int32>(i + 1), nMin, nMax);
2386 aRetStr += OUString::number(nMin) + " to " + OUString::number(nMax);
2387 if( i < nDims - 1 )
2388 aRetStr += ", ";
2390 aRetStr += ")";
2393 return aRetStr;
2396 } // namespace
2398 void WatchWindow::implEnableChildren(const weld::TreeIter& rEntry, bool bEnable)
2400 if (bEnable)
2402 if (!m_xTreeListBox->get_row_expanded(rEntry))
2403 m_xTreeListBox->set_children_on_demand(rEntry, true);
2405 else
2407 assert(!m_xTreeListBox->get_row_expanded(rEntry));
2408 m_xTreeListBox->set_children_on_demand(rEntry, false);
2412 void WatchWindow::UpdateWatches(bool bBasicStopped)
2414 SbMethod* pCurMethod = StarBASIC::GetActiveMethod();
2416 ErrCode eOld = SbxBase::GetError();
2417 setBasicWatchMode( true );
2419 m_xTreeListBox->all_foreach([this, pCurMethod, bBasicStopped](weld::TreeIter& rEntry){
2420 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2421 DBG_ASSERT( !pItem->maName.isEmpty(), "Var? - Must not be empty!" );
2422 OUString aWatchStr;
2423 OUString aTypeStr;
2424 if ( pCurMethod )
2426 bool bCollapse = false;
2427 TriState eEnableChildren = TRISTATE_INDET;
2429 bool bArrayElement;
2430 SbxBase* pSBX = ImplGetSBXForEntry(rEntry, bArrayElement);
2432 // Array? If no end node create type string
2433 if( bArrayElement && pItem->nDimLevel < pItem->nDimCount )
2435 SbxDimArray* pRootArray = pItem->GetRootArray();
2436 SbxDataType eType = pRootArray->GetType();
2437 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2438 eEnableChildren = TRISTATE_TRUE;
2441 if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pSBX))
2443 // extra treatment of arrays
2444 SbxDataType eType = pVar->GetType();
2445 if ( eType & SbxARRAY )
2447 // consider multidimensional arrays!
2448 if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(pVar->GetObject()))
2450 SbxDimArray* pOldArray = pItem->mpArray.get();
2452 bool bArrayChanged = false;
2453 if (pOldArray != nullptr)
2455 // Compare Array dimensions to see if array has changed
2456 // Can be a copy, so comparing pointers does not work
2457 sal_Int32 nOldDims = pOldArray->GetDims();
2458 sal_Int32 nNewDims = pNewArray->GetDims();
2459 if( nOldDims != nNewDims )
2461 bArrayChanged = true;
2463 else
2465 for( sal_Int32 i = 0 ; i < nOldDims ; i++ )
2467 sal_Int32 nOldMin, nOldMax;
2468 sal_Int32 nNewMin, nNewMax;
2470 pOldArray->GetDim(i + 1, nOldMin, nOldMax);
2471 pNewArray->GetDim(i + 1, nNewMin, nNewMax);
2472 if( nOldMin != nNewMin || nOldMax != nNewMax )
2474 bArrayChanged = true;
2475 break;
2480 else
2482 bArrayChanged = true;
2484 eEnableChildren = TRISTATE_TRUE;
2485 // #i37227 Clear always and replace array
2486 if( pNewArray != pOldArray )
2488 pItem->clearWatchItem();
2489 eEnableChildren = TRISTATE_TRUE;
2491 pItem->mpArray = pNewArray;
2492 sal_Int32 nDims = pNewArray->GetDims();
2493 pItem->nDimLevel = 0;
2494 pItem->nDimCount = nDims;
2496 if( bArrayChanged && pOldArray != nullptr )
2498 bCollapse = true;
2500 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2502 else
2504 aWatchStr += "<?>";
2507 else if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
2509 if (SbxObject* pObj = dynamic_cast<SbxObject*>(pVar->GetObject()))
2511 if ( pItem->mpObject.is() && !pItem->maMemberList.empty() )
2513 createAllObjectProperties(pObj);
2514 SbxArray* pProps = pObj->GetProperties();
2515 const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2516 // Check if member list has changed
2517 bCollapse = pItem->maMemberList.size() != nPropCount;
2518 for( sal_uInt32 i = 0 ; !bCollapse && i < nPropCount ; i++ )
2520 SbxVariable* pVar_ = pProps->Get(i);
2521 if( pItem->maMemberList[i] != pVar_->GetName() )
2522 bCollapse = true;
2526 pItem->mpObject = pObj;
2527 eEnableChildren = TRISTATE_TRUE;
2528 aTypeStr = getBasicObjectTypeName( pObj );
2530 else
2532 aWatchStr = "Null";
2533 if( pItem->mpObject.is() )
2535 bCollapse = true;
2536 eEnableChildren = TRISTATE_FALSE;
2540 else
2542 if( pItem->mpObject.is() )
2544 bCollapse = true;
2545 eEnableChildren = TRISTATE_FALSE;
2548 bool bString = (static_cast<sal_uInt8>(eType) == sal_uInt8(SbxSTRING));
2549 OUString aStrStr( "\"" );
2550 if( bString )
2552 aWatchStr += aStrStr;
2554 // tdf#57308 - avoid a second call to retrieve the data
2555 const SbxFlagBits nFlags = pVar->GetFlags();
2556 pVar->SetFlag(SbxFlagBits::NoBroadcast);
2557 aWatchStr += pVar->GetOUString();
2558 pVar->SetFlags(nFlags);
2559 if( bString )
2561 aWatchStr += aStrStr;
2564 if( aTypeStr.isEmpty() )
2566 if( !pVar->IsFixed() )
2568 aTypeStr = "Variant/";
2570 aTypeStr += getBasicTypeName( pVar->GetType() );
2573 else if( !bArrayElement )
2575 aWatchStr += "<Out of Scope>";
2578 if( bCollapse )
2580 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2581 pItem->clearWatchItem();
2584 if (eEnableChildren != TRISTATE_INDET)
2585 implEnableChildren(rEntry, eEnableChildren == TRISTATE_TRUE);
2587 else if( bBasicStopped )
2589 if( pItem->mpObject.is() || pItem->mpArray.is() )
2591 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2592 pItem->mpObject.clear();
2593 pItem->mpArray.clear();
2595 pItem->clearWatchItem();
2598 m_xTreeListBox->set_text(rEntry, aWatchStr, 1);
2599 m_xTreeListBox->set_text(rEntry, aTypeStr, 2);
2601 return false;
2604 SbxBase::ResetError();
2605 if( eOld != ERRCODE_NONE )
2606 SbxBase::SetError( eOld );
2607 setBasicWatchMode( false );
2610 IMPL_LINK_NOARG(CodeCompleteWindow, ImplDoubleClickHdl, weld::TreeView&, bool)
2612 InsertSelectedEntry();
2613 return true;
2616 IMPL_LINK_NOARG(CodeCompleteWindow, ImplSelectHdl, weld::TreeView&, void)
2618 //give back the focus to the parent
2619 pParent->GrabFocus();
2622 TextView* CodeCompleteWindow::GetParentEditView()
2624 return pParent->GetEditView();
2627 void CodeCompleteWindow::InsertSelectedEntry()
2629 OUString sSelectedEntry = m_xListBox->get_selected_text();
2631 if( !aFuncBuffer.isEmpty() )
2633 // if the user typed in something: remove, and insert
2634 GetParentEditView()->SetSelection(pParent->GetLastHighlightPortionTextSelection());
2635 GetParentEditView()->DeleteSelected();
2637 if (!sSelectedEntry.isEmpty())
2639 // if the user selected something
2640 GetParentEditView()->InsertText(sSelectedEntry);
2643 else
2645 if (!sSelectedEntry.isEmpty())
2647 // if the user selected something
2648 GetParentEditView()->InsertText(sSelectedEntry);
2651 HideAndRestoreFocus();
2654 void CodeCompleteWindow::SetMatchingEntries()
2656 for (sal_Int32 i = 0, nEntryCount = m_xListBox->n_children(); i< nEntryCount; ++i)
2658 OUString sEntry = m_xListBox->get_text(i);
2659 if (sEntry.startsWithIgnoreAsciiCase(aFuncBuffer))
2661 m_xListBox->select(i);
2662 break;
2667 IMPL_LINK(CodeCompleteWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
2669 return HandleKeyInput(rKEvt);
2672 bool CodeCompleteWindow::HandleKeyInput( const KeyEvent& rKeyEvt )
2674 bool bHandled = true;
2676 sal_Unicode aChar = rKeyEvt.GetKeyCode().GetCode();
2677 if( (( aChar >= KEY_A ) && ( aChar <= KEY_Z ))
2678 || ((aChar >= KEY_0) && (aChar <= KEY_9)) )
2680 aFuncBuffer.append(rKeyEvt.GetCharCode());
2681 SetMatchingEntries();
2683 else
2685 switch( aChar )
2687 case KEY_POINT:
2688 break;
2689 case KEY_ESCAPE: // hide, do nothing
2690 HideAndRestoreFocus();
2691 break;
2692 case KEY_RIGHT:
2694 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2695 if( aTextSelection.GetEnd().GetPara() != GetTextSelection().GetEnd().GetPara()-1 )
2697 HideAndRestoreFocus();
2699 break;
2701 case KEY_LEFT:
2703 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2704 if( aTextSelection.GetStart().GetIndex()-1 < GetTextSelection().GetStart().GetIndex() )
2705 {//leave the cursor where it is
2706 HideAndRestoreFocus();
2708 break;
2710 case KEY_TAB:
2712 TextSelection aTextSelection = pParent->GetLastHighlightPortionTextSelection();
2713 OUString sTypedText = pParent->GetEditEngine()->GetText(aTextSelection);
2714 if( !aFuncBuffer.isEmpty() )
2716 sal_Int32 nInd = m_xListBox->get_selected_index();
2717 if (nInd != -1)
2719 int nEntryCount = m_xListBox->n_children();
2720 //if there is something selected
2721 bool bFound = false;
2722 for (sal_Int32 i = nInd; i != nEntryCount; ++i)
2724 OUString sEntry = m_xListBox->get_text(i);
2725 if( sEntry.startsWithIgnoreAsciiCase( aFuncBuffer )
2726 && (std::u16string_view(aFuncBuffer) != sTypedText) && (i != nInd) )
2728 m_xListBox->select(i);
2729 bFound = true;
2730 break;
2733 if( !bFound )
2734 SetMatchingEntries();
2736 GetParentEditView()->SetSelection( aTextSelection );
2737 GetParentEditView()->DeleteSelected();
2738 GetParentEditView()->InsertText(m_xListBox->get_selected_text());
2741 break;
2743 case KEY_SPACE:
2744 HideAndRestoreFocus();
2745 break;
2746 case KEY_BACKSPACE: case KEY_DELETE:
2747 if( !aFuncBuffer.isEmpty() )
2749 //if there was something inserted by tab: add it to aFuncBuffer
2750 TextSelection aSel( GetParentEditView()->GetSelection() );
2751 TextPaM aEnd( GetParentEditView()->CursorEndOfLine(GetTextSelection().GetEnd()) );
2752 GetParentEditView()->SetSelection(TextSelection(GetTextSelection().GetStart(), aEnd ) );
2753 OUString aTabInsertedStr( GetParentEditView()->GetSelected() );
2754 GetParentEditView()->SetSelection( aSel );
2756 if( !aTabInsertedStr.isEmpty() && aTabInsertedStr != std::u16string_view(aFuncBuffer) )
2758 aFuncBuffer = aTabInsertedStr;
2760 aFuncBuffer.remove(aFuncBuffer.getLength()-1, 1);
2761 SetMatchingEntries();
2763 else
2765 ClearAndHide();
2766 bHandled = false;
2768 break;
2769 case KEY_RETURN:
2770 InsertSelectedEntry();
2771 break;
2772 case KEY_UP:
2774 int nInd = m_xListBox->get_selected_index();
2775 if (nInd)
2776 m_xListBox->select(nInd - 1);
2777 break;
2779 case KEY_DOWN:
2781 int nInd = m_xListBox->get_selected_index();
2782 if (nInd + 1 < m_xListBox->n_children())
2783 m_xListBox->select(nInd + 1);
2784 break;
2786 default:
2787 bHandled = false;
2788 break;
2792 return bHandled;
2795 void CodeCompleteWindow::HideAndRestoreFocus()
2797 Hide();
2798 pParent->GrabFocus();
2801 CodeCompleteWindow::CodeCompleteWindow(EditorWindow* pPar)
2802 : InterimItemWindow(pPar, "modules/BasicIDE/ui/codecomplete.ui", "CodeComplete")
2803 , pParent(pPar)
2804 , m_xListBox(m_xBuilder->weld_tree_view("treeview"))
2806 m_xListBox->connect_row_activated(LINK(this, CodeCompleteWindow, ImplDoubleClickHdl));
2807 m_xListBox->connect_changed(LINK(this, CodeCompleteWindow, ImplSelectHdl));
2808 m_xListBox->connect_key_press(LINK(this, CodeCompleteWindow, KeyInputHdl));
2809 m_xListBox->make_sorted();
2811 m_xListBox->set_size_request(150, 150); // default, this will adopt the line length
2812 SetSizePixel(m_xContainer->get_preferred_size());
2815 CodeCompleteWindow::~CodeCompleteWindow()
2817 disposeOnce();
2820 void CodeCompleteWindow::dispose()
2822 m_xListBox.reset();
2823 pParent.clear();
2824 InterimItemWindow::dispose();
2827 void CodeCompleteWindow::InsertEntry( const OUString& aStr )
2829 m_xListBox->append_text(aStr);
2832 void CodeCompleteWindow::ClearListBox()
2834 m_xListBox->clear();
2835 aFuncBuffer.setLength(0);
2838 void CodeCompleteWindow::SetTextSelection( const TextSelection& aSel )
2840 m_aTextSelection = aSel;
2843 void CodeCompleteWindow::ResizeAndPositionListBox()
2845 if (m_xListBox->n_children() < 1)
2846 return;
2848 // if there is at least one element inside
2849 // calculate basic position: under the current line
2850 tools::Rectangle aRect = static_cast<TextEngine*>(pParent->GetEditEngine())->PaMtoEditCursor( pParent->GetEditView()->GetSelection().GetEnd() );
2851 tools::Long nViewYOffset = pParent->GetEditView()->GetStartDocPos().Y();
2852 Point aPos = aRect.BottomRight();// this variable will be used later (if needed)
2853 aPos.setY( (aPos.Y() - nViewYOffset) + nBasePad );
2855 // get line count
2856 const sal_uInt16 nLines = static_cast<sal_uInt16>(std::min(6, m_xListBox->n_children()));
2858 m_xListBox->set_size_request(-1, m_xListBox->get_height_rows(nLines));
2860 Size aSize = m_xContainer->get_preferred_size();
2861 //set the size
2862 SetSizePixel( aSize );
2864 //calculate position
2865 const tools::Rectangle aVisArea( pParent->GetEditView()->GetStartDocPos(), pParent->GetOutputSizePixel() ); //the visible area
2866 const Point& aBottomPoint = aVisArea.BottomRight();
2868 if( aVisArea.TopRight().getY() + aPos.getY() + aSize.getHeight() > aBottomPoint.getY() )
2869 {//clipped at the bottom: move it up
2870 const tools::Long& nParentFontHeight = pParent->GetEditEngine()->GetFont().GetFontHeight(); //parent's font (in the IDE): needed for height
2871 aPos.AdjustY( -(aSize.getHeight() + nParentFontHeight + nCursorPad) );
2874 if( aVisArea.TopLeft().getX() + aPos.getX() + aSize.getWidth() > aBottomPoint.getX() )
2875 {//clipped at the right side, move it a bit left
2876 aPos.AdjustX( -(aSize.getWidth() + aVisArea.TopLeft().getX()) );
2878 //set the position
2879 SetPosPixel( aPos );
2882 void CodeCompleteWindow::SelectFirstEntry()
2884 if (m_xListBox->n_children() > 0)
2885 m_xListBox->select(0);
2888 void CodeCompleteWindow::ClearAndHide()
2890 ClearListBox();
2891 HideAndRestoreFocus();
2894 UnoTypeCodeCompletetor::UnoTypeCodeCompletetor( const std::vector< OUString >& aVect, const OUString& sVarType )
2895 : bCanComplete( true )
2897 if( aVect.empty() || sVarType.isEmpty() )
2899 bCanComplete = false;//invalid parameters, nothing to code complete
2900 return;
2905 // Get the base class for reflection:
2906 xClass = css::reflection::theCoreReflection::get(
2907 comphelper::getProcessComponentContext())->forName(sVarType);
2909 catch( const Exception& )
2911 bCanComplete = false;
2912 return;
2915 //start from aVect[1]: aVect[0] is the variable name
2916 bCanComplete = std::none_of(aVect.begin() + 1, aVect.end(), [this](const OUString& rMethName) {
2917 return (!CodeCompleteOptions::IsExtendedTypeDeclaration() || !CheckMethod(rMethName)) && !CheckField(rMethName); });
2920 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassMethods() const
2922 std::vector< OUString > aRetVect;
2923 if( bCanComplete && ( xClass != nullptr ) )
2925 const Sequence< Reference< reflection::XIdlMethod > > aMethods = xClass->getMethods();
2926 for(Reference< reflection::XIdlMethod > const & rMethod : aMethods)
2928 aRetVect.push_back( rMethod->getName() );
2931 return aRetVect;//this is empty when cannot code complete
2934 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassFields() const
2936 std::vector< OUString > aRetVect;
2937 if( bCanComplete && ( xClass != nullptr ) )
2939 const Sequence< Reference< reflection::XIdlField > > aFields = xClass->getFields();
2940 for(Reference< reflection::XIdlField > const & rxField : aFields)
2942 aRetVect.push_back( rxField->getName() );
2945 return aRetVect;//this is empty when cannot code complete
2949 bool UnoTypeCodeCompletetor::CheckField( const OUString& sFieldName )
2950 {// modifies xClass!!!
2952 if ( xClass == nullptr )
2953 return false;
2955 Reference< reflection::XIdlField> xField = xClass->getField( sFieldName );
2956 if( xField != nullptr )
2958 xClass = xField->getType();
2959 if( xClass != nullptr )
2961 return true;
2964 return false;
2967 bool UnoTypeCodeCompletetor::CheckMethod( const OUString& sMethName )
2968 {// modifies xClass!!!
2971 if ( xClass == nullptr )
2972 return false;
2974 Reference< reflection::XIdlMethod> xMethod = xClass->getMethod( sMethName );
2975 if( xMethod != nullptr ) //method OK, check return type
2977 xClass = xMethod->getReturnType();
2978 if( xClass != nullptr )
2980 return true;
2983 return false;
2986 } // namespace basctl
2988 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */