xref: /core/basctl/source/basicide/baside2b.cxx (revision bd6fb0cd)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cassert>
23 #include <string_view>
24 
25 #include <helpids.h>
26 #include <iderid.hxx>
27 #include <strings.hrc>
28 #include <bitmaps.hlst>
29 
30 #include "baside2.hxx"
31 #include "brkdlg.hxx"
32 #include <basidesh.hxx>
33 #include <basobj.hxx>
34 #include <iderdll.hxx>
35 
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 
74 namespace basctl
75 {
76 
77 using namespace ::com::sun::star;
78 using namespace ::com::sun::star::uno;
79 
80 namespace
81 {
82 
83 sal_uInt16 const NoMarker = 0xFFFF;
84 tools::Long const nBasePad = 2;
85 tools::Long const nCursorPad = 5;
86 
87 tools::Long nVirtToolBoxHeight;    // inited in WatchWindow, used in Stackwindow
88 
89 // Returns pBase converted to SbxVariable if valid and is not an SbxMethod.
90 SbxVariable* IsSbxVariable (SbxBase* pBase)
91 {
92     if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pBase))
93         if (!dynamic_cast<SbxMethod*>(pVar))
94             return pVar;
95     return nullptr;
96 }
97 
98 Image GetImage(const OUString& rId)
99 {
100     return Image(StockImage::Yes, rId);
101 }
102 
103 int const nScrollLine = 12;
104 int const nScrollPage = 60;
105 int const DWBORDER = 3;
106 
107 std::u16string_view const cSuffixes = u"%&!#@$";
108 
109 } // namespace
110 
111 
112 /**
113  * Helper functions to get/set text in TextEngine using
114  * the stream interface.
115  *
116  * get/setText() only supports tools Strings limited to 64K).
117  */
118 OUString getTextEngineText (ExtTextEngine& rEngine)
119 {
120     SvMemoryStream aMemStream;
121     aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
122     aMemStream.SetLineDelimiter( LINEEND_LF );
123     rEngine.Write( aMemStream );
124     std::size_t nSize = aMemStream.Tell();
125     OUString aText( static_cast<const char*>(aMemStream.GetData()),
126         nSize, RTL_TEXTENCODING_UTF8 );
127     return aText;
128 }
129 
130 void setTextEngineText (ExtTextEngine& rEngine, std::u16string_view aStr)
131 {
132     rEngine.SetText(OUString());
133     OString aUTF8Str = OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 );
134     SvMemoryStream aMemStream( const_cast<char *>(aUTF8Str.getStr()), aUTF8Str.getLength(),
135         StreamMode::READ );
136     aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
137     aMemStream.SetLineDelimiter( LINEEND_LF );
138     rEngine.Read(aMemStream);
139 }
140 
141 namespace
142 {
143 
144 void lcl_DrawIDEWindowFrame(DockingWindow const * pWin, vcl::RenderContext& rRenderContext)
145 {
146     if (pWin->IsFloatingMode())
147         return;
148 
149     Size aSz(pWin->GetOutputSizePixel());
150     const Color aOldLineColor(rRenderContext.GetLineColor());
151     rRenderContext.SetLineColor(COL_WHITE);
152     // White line on top
153     rRenderContext.DrawLine(Point(0, 0), Point(aSz.Width(), 0));
154     // Black line at bottom
155     rRenderContext.SetLineColor(COL_BLACK);
156     rRenderContext.DrawLine(Point(0, aSz.Height() - 1),
157                             Point(aSz.Width(), aSz.Height() - 1));
158     rRenderContext.SetLineColor(aOldLineColor);
159 }
160 
161 void lcl_SeparateNameAndIndex( const OUString& rVName, OUString& rVar, OUString& rIndex )
162 {
163     rVar = rVName;
164     rIndex.clear();
165     sal_Int32 nIndexStart = rVar.indexOf( '(' );
166     if ( nIndexStart != -1 )
167     {
168         sal_Int32 nIndexEnd = rVar.indexOf( ')', nIndexStart );
169         if (nIndexEnd != -1)
170         {
171             rIndex = rVar.copy(nIndexStart + 1, nIndexEnd - nIndexStart - 1);
172             rVar = rVar.copy(0, nIndexStart);
173             rVar = comphelper::string::stripEnd(rVar, ' ');
174             rIndex = comphelper::string::strip(rIndex, ' ');
175         }
176     }
177 
178     if ( !rVar.isEmpty() )
179     {
180         sal_uInt16 nLastChar = rVar.getLength()-1;
181         if ( cSuffixes.find(rVar[ nLastChar ] ) != std::u16string_view::npos )
182             rVar = rVar.replaceAt( nLastChar, 1, u"" );
183     }
184     if ( !rIndex.isEmpty() )
185     {
186         sal_uInt16 nLastChar = rIndex.getLength()-1;
187         if ( cSuffixes.find(rIndex[ nLastChar ] ) != std::u16string_view::npos )
188             rIndex = rIndex.replaceAt( nLastChar, 1, u"" );
189     }
190 }
191 
192 } // namespace
193 
194 
195 // EditorWindow
196 
197 
198 class EditorWindow::ChangesListener:
199     public cppu::WeakImplHelper< beans::XPropertiesChangeListener >
200 {
201 public:
202     explicit ChangesListener(EditorWindow & editor): editor_(editor) {}
203 
204 private:
205     virtual ~ChangesListener() override {}
206 
207     virtual void SAL_CALL disposing(lang::EventObject const &) override
208     {
209         std::unique_lock g(editor_.mutex_);
210         editor_.notifier_.clear();
211     }
212 
213     virtual void SAL_CALL propertiesChange(
214         Sequence< beans::PropertyChangeEvent > const &) override
215     {
216         SolarMutexGuard g;
217         editor_.ImplSetFont();
218     }
219 
220     EditorWindow & editor_;
221 };
222 
223 class EditorWindow::ProgressInfo : public SfxProgress
224 {
225 public:
226     ProgressInfo (SfxObjectShell* pObjSh, OUString const& rText, sal_uInt32 nRange) :
227         SfxProgress(pObjSh, rText, nRange),
228         nCurState(0)
229     { }
230 
231     void StepProgress ()
232     {
233         SetState(++nCurState);
234     }
235 
236 private:
237     sal_uInt32 nCurState;
238 };
239 
240 EditorWindow::EditorWindow (vcl::Window* pParent, ModulWindow* pModulWindow) :
241     Window(pParent, WB_BORDER),
242     rModulWindow(*pModulWindow),
243     nCurTextWidth(0),
244     m_nSetSourceInBasicId(nullptr),
245     aHighlighter(HighlighterLanguage::Basic),
246     aSyntaxIdle( "basctl EditorWindow aSyntaxIdle" ),
247     bHighlighting(false),
248     bDoSyntaxHighlight(true),
249     bDelayHighlight(true),
250     pCodeCompleteWnd(VclPtr<CodeCompleteWindow>::Create(this))
251 {
252     set_id("EditorWindow");
253     const Wallpaper aBackground(rModulWindow.GetLayout().GetSyntaxBackgroundColor());
254     SetBackground(aBackground);
255     GetWindow(GetWindowType::Border)->SetBackground(aBackground);
256     SetPointer( PointerStyle::Text );
257     SetHelpId( HID_BASICIDE_EDITORWINDOW );
258 
259     listener_ = new ChangesListener(*this);
260     Reference< beans::XMultiPropertySet > n(
261         officecfg::Office::Common::Font::SourceViewFont::get(),
262         UNO_QUERY_THROW);
263     {
264         std::unique_lock g(mutex_);
265         notifier_ = n;
266     }
267 
268     // The zoom level applied to the editor window is the zoom slider value in the shell
269     nCurrentZoomLevel = GetShell()->GetCurrentZoomSliderValue();
270 
271     const Sequence<OUString> aPropertyNames{"FontHeight", "FontName"};
272     n->addPropertiesChangeListener(aPropertyNames, listener_);
273 }
274 
275 
276 EditorWindow::~EditorWindow()
277 {
278     disposeOnce();
279 }
280 
281 void EditorWindow::dispose()
282 {
283     if (m_nSetSourceInBasicId)
284     {
285         Application::RemoveUserEvent(m_nSetSourceInBasicId);
286         m_nSetSourceInBasicId = nullptr;
287     }
288 
289     Reference< beans::XMultiPropertySet > n;
290     {
291         std::unique_lock g(mutex_);
292         n = notifier_;
293     }
294     if (n.is()) {
295         n->removePropertiesChangeListener(listener_);
296     }
297 
298     aSyntaxIdle.Stop();
299 
300     if ( pEditEngine )
301     {
302         EndListening( *pEditEngine );
303         pEditEngine->RemoveView(pEditView.get());
304     }
305     pCodeCompleteWnd.disposeAndClear();
306     vcl::Window::dispose();
307 }
308 
309 OUString EditorWindow::GetWordAtCursor()
310 {
311     OUString aWord;
312 
313     if ( pEditView )
314     {
315         TextEngine* pTextEngine = pEditView->GetTextEngine();
316         if ( pTextEngine )
317         {
318             // check first, if the cursor is at a help URL
319             const TextSelection& rSelection = pEditView->GetSelection();
320             const TextPaM& rSelStart = rSelection.GetStart();
321             const TextPaM& rSelEnd = rSelection.GetEnd();
322             OUString aText = pTextEngine->GetText( rSelEnd.GetPara() );
323             CharClass aClass( ::comphelper::getProcessComponentContext() , Application::GetSettings().GetLanguageTag() );
324             sal_Int32 nSelStart = rSelStart.GetIndex();
325             sal_Int32 nSelEnd = rSelEnd.GetIndex();
326             sal_Int32 nLength = aText.getLength();
327             sal_Int32 nStart = 0;
328             sal_Int32 nEnd = nLength;
329             while ( nStart < nLength )
330             {
331                 OUString aURL( URIHelper::FindFirstURLInText( aText, nStart, nEnd, aClass ) );
332                 INetURLObject aURLObj( aURL );
333                 if ( aURLObj.GetProtocol() == INetProtocol::VndSunStarHelp
334                      && nSelStart >= nStart && nSelStart <= nEnd && nSelEnd >= nStart && nSelEnd <= nEnd )
335                 {
336                     aWord = aURL;
337                     break;
338                 }
339                 nStart = nEnd;
340                 nEnd = nLength;
341             }
342 
343             // Not the selected range, but at the CursorPosition,
344             // if a word is partially selected.
345             if ( aWord.isEmpty() )
346                 aWord = pTextEngine->GetWord( rSelEnd );
347 
348             // Can be empty when full word selected, as Cursor behind it
349             if ( aWord.isEmpty() && pEditView->HasSelection() )
350                 aWord = pTextEngine->GetWord( rSelStart );
351         }
352     }
353 
354     return aWord;
355 }
356 
357 void EditorWindow::RequestHelp( const HelpEvent& rHEvt )
358 {
359     bool bDone = false;
360 
361     // Should have been activated at some point
362     if ( pEditEngine )
363     {
364         if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
365         {
366             OUString aKeyword = GetWordAtCursor();
367             Application::GetHelp()->SearchKeyword( aKeyword );
368             bDone = true;
369         }
370         else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
371         {
372             OUString aHelpText;
373             tools::Rectangle aHelpRect;
374             if ( StarBASIC::IsRunning() )
375             {
376                 Point aWindowPos = rHEvt.GetMousePosPixel();
377                 aWindowPos = ScreenToOutputPixel( aWindowPos );
378                 Point aDocPos = GetEditView()->GetDocPos( aWindowPos );
379                 TextPaM aCursor = GetEditView()->GetTextEngine()->GetPaM(aDocPos);
380                 TextPaM aStartOfWord;
381                 OUString aWord = GetEditView()->GetTextEngine()->GetWord( aCursor, &aStartOfWord );
382                 if ( !aWord.isEmpty() && !comphelper::string::isdigitAsciiString(aWord) )
383                 {
384                     sal_uInt16 nLastChar = aWord.getLength() - 1;
385                     if ( cSuffixes.find(aWord[ nLastChar ] ) != std::u16string_view::npos )
386                         aWord = aWord.replaceAt( nLastChar, 1, u"" );
387                     SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord );
388                     if (SbxVariable const* pVar = IsSbxVariable(pSBX))
389                     {
390                         SbxDataType eType = pVar->GetType();
391                         if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
392                             // might cause a crash e. g. at the selections-object
393                             // Type == Object does not mean pVar == Object!
394                             ; // aHelpText = ((SbxObject*)pVar)->GetClassName();
395                         else if ( eType & SbxARRAY )
396                             ; // aHelpText = "{...}";
397                         else if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxEMPTY) )
398                         {
399                             aHelpText = pVar->GetName();
400                             if ( aHelpText.isEmpty() )     // name is not copied with the passed parameters
401                                 aHelpText = aWord;
402                             aHelpText += "=" + pVar->GetOUString();
403                         }
404                     }
405                     if ( !aHelpText.isEmpty() )
406                     {
407                         tools::Rectangle aStartWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aStartOfWord));
408                         TextPaM aEndOfWord(aStartOfWord.GetPara(), aStartOfWord.GetIndex() + aWord.getLength());
409                         tools::Rectangle aEndWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aEndOfWord));
410                         aHelpRect = aStartWordRect.GetUnion(aEndWordRect);
411 
412                         Point aTopLeft = GetEditView()->GetWindowPos(aHelpRect.TopLeft());
413                         aTopLeft = GetEditView()->GetWindow()->OutputToScreenPixel(aTopLeft);
414 
415                         aHelpRect.SetPos(aTopLeft);
416                     }
417                 }
418             }
419             Help::ShowQuickHelp( this, aHelpRect, aHelpText, QuickHelpFlags::NONE);
420             bDone = true;
421         }
422     }
423 
424     if ( !bDone )
425         Window::RequestHelp( rHEvt );
426 }
427 
428 
429 void EditorWindow::Resize()
430 {
431     // ScrollBars, etc. happens in Adjust...
432     if ( !pEditView )
433         return;
434 
435     tools::Long nVisY = pEditView->GetStartDocPos().Y();
436 
437     pEditView->ShowCursor();
438     Size aOutSz( GetOutputSizePixel() );
439     tools::Long nMaxVisAreaStart = pEditView->GetTextEngine()->GetTextHeight() - aOutSz.Height();
440     if ( nMaxVisAreaStart < 0 )
441         nMaxVisAreaStart = 0;
442     if ( pEditView->GetStartDocPos().Y() > nMaxVisAreaStart )
443     {
444         Point aStartDocPos( pEditView->GetStartDocPos() );
445         aStartDocPos.setY( nMaxVisAreaStart );
446         pEditView->SetStartDocPos( aStartDocPos );
447         pEditView->ShowCursor();
448         rModulWindow.GetBreakPointWindow().GetCurYOffset() = aStartDocPos.Y();
449         rModulWindow.GetLineNumberWindow().GetCurYOffset() = aStartDocPos.Y();
450     }
451     InitScrollBars();
452     if ( nVisY != pEditView->GetStartDocPos().Y() )
453         Invalidate();
454 }
455 
456 
457 void EditorWindow::MouseMove( const MouseEvent &rEvt )
458 {
459     if ( pEditView )
460         pEditView->MouseMove( rEvt );
461 }
462 
463 
464 void EditorWindow::MouseButtonUp( const MouseEvent &rEvt )
465 {
466     if ( pEditView )
467     {
468         pEditView->MouseButtonUp( rEvt );
469         if (SfxBindings* pBindings = GetBindingsPtr())
470         {
471             pBindings->Invalidate( SID_BASICIDE_STAT_POS );
472             pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
473         }
474     }
475 }
476 
477 void EditorWindow::MouseButtonDown( const MouseEvent &rEvt )
478 {
479     GrabFocus();
480     if (!pEditView)
481         return;
482     pEditView->MouseButtonDown(rEvt);
483     if( pCodeCompleteWnd->IsVisible() )
484     {
485         if (pEditView->GetSelection() != pCodeCompleteWnd->GetTextSelection())
486         {
487             //selection changed, code complete window should be hidden
488             pCodeCompleteWnd->HideAndRestoreFocus();
489         }
490     }
491 }
492 
493 void EditorWindow::Command( const CommandEvent& rCEvt )
494 {
495     if ( !pEditView )
496         return;
497 
498     pEditView->Command( rCEvt );
499     if ( ( rCEvt.GetCommand() == CommandEventId::Wheel ) ||
500          ( rCEvt.GetCommand() == CommandEventId::StartAutoScroll ) ||
501          ( rCEvt.GetCommand() == CommandEventId::AutoScroll ) )
502     {
503         const CommandWheelData* pData = rCEvt.GetWheelData();
504 
505         // Check if it is a Ctrl+Wheel zoom command
506         if (pData && pData->IsMod1())
507         {
508             const sal_uInt16 nOldZoom = GetCurrentZoom();
509             sal_uInt16 nNewZoom;
510             if( pData->GetDelta() < 0 )
511                 nNewZoom = std::max<sal_uInt16>(basctl::Shell::GetMinZoom(),
512                                                 basegfx::zoomtools::zoomOut(nOldZoom));
513             else
514                 nNewZoom = std::min<sal_uInt16>(basctl::Shell::GetMaxZoom(),
515                                                 basegfx::zoomtools::zoomIn(nOldZoom));
516             GetShell()->SetGlobalEditorZoomLevel(nNewZoom);
517         }
518         else
519             HandleScrollCommand(rCEvt, &rModulWindow.GetEditHScrollBar(), &rModulWindow.GetEditVScrollBar());
520     }
521     else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) {
522         SfxDispatcher* pDispatcher = GetDispatcher();
523         if ( pDispatcher )
524         {
525             SfxDispatcher::ExecutePopup();
526         }
527         if( pCodeCompleteWnd->IsVisible() ) // hide the code complete window
528             pCodeCompleteWnd->ClearAndHide();
529     }
530 }
531 
532 bool EditorWindow::ImpCanModify()
533 {
534     bool bCanModify = true;
535     if ( StarBASIC::IsRunning() && rModulWindow.GetBasicStatus().bIsRunning )
536     {
537         // If in Trace-mode, abort the trace or refuse input
538         // Remove markers in the modules in Notify at Basic::Stopped
539         std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
540                                                        VclMessageType::Question, VclButtonsType::OkCancel,
541                                                        IDEResId(RID_STR_WILLSTOPPRG)));
542         if (xQueryBox->run() == RET_OK)
543         {
544             rModulWindow.GetBasicStatus().bIsRunning = false;
545             StopBasic();
546         }
547         else
548             bCanModify = false;
549     }
550     return bCanModify;
551 }
552 
553 void EditorWindow::KeyInput( const KeyEvent& rKEvt )
554 {
555     if ( !pEditView )   // Happens in Win95
556         return;
557 
558     bool const bWasModified = pEditEngine->IsModified();
559     // see if there is an accelerator to be processed first
560     SfxViewShell *pVS( SfxViewShell::Current());
561     bool bDone = pVS && pVS->KeyInput( rKEvt );
562 
563     if (pCodeCompleteWnd->IsVisible() && CodeCompleteOptions::IsCodeCompleteOn())
564     {
565         if (pCodeCompleteWnd->HandleKeyInput(rKEvt))
566             return;
567     }
568 
569     if( (rKEvt.GetKeyCode().GetCode() == KEY_SPACE ||
570         rKEvt.GetKeyCode().GetCode() == KEY_TAB ||
571         rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) && CodeCompleteOptions::IsAutoCorrectOn() )
572     {
573         HandleAutoCorrect();
574     }
575 
576     if( rKEvt.GetCharCode() == '"' && CodeCompleteOptions::IsAutoCloseQuotesOn() )
577     {//autoclose double quotes
578         HandleAutoCloseDoubleQuotes();
579     }
580 
581     if( rKEvt.GetCharCode() == '(' && CodeCompleteOptions::IsAutoCloseParenthesisOn() )
582     {//autoclose parenthesis
583         HandleAutoCloseParen();
584     }
585 
586     if( rKEvt.GetKeyCode().GetCode() == KEY_RETURN && CodeCompleteOptions::IsProcedureAutoCompleteOn() )
587     {//autoclose implementation
588        HandleProcedureCompletion();
589     }
590 
591     if( rKEvt.GetKeyCode().GetCode() == KEY_POINT && CodeCompleteOptions::IsCodeCompleteOn() )
592     {
593         HandleCodeCompletion();
594     }
595     if ( !bDone && ( !TextEngine::DoesKeyChangeText( rKEvt ) || ImpCanModify()  ) )
596     {
597         if ( ( rKEvt.GetKeyCode().GetCode() == KEY_TAB ) && !rKEvt.GetKeyCode().IsMod1() &&
598               !rKEvt.GetKeyCode().IsMod2() && !GetEditView()->IsReadOnly() )
599         {
600             TextSelection aSel( pEditView->GetSelection() );
601             if ( aSel.GetStart().GetPara() != aSel.GetEnd().GetPara() )
602             {
603                 bDelayHighlight = false;
604                 if ( !rKEvt.GetKeyCode().IsShift() )
605                     pEditView->IndentBlock();
606                 else
607                     pEditView->UnindentBlock();
608                 bDelayHighlight = true;
609                 bDone = true;
610             }
611         }
612         if ( !bDone )
613             bDone = pEditView->KeyInput( rKEvt );
614     }
615     if ( !bDone )
616     {
617             Window::KeyInput( rKEvt );
618     }
619     else
620     {
621         if (SfxBindings* pBindings = GetBindingsPtr())
622         {
623             pBindings->Invalidate( SID_BASICIDE_STAT_POS );
624             pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
625             if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR )
626             {
627                 pBindings->Update( SID_BASICIDE_STAT_POS );
628                 pBindings->Update( SID_BASICIDE_STAT_TITLE );
629             }
630             if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ||
631                  rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM )
632             {
633                 // If the module is read-only, warn that it can't be edited
634                 if ( rModulWindow.IsReadOnly() )
635                     rModulWindow.ShowReadOnlyInfoBar();
636             }
637             if ( !bWasModified && pEditEngine->IsModified() )
638             {
639                 pBindings->Invalidate( SID_SAVEDOC );
640                 pBindings->Invalidate( SID_DOC_MODIFIED );
641                 pBindings->Invalidate( SID_UNDO );
642             }
643             if ( rKEvt.GetKeyCode().GetCode() == KEY_INSERT )
644                 pBindings->Invalidate( SID_ATTR_INSERT );
645         }
646     }
647 }
648 
649 void EditorWindow::HandleAutoCorrect()
650 {
651     TextSelection aSel = GetEditView()->GetSelection();
652     const sal_uInt32 nLine =  aSel.GetStart().GetPara();
653     const sal_Int32 nIndex =  aSel.GetStart().GetIndex();
654     OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
655     const OUString& sActSubName = GetActualSubName( nLine ); // the actual procedure
656 
657     std::vector<HighlightPortion> aPortions;
658     aHighlighter.getHighlightPortions( aLine, aPortions );
659 
660     if( aPortions.empty() )
661         return;
662 
663     HighlightPortion& r = aPortions.back();
664     if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
665     {//cursor is not standing at the end of the line
666         for (auto const& portion : aPortions)
667         {
668             if( portion.nEnd == nIndex )
669             {
670                 r = portion;
671                 break;
672             }
673         }
674     }
675 
676     OUString sStr = aLine.copy( r.nBegin, r.nEnd - r.nBegin );
677     //if WS or empty string: stop, nothing to do
678     if( ( r.tokenType == TokenType::Whitespace ) || sStr.isEmpty() )
679         return;
680     //create the appropriate TextSelection, and update the cache
681     TextPaM aStart( nLine, r.nBegin );
682     TextPaM aEnd( nLine, r.nBegin + sStr.getLength() );
683     TextSelection sTextSelection( aStart, aEnd );
684     rModulWindow.UpdateModule();
685     rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse( aCodeCompleteCache );
686     // correct the last entered keyword
687     if( r.tokenType == TokenType::Keywords )
688     {
689         sStr = sStr.toAsciiLowerCase();
690         if( !SbModule::GetKeywordCase(sStr).isEmpty() )
691         // if it is a keyword, get its correct case
692             sStr = SbModule::GetKeywordCase(sStr);
693         else
694         // else capitalize first letter/select the correct one, and replace
695             sStr = sStr.replaceAt( 0, 1, OUString(sStr[0]).toAsciiUpperCase() );
696 
697         pEditEngine->ReplaceText( sTextSelection, sStr );
698         pEditView->SetSelection( aSel );
699     }
700     if( r.tokenType != TokenType::Identifier )
701         return;
702 
703 // correct variables
704     if( !aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName ).isEmpty() )
705     {
706         sStr = aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName );
707         pEditEngine->ReplaceText( sTextSelection, sStr );
708         pEditView->SetSelection( aSel );
709     }
710     else
711     {
712         //autocorrect procedures
713         SbxArray* pArr = rModulWindow.GetSbModule()->GetMethods().get();
714         for (sal_uInt32 i = 0; i < pArr->Count(); ++i)
715         {
716             if (pArr->Get(i)->GetName().equalsIgnoreAsciiCase(sStr))
717             {
718                 sStr = pArr->Get(i)->GetName(); //if found, get the correct case
719                 pEditEngine->ReplaceText( sTextSelection, sStr );
720                 pEditView->SetSelection( aSel );
721                 return;
722             }
723         }
724     }
725 }
726 
727 TextSelection EditorWindow::GetLastHighlightPortionTextSelection() const
728 {//creates a text selection from the highlight portion on the cursor
729     const sal_uInt32 nLine = GetEditView()->GetSelection().GetStart().GetPara();
730     const sal_Int32 nIndex = GetEditView()->GetSelection().GetStart().GetIndex();
731     OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
732     std::vector<HighlightPortion> aPortions;
733     aHighlighter.getHighlightPortions( aLine, aPortions );
734 
735     assert(!aPortions.empty());
736     HighlightPortion& r = aPortions.back();
737     if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
738     {//cursor is not standing at the end of the line
739         for (auto const& portion : aPortions)
740         {
741             if( portion.nEnd == nIndex )
742             {
743                 r = portion;
744                 break;
745             }
746         }
747     }
748 
749     if( aPortions.empty() )
750         return TextSelection();
751 
752     std::u16string_view sStr = aLine.subView( r.nBegin, r.nEnd - r.nBegin );
753     TextPaM aStart( nLine, r.nBegin );
754     TextPaM aEnd( nLine, r.nBegin + sStr.size() );
755     return TextSelection( aStart, aEnd );
756 }
757 
758 void EditorWindow::HandleAutoCloseParen()
759 {
760     TextSelection aSel = GetEditView()->GetSelection();
761     const sal_uInt32 nLine =  aSel.GetStart().GetPara();
762     OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
763 
764     if( aLine.getLength() > 0 && aLine[aSel.GetEnd().GetIndex()-1] != '(' )
765     {
766         GetEditView()->InsertText(")");
767         //leave the cursor on its place: inside the parenthesis
768         TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
769         GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
770     }
771 }
772 
773 void EditorWindow::HandleAutoCloseDoubleQuotes()
774 {
775     TextSelection aSel = GetEditView()->GetSelection();
776     const sal_uInt32 nLine =  aSel.GetStart().GetPara();
777     OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
778 
779     std::vector<HighlightPortion> aPortions;
780     aHighlighter.getHighlightPortions( aLine, aPortions );
781 
782     if( aPortions.empty() )
783         return;
784 
785     if( aLine.getLength() > 0 && !aLine.endsWith("\"") && (aPortions.back().tokenType != TokenType::String) )
786     {
787         GetEditView()->InsertText("\"");
788         //leave the cursor on its place: inside the two double quotes
789         TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
790         GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
791     }
792 }
793 
794 void EditorWindow::HandleProcedureCompletion()
795 {
796 
797     TextSelection aSel = GetEditView()->GetSelection();
798     const sal_uInt32 nLine = aSel.GetStart().GetPara();
799     OUString aLine( pEditEngine->GetText( nLine ) );
800 
801     OUString sProcType;
802     OUString sProcName;
803     bool bFoundName = GetProcedureName(aLine, sProcType, sProcName);
804     if (!bFoundName)
805       return;
806 
807     OUString sText("\nEnd ");
808     aSel = GetEditView()->GetSelection();
809     if( sProcType.equalsIgnoreAsciiCase("function") )
810         sText += "Function\n";
811     if( sProcType.equalsIgnoreAsciiCase("sub") )
812         sText += "Sub\n";
813 
814     if( nLine+1 == pEditEngine->GetParagraphCount() )
815     {
816         pEditView->InsertText( sText );//append to the end
817         GetEditView()->SetSelection(aSel);
818     }
819     else
820     {
821         for( sal_uInt32 i = nLine+1; i < pEditEngine->GetParagraphCount(); ++i )
822         {//searching forward for end token, or another sub/function definition
823             OUString aCurrLine = pEditEngine->GetText( i );
824             std::vector<HighlightPortion> aCurrPortions;
825             aHighlighter.getHighlightPortions( aCurrLine, aCurrPortions );
826 
827             if( aCurrPortions.size() >= 3 )
828             {//at least 3 tokens: (sub|function) whitespace identifier...
829                 HighlightPortion& r = aCurrPortions.front();
830                 std::u16string_view sStr = aCurrLine.subView(r.nBegin, r.nEnd - r.nBegin);
831 
832                 if( r.tokenType == TokenType::Keywords )
833                 {
834                     if( o3tl::equalsIgnoreAsciiCase(sStr, u"sub") || o3tl::equalsIgnoreAsciiCase(sStr, u"function") )
835                     {
836                         pEditView->InsertText( sText );//append to the end
837                         GetEditView()->SetSelection(aSel);
838                         break;
839                     }
840                     if( o3tl::equalsIgnoreAsciiCase(sStr, u"end") )
841                         break;
842                 }
843             }
844         }
845     }
846 }
847 
848 bool EditorWindow::GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const
849 {
850     std::vector<HighlightPortion> aPortions;
851     aHighlighter.getHighlightPortions(rLine, aPortions);
852 
853     if( aPortions.empty() )
854         return false;
855 
856     bool bFoundType = false;
857     bool bFoundName = false;
858 
859     for (auto const& portion : aPortions)
860     {
861         std::u16string_view sTokStr = rLine.substr(portion.nBegin, portion.nEnd - portion.nBegin);
862 
863         if( portion.tokenType == TokenType::Keywords && ( o3tl::equalsIgnoreAsciiCase(sTokStr, u"sub")
864             || o3tl::equalsIgnoreAsciiCase(sTokStr, u"function")) )
865         {
866             rProcType = sTokStr;
867             bFoundType = true;
868         }
869         if( portion.tokenType == TokenType::Identifier && bFoundType )
870         {
871             rProcName = sTokStr;
872             bFoundName = true;
873             break;
874         }
875     }
876 
877     if( !bFoundType || !bFoundName )
878         return false;// no sub/function keyword or there is no identifier
879 
880     return true;
881 
882 }
883 
884 void EditorWindow::HandleCodeCompletion()
885 {
886     rModulWindow.UpdateModule();
887     rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse(aCodeCompleteCache);
888     TextSelection aSel = GetEditView()->GetSelection();
889     const sal_uInt32 nLine =  aSel.GetStart().GetPara();
890     OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
891     std::vector< OUString > aVect; //vector to hold the base variable+methods for the nested reflection
892 
893     std::vector<HighlightPortion> aPortions;
894     aLine = aLine.copy(0, aSel.GetEnd().GetIndex());
895     aHighlighter.getHighlightPortions( aLine, aPortions );
896     if( aPortions.empty() )
897         return;
898 
899     //use the syntax highlighter to grab out nested reflection calls, eg. aVar.aMethod("aa").aOtherMethod ..
900     for( std::vector<HighlightPortion>::reverse_iterator i(
901              aPortions.rbegin());
902          i != aPortions.rend(); ++i)
903     {
904         if( i->tokenType == TokenType::Whitespace ) // a whitespace: stop; if there is no ws, it goes to the beginning of the line
905             break;
906         if( i->tokenType == TokenType::Identifier || i->tokenType == TokenType::Keywords ) // extract the identifiers(methods, base variable)
907         /* an example: Dim aLocVar2 as com.sun.star.beans.PropertyValue
908          * here, aLocVar2.Name, and PropertyValue's Name field is treated as a keyword(?!)
909          * */
910             aVect.insert( aVect.begin(), aLine.copy(i->nBegin, i->nEnd - i->nBegin) );
911     }
912 
913     if( aVect.empty() )//nothing to do
914         return;
915 
916     OUString sBaseName = aVect[aVect.size()-1];//variable name
917     OUString sVarType = aCodeCompleteCache.GetVarType( sBaseName );
918 
919     if( !sVarType.isEmpty() && CodeCompleteOptions::IsAutoCorrectOn() )
920     {//correct variable name, if autocorrection on
921         const OUString& sStr = aCodeCompleteCache.GetCorrectCaseVarName( sBaseName, GetActualSubName(nLine) );
922         if( !sStr.isEmpty() )
923         {
924             TextPaM aStart(nLine, aSel.GetStart().GetIndex() - sStr.getLength() );
925             TextSelection sTextSelection(aStart, TextPaM(nLine, aSel.GetStart().GetIndex()));
926             pEditEngine->ReplaceText( sTextSelection, sStr );
927             pEditView->SetSelection( aSel );
928         }
929     }
930 
931     UnoTypeCodeCompletetor aTypeCompletor( aVect, sVarType );
932 
933     if( !aTypeCompletor.CanCodeComplete() )
934         return;
935 
936     std::vector< OUString > aEntryVect;//entries to be inserted into the list
937     std::vector< OUString > aFieldVect = aTypeCompletor.GetXIdlClassFields();//fields
938     aEntryVect.insert(aEntryVect.end(), aFieldVect.begin(), aFieldVect.end() );
939     if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
940     {// if extended types on, reflect classes, else just the structs (XIdlClass without methods)
941         std::vector< OUString > aMethVect = aTypeCompletor.GetXIdlClassMethods();//methods
942         aEntryVect.insert(aEntryVect.end(), aMethVect.begin(), aMethVect.end() );
943     }
944     if( !aEntryVect.empty() )
945         SetupAndShowCodeCompleteWnd( aEntryVect, aSel );
946 }
947 
948 void EditorWindow::SetupAndShowCodeCompleteWnd( const std::vector< OUString >& aEntryVect, TextSelection aSel )
949 {
950     // clear the listbox
951     pCodeCompleteWnd->ClearListBox();
952     // fill the listbox
953     for(const auto & l : aEntryVect)
954     {
955         pCodeCompleteWnd->InsertEntry( l );
956     }
957     // show it
958     pCodeCompleteWnd->Show();
959     pCodeCompleteWnd->ResizeAndPositionListBox();
960     pCodeCompleteWnd->SelectFirstEntry();
961     // correct text selection, and set it
962     ++aSel.GetStart().GetIndex();
963     ++aSel.GetEnd().GetIndex();
964     pCodeCompleteWnd->SetTextSelection( aSel );
965     //give the focus to the EditView
966     pEditView->GetWindow()->GrabFocus();
967 }
968 
969 void EditorWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
970 {
971     if (!pEditEngine)     // We need it now at latest
972         CreateEditEngine();
973 
974     pEditView->Paint(rRenderContext, rRect);
975 }
976 
977 void EditorWindow::LoseFocus()
978 {
979     // tdf#114258 wait until the next event loop cycle to do this so it doesn't
980     // happen during a mouse down/up selection in the treeview whose contents
981     // this may update
982     if (!m_nSetSourceInBasicId)
983         m_nSetSourceInBasicId = Application::PostUserEvent(LINK(this, EditorWindow, SetSourceInBasicHdl));
984     Window::LoseFocus();
985 }
986 
987 IMPL_LINK_NOARG(EditorWindow, SetSourceInBasicHdl, void*, void)
988 {
989     m_nSetSourceInBasicId = nullptr;
990     SetSourceInBasic();
991 }
992 
993 void EditorWindow::SetSourceInBasic()
994 {
995     if ( pEditEngine && pEditEngine->IsModified()
996         && !GetEditView()->IsReadOnly() )   // Added for #i60626, otherwise
997             // any read only bug in the text engine could lead to a crash later
998     {
999         if ( !StarBASIC::IsRunning() ) // Not at runtime!
1000         {
1001             rModulWindow.UpdateModule();
1002         }
1003     }
1004 }
1005 
1006 // Returns the position of the last character of any of the following
1007 // EOL char combinations: CR, CR/LF, LF, return -1 if no EOL is found
1008 sal_Int32 searchEOL( std::u16string_view rStr, sal_Int32 fromIndex )
1009 {
1010     size_t iLF = rStr.find( LINE_SEP, fromIndex );
1011     if( iLF != std::u16string_view::npos )
1012         return iLF;
1013 
1014     size_t iCR = rStr.find( LINE_SEP_CR, fromIndex );
1015     return iCR == std::u16string_view::npos ? -1 : iCR;
1016 }
1017 
1018 void EditorWindow::CreateEditEngine()
1019 {
1020     if (pEditEngine)
1021         return;
1022 
1023     pEditEngine.reset(new ExtTextEngine);
1024     pEditView.reset(new TextView(pEditEngine.get(), this));
1025     pEditView->SetAutoIndentMode(true);
1026     pEditEngine->SetUpdateMode(false);
1027     pEditEngine->InsertView(pEditView.get());
1028 
1029     ImplSetFont();
1030 
1031     aSyntaxIdle.SetInvokeHandler( LINK( this, EditorWindow, SyntaxTimerHdl ) );
1032 
1033     bool bWasDoSyntaxHighlight = bDoSyntaxHighlight;
1034     bDoSyntaxHighlight = false; // too slow for large texts...
1035     OUString aOUSource(rModulWindow.GetModule());
1036     sal_Int32 nLines = 0;
1037     sal_Int32 nIndex = -1;
1038     do
1039     {
1040         nLines++;
1041         nIndex = searchEOL( aOUSource, nIndex+1 );
1042     }
1043     while (nIndex >= 0);
1044 
1045     // nLines*4: SetText+Formatting+DoHighlight+Formatting
1046     // it could be cut down on one formatting but you would wait even longer
1047     // for the text then if the source code is long...
1048     pProgress.reset(new ProgressInfo(GetShell()->GetViewFrame().GetObjectShell(),
1049                                      IDEResId(RID_STR_GENERATESOURCE),
1050                                      nLines * 4));
1051     setTextEngineText(*pEditEngine, aOUSource);
1052 
1053     pEditView->SetStartDocPos(Point(0, 0));
1054     pEditView->SetSelection(TextSelection());
1055     rModulWindow.GetBreakPointWindow().GetCurYOffset() = 0;
1056     rModulWindow.GetLineNumberWindow().GetCurYOffset() = 0;
1057     pEditEngine->SetUpdateMode(true);
1058     rModulWindow.PaintImmediately();   // has only been invalidated at UpdateMode = true
1059 
1060     pEditView->ShowCursor();
1061 
1062     StartListening(*pEditEngine);
1063 
1064     aSyntaxIdle.Stop();
1065     bDoSyntaxHighlight = bWasDoSyntaxHighlight;
1066 
1067     for (sal_Int32 nLine = 0; nLine < nLines; nLine++)
1068         aSyntaxLineTable.insert(nLine);
1069     ForceSyntaxTimeout();
1070 
1071     pProgress.reset();
1072 
1073     pEditEngine->SetModified( false );
1074     pEditEngine->EnableUndo( true );
1075 
1076     InitScrollBars();
1077 
1078     if (SfxBindings* pBindings = GetBindingsPtr())
1079     {
1080         pBindings->Invalidate(SID_BASICIDE_STAT_POS);
1081         pBindings->Invalidate(SID_BASICIDE_STAT_TITLE);
1082     }
1083 
1084     DBG_ASSERT(rModulWindow.GetBreakPointWindow().GetCurYOffset() == 0, "CreateEditEngine: breakpoints moved?");
1085 
1086     // set readonly mode for readonly libraries
1087     ScriptDocument aDocument(rModulWindow.GetDocument());
1088     OUString aOULibName(rModulWindow.GetLibName());
1089     Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY );
1090     if (xModLibContainer.is()
1091      && xModLibContainer->hasByName(aOULibName)
1092      && xModLibContainer->isLibraryReadOnly(aOULibName))
1093     {
1094         rModulWindow.SetReadOnly(true);
1095     }
1096 
1097     if (aDocument.isDocument() && aDocument.isReadOnly())
1098         rModulWindow.SetReadOnly(true);
1099 }
1100 
1101 void EditorWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1102 {
1103     TextHint const* pTextHint = dynamic_cast<TextHint const*>(&rHint);
1104     if (!pTextHint)
1105         return;
1106 
1107     TextHint const& rTextHint = *pTextHint;
1108     if( rTextHint.GetId() == SfxHintId::TextViewScrolled )
1109     {
1110         rModulWindow.GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() );
1111         rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1112         rModulWindow.GetBreakPointWindow().DoScroll
1113             ( rModulWindow.GetBreakPointWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1114         rModulWindow.GetLineNumberWindow().DoScroll
1115             ( rModulWindow.GetLineNumberWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1116     }
1117     else if( rTextHint.GetId() == SfxHintId::TextHeightChanged )
1118     {
1119         if ( pEditView->GetStartDocPos().Y() )
1120         {
1121             tools::Long nOutHeight = GetOutputSizePixel().Height();
1122             tools::Long nTextHeight = pEditEngine->GetTextHeight();
1123             if ( nTextHeight < nOutHeight )
1124                 pEditView->Scroll( 0, pEditView->GetStartDocPos().Y() );
1125 
1126             rModulWindow.GetLineNumberWindow().Invalidate();
1127         }
1128 
1129         SetScrollBarRanges();
1130     }
1131     else if( rTextHint.GetId() == SfxHintId::TextFormatted )
1132     {
1133 
1134         const tools::Long nWidth = pEditEngine->CalcTextWidth();
1135         if ( nWidth != nCurTextWidth )
1136         {
1137             nCurTextWidth = nWidth;
1138             rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1) );
1139             rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1140         }
1141         tools::Long nPrevTextWidth = nCurTextWidth;
1142         nCurTextWidth = pEditEngine->CalcTextWidth();
1143         if ( nCurTextWidth != nPrevTextWidth )
1144             SetScrollBarRanges();
1145     }
1146     else if( rTextHint.GetId() == SfxHintId::TextParaInserted )
1147     {
1148         ParagraphInsertedDeleted( rTextHint.GetValue(), true );
1149         DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1150     }
1151     else if( rTextHint.GetId() == SfxHintId::TextParaRemoved )
1152     {
1153         ParagraphInsertedDeleted( rTextHint.GetValue(), false );
1154     }
1155     else if( rTextHint.GetId() == SfxHintId::TextParaContentChanged )
1156     {
1157         DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1158     }
1159     else if( rTextHint.GetId() == SfxHintId::TextViewSelectionChanged )
1160     {
1161         if (SfxBindings* pBindings = GetBindingsPtr())
1162         {
1163             pBindings->Invalidate( SID_CUT );
1164             pBindings->Invalidate( SID_COPY );
1165         }
1166     }
1167 }
1168 
1169 OUString EditorWindow::GetActualSubName( sal_uInt32 nLine )
1170 {
1171     SbxArrayRef pMethods = rModulWindow.GetSbModule()->GetMethods();
1172     for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
1173     {
1174         SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i));
1175         if( pMeth )
1176         {
1177             sal_uInt16 l1,l2;
1178             pMeth->GetLineRange(l1,l2);
1179             if( (l1 <= nLine+1) && (nLine+1 <= l2) )
1180             {
1181                 return pMeth->GetName();
1182             }
1183         }
1184     }
1185     return OUString();
1186 }
1187 
1188 void EditorWindow::SetScrollBarRanges()
1189 {
1190     // extra method, not InitScrollBars, because for EditEngine events too
1191     if ( !pEditEngine )
1192         return;
1193 
1194     rModulWindow.GetEditVScrollBar().SetRange( Range( 0, pEditEngine->GetTextHeight()-1 ) );
1195     rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1 ) );
1196 }
1197 
1198 void EditorWindow::InitScrollBars()
1199 {
1200     if (!pEditEngine)
1201         return;
1202 
1203     SetScrollBarRanges();
1204     Size aOutSz(GetOutputSizePixel());
1205     rModulWindow.GetEditVScrollBar().SetVisibleSize(aOutSz.Height());
1206     rModulWindow.GetEditVScrollBar().SetPageSize(aOutSz.Height() * 8 / 10);
1207     rModulWindow.GetEditVScrollBar().SetLineSize(GetTextHeight());
1208     rModulWindow.GetEditVScrollBar().SetThumbPos(pEditView->GetStartDocPos().Y());
1209     rModulWindow.GetEditVScrollBar().Show();
1210 
1211     rModulWindow.GetEditHScrollBar().SetVisibleSize(aOutSz.Width());
1212     rModulWindow.GetEditHScrollBar().SetPageSize(aOutSz.Width() * 8 / 10);
1213     rModulWindow.GetEditHScrollBar().SetLineSize(GetTextWidth( "x" ));
1214     rModulWindow.GetEditHScrollBar().SetThumbPos(pEditView->GetStartDocPos().X());
1215     rModulWindow.GetEditHScrollBar().Show();
1216 }
1217 
1218 void EditorWindow::ImpDoHighlight( sal_uInt32 nLine )
1219 {
1220     if ( !bDoSyntaxHighlight )
1221         return;
1222 
1223     OUString aLine( pEditEngine->GetText( nLine ) );
1224     bool const bWasModified = pEditEngine->IsModified();
1225     pEditEngine->RemoveAttribs( nLine );
1226     std::vector<HighlightPortion> aPortions;
1227     aHighlighter.getHighlightPortions( aLine, aPortions );
1228 
1229     for (auto const& portion : aPortions)
1230     {
1231         Color const aColor = rModulWindow.GetLayout().GetSyntaxColor(portion.tokenType);
1232         pEditEngine->SetAttrib(TextAttribFontColor(aColor), nLine, portion.nBegin, portion.nEnd);
1233     }
1234 
1235     pEditEngine->SetModified(bWasModified);
1236 }
1237 
1238 void EditorWindow::ChangeFontColor( Color aColor )
1239 {
1240     if (pEditEngine)
1241     {
1242         vcl::Font aFont(pEditEngine->GetFont());
1243         aFont.SetColor(aColor);
1244         pEditEngine->SetFont(aFont);
1245     }
1246 }
1247 
1248 void EditorWindow::UpdateSyntaxHighlighting ()
1249 {
1250     const sal_uInt32 nCount = pEditEngine->GetParagraphCount();
1251     for (sal_uInt32 i = 0; i < nCount; ++i)
1252         DoDelayedSyntaxHighlight(i);
1253 }
1254 
1255 void EditorWindow::ImplSetFont()
1256 {
1257     // Get default font name and height defined in the Options dialog
1258     OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString()));
1259     if (sFontName.isEmpty())
1260     {
1261         vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED,
1262                                                         Application::GetSettings().GetUILanguageTag().getLanguageType(),
1263                                                         GetDefaultFontFlags::NONE, GetOutDev()));
1264         sFontName = aTmpFont.GetFamilyName();
1265     }
1266     sal_uInt16 nDefaultFontHeight = officecfg::Office::Common::Font::SourceViewFont::FontHeight::get();
1267 
1268     // Calculate font size considering zoom level
1269     sal_uInt16 nNewFontHeight = nDefaultFontHeight * (static_cast<float>(nCurrentZoomLevel) / 100);
1270     Size aFontSize(0, nNewFontHeight);
1271 
1272     vcl::Font aFont(sFontName, aFontSize);
1273     aFont.SetColor(rModulWindow.GetLayout().GetFontColor());
1274     SetPointFont(*GetOutDev(), aFont); // FIXME RenderContext
1275     aFont = GetFont();
1276 
1277     rModulWindow.GetBreakPointWindow().SetFont(aFont);
1278     rModulWindow.GetLineNumberWindow().SetFont(aFont);
1279     rModulWindow.Invalidate();
1280 
1281     if (pEditEngine)
1282     {
1283         bool const bModified = pEditEngine->IsModified();
1284         pEditEngine->SetFont(aFont);
1285         pEditEngine->SetModified(bModified);
1286     }
1287 
1288     // Update controls
1289     if (SfxBindings* pBindings = GetBindingsPtr())
1290     {
1291         pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM );
1292         pBindings->Invalidate( SID_ATTR_ZOOMSLIDER );
1293     }
1294 }
1295 
1296 void EditorWindow::SetEditorZoomLevel(sal_uInt16 nNewZoomLevel)
1297 {
1298     if (nCurrentZoomLevel == nNewZoomLevel)
1299         return;
1300 
1301     if (nNewZoomLevel < MIN_ZOOM_LEVEL || nNewZoomLevel > MAX_ZOOM_LEVEL)
1302         return;
1303 
1304     nCurrentZoomLevel = nNewZoomLevel;
1305     ImplSetFont();
1306 }
1307 
1308 void EditorWindow::DoSyntaxHighlight( sal_uInt32 nPara )
1309 {
1310     // because of the DelayedSyntaxHighlight it's possible
1311     // that this line does not exist anymore!
1312     if ( nPara < pEditEngine->GetParagraphCount() )
1313     {
1314         // unfortunately I'm not sure that exactly this line does Modified()...
1315         if ( pProgress )
1316             pProgress->StepProgress();
1317         ImpDoHighlight( nPara );
1318     }
1319 }
1320 
1321 void EditorWindow::DoDelayedSyntaxHighlight( sal_uInt32 nPara )
1322 {
1323     // line is only added to list, processed in TimerHdl
1324     // => don't manipulate breaks while EditEngine is formatting
1325     if ( pProgress )
1326         pProgress->StepProgress();
1327 
1328     if ( !bHighlighting && bDoSyntaxHighlight )
1329     {
1330         if ( bDelayHighlight )
1331         {
1332             aSyntaxLineTable.insert( nPara );
1333             aSyntaxIdle.Start();
1334         }
1335         else
1336             DoSyntaxHighlight( nPara );
1337     }
1338 }
1339 
1340 IMPL_LINK_NOARG(EditorWindow, SyntaxTimerHdl, Timer *, void)
1341 {
1342     DBG_ASSERT( pEditView, "Not yet a View, but Syntax-Highlight?!" );
1343 
1344     bool const bWasModified = pEditEngine->IsModified();
1345     //pEditEngine->SetUpdateMode(false);
1346 
1347     bHighlighting = true;
1348     for (auto const& syntaxLine : aSyntaxLineTable)
1349     {
1350         DoSyntaxHighlight(syntaxLine);
1351     }
1352 
1353     // #i45572#
1354     if ( pEditView )
1355         pEditView->ShowCursor( false );
1356 
1357     pEditEngine->SetModified( bWasModified );
1358 
1359     aSyntaxLineTable.clear();
1360     bHighlighting = false;
1361 }
1362 
1363 void EditorWindow::ParagraphInsertedDeleted( sal_uInt32 nPara, bool bInserted )
1364 {
1365     if ( pProgress )
1366         pProgress->StepProgress();
1367 
1368     if ( !bInserted && ( nPara == TEXT_PARA_ALL ) )
1369     {
1370         rModulWindow.GetBreakPoints().reset();
1371         rModulWindow.GetBreakPointWindow().Invalidate();
1372         rModulWindow.GetLineNumberWindow().Invalidate();
1373     }
1374     else
1375     {
1376         rModulWindow.GetBreakPoints().AdjustBreakPoints( static_cast<sal_uInt16>(nPara)+1, bInserted );
1377 
1378         tools::Long nLineHeight = GetTextHeight();
1379         Size aSz = rModulWindow.GetBreakPointWindow().GetOutDev()->GetOutputSize();
1380         tools::Rectangle aInvRect( Point( 0, 0 ), aSz );
1381         tools::Long nY = nPara*nLineHeight - rModulWindow.GetBreakPointWindow().GetCurYOffset();
1382         aInvRect.SetTop( nY );
1383         rModulWindow.GetBreakPointWindow().Invalidate( aInvRect );
1384 
1385         Size aLnSz(rModulWindow.GetLineNumberWindow().GetWidth(),
1386                    GetOutputSizePixel().Height() - 2 * DWBORDER);
1387         rModulWindow.GetLineNumberWindow().SetPosSizePixel(Point(DWBORDER + 19, DWBORDER), aLnSz);
1388         rModulWindow.GetLineNumberWindow().Invalidate();
1389     }
1390 }
1391 
1392 void EditorWindow::CreateProgress( const OUString& rText, sal_uInt32 nRange )
1393 {
1394     DBG_ASSERT( !pProgress, "ProgressInfo exists already" );
1395     pProgress.reset(new ProgressInfo(
1396         GetShell()->GetViewFrame().GetObjectShell(),
1397         rText,
1398         nRange
1399     ));
1400 }
1401 
1402 void EditorWindow::DestroyProgress()
1403 {
1404     pProgress.reset();
1405 }
1406 
1407 void EditorWindow::ForceSyntaxTimeout()
1408 {
1409     aSyntaxIdle.Stop();
1410     aSyntaxIdle.Invoke();
1411 }
1412 
1413 FactoryFunction EditorWindow::GetUITestFactory() const
1414 {
1415     return EditorWindowUIObject::create;
1416 }
1417 
1418 
1419 // BreakPointWindow
1420 
1421 BreakPointWindow::BreakPointWindow (vcl::Window* pParent, ModulWindow* pModulWindow)
1422     : Window(pParent, WB_BORDER)
1423     , rModulWindow(*pModulWindow)
1424     , nCurYOffset(0) // memorize nCurYOffset and not take it from EditEngine
1425     , nMarkerPos(NoMarker)
1426     , bErrorMarker(false)
1427 {
1428     setBackgroundColor(GetSettings().GetStyleSettings().GetFieldColor());
1429     SetHelpId(HID_BASICIDE_BREAKPOINTWINDOW);
1430 }
1431 
1432 void BreakPointWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1433 {
1434     if (SyncYOffset())
1435         return;
1436 
1437     Size const aOutSz = rRenderContext.GetOutputSize();
1438     tools::Long const nLineHeight = rRenderContext.GetTextHeight();
1439 
1440     Image const aBrk[2] =
1441     {
1442         GetImage(RID_BMP_BRKDISABLED),
1443         GetImage(RID_BMP_BRKENABLED)
1444     };
1445 
1446     Size const aBmpSz = rRenderContext.PixelToLogic(aBrk[1].GetSizePixel());
1447     Point const aBmpOff((aOutSz.Width() - aBmpSz.Width()) / 2,
1448                         (nLineHeight - aBmpSz.Height()) / 2);
1449 
1450     for (size_t i = 0, n = GetBreakPoints().size(); i < n; ++i)
1451     {
1452         BreakPoint& rBrk = GetBreakPoints().at(i);
1453         sal_uInt16 const nLine = rBrk.nLine - 1;
1454         size_t const nY = nLine*nLineHeight - nCurYOffset;
1455         rRenderContext.DrawImage(Point(0, nY) + aBmpOff, aBrk[rBrk.bEnabled]);
1456     }
1457 
1458     ShowMarker(rRenderContext);
1459 }
1460 
1461 void BreakPointWindow::ShowMarker(vcl::RenderContext& rRenderContext)
1462 {
1463     if (nMarkerPos == NoMarker)
1464         return;
1465 
1466     Size const aOutSz = GetOutDev()->GetOutputSize();
1467     tools::Long const nLineHeight = GetTextHeight();
1468 
1469     Image aMarker = GetImage(bErrorMarker ? RID_BMP_ERRORMARKER : RID_BMP_STEPMARKER);
1470 
1471     Size aMarkerSz(aMarker.GetSizePixel());
1472     aMarkerSz = rRenderContext.PixelToLogic(aMarkerSz);
1473     Point aMarkerOff(0, 0);
1474     aMarkerOff.setX( (aOutSz.Width() - aMarkerSz.Width()) / 2 );
1475     aMarkerOff.setY( (nLineHeight - aMarkerSz.Height()) / 2 );
1476 
1477     tools::Long nY = nMarkerPos * nLineHeight - nCurYOffset;
1478     Point aPos(0, nY);
1479     aPos += aMarkerOff;
1480 
1481     rRenderContext.DrawImage(aPos, aMarker);
1482 }
1483 
1484 void BreakPointWindow::DoScroll( tools::Long nVertScroll )
1485 {
1486     nCurYOffset -= nVertScroll;
1487     Window::Scroll( 0, nVertScroll );
1488 }
1489 
1490 void BreakPointWindow::SetMarkerPos( sal_uInt16 nLine, bool bError )
1491 {
1492     if ( SyncYOffset() )
1493         PaintImmediately();
1494 
1495     nMarkerPos = nLine;
1496     bErrorMarker = bError;
1497     Invalidate();
1498 }
1499 
1500 void BreakPointWindow::SetNoMarker ()
1501 {
1502     SetMarkerPos(NoMarker);
1503 }
1504 
1505 BreakPoint* BreakPointWindow::FindBreakPoint( const Point& rMousePos )
1506 {
1507     size_t nLineHeight = GetTextHeight();
1508     nLineHeight = nLineHeight > 0 ? nLineHeight : 1;
1509     size_t nYPos = rMousePos.Y() + nCurYOffset;
1510 
1511     for ( size_t i = 0, n = GetBreakPoints().size(); i < n ; ++i )
1512     {
1513         BreakPoint& rBrk = GetBreakPoints().at( i );
1514         sal_uInt16 nLine = rBrk.nLine-1;
1515         size_t nY = nLine*nLineHeight;
1516         if ( ( nYPos > nY ) && ( nYPos < ( nY + nLineHeight ) ) )
1517             return &rBrk;
1518     }
1519     return nullptr;
1520 }
1521 
1522 void BreakPointWindow::MouseButtonDown( const MouseEvent& rMEvt )
1523 {
1524     if ( rMEvt.GetClicks() == 2 )
1525     {
1526         Point aMousePos( PixelToLogic( rMEvt.GetPosPixel() ) );
1527         tools::Long nLineHeight = GetTextHeight();
1528         if(nLineHeight)
1529         {
1530             tools::Long nYPos = aMousePos.Y() + nCurYOffset;
1531             tools::Long nLine = nYPos / nLineHeight + 1;
1532             rModulWindow.ToggleBreakPoint( static_cast<sal_uInt16>(nLine) );
1533             Invalidate();
1534         }
1535     }
1536 }
1537 
1538 void BreakPointWindow::Command( const CommandEvent& rCEvt )
1539 {
1540     if ( rCEvt.GetCommand() != CommandEventId::ContextMenu )
1541         return;
1542 
1543     Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) );
1544     tools::Rectangle aRect(aPos, Size(1, 1));
1545     weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
1546 
1547     std::unique_ptr<weld::Builder> xUIBuilder(Application::CreateBuilder(pPopupParent, "modules/BasicIDE/ui/breakpointmenus.ui"));
1548 
1549     Point aEventPos( PixelToLogic( aPos ) );
1550     BreakPoint* pBrk = rCEvt.IsMouseEvent() ? FindBreakPoint( aEventPos ) : nullptr;
1551     if ( pBrk )
1552     {
1553         // test if break point is enabled...
1554         std::unique_ptr<weld::Menu> xBrkPropMenu = xUIBuilder->weld_menu("breakmenu");
1555         xBrkPropMenu->set_active("active", pBrk->bEnabled);
1556         OUString sCommand = xBrkPropMenu->popup_at_rect(pPopupParent, aRect);
1557         if (sCommand == "active")
1558         {
1559             pBrk->bEnabled = !pBrk->bEnabled;
1560             rModulWindow.UpdateBreakPoint( *pBrk );
1561             Invalidate();
1562         }
1563         else if (sCommand == "properties")
1564         {
1565             BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1566             aBrkDlg.SetCurrentBreakPoint( *pBrk );
1567             aBrkDlg.run();
1568             Invalidate();
1569         }
1570     }
1571     else
1572     {
1573         std::unique_ptr<weld::Menu> xBrkListMenu = xUIBuilder->weld_menu("breaklistmenu");
1574         OUString sCommand = xBrkListMenu->popup_at_rect(pPopupParent, aRect);
1575         if (sCommand == "manage")
1576         {
1577             BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1578             aBrkDlg.run();
1579             Invalidate();
1580         }
1581     }
1582 }
1583 
1584 bool BreakPointWindow::SyncYOffset()
1585 {
1586     TextView* pView = rModulWindow.GetEditView();
1587     if ( pView )
1588     {
1589         tools::Long nViewYOffset = pView->GetStartDocPos().Y();
1590         if ( nCurYOffset != nViewYOffset )
1591         {
1592             nCurYOffset = nViewYOffset;
1593             Invalidate();
1594             return true;
1595         }
1596     }
1597     return false;
1598 }
1599 
1600 // virtual
1601 void BreakPointWindow::DataChanged(DataChangedEvent const & rDCEvt)
1602 {
1603     Window::DataChanged(rDCEvt);
1604     if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
1605         && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
1606     {
1607         Color aColor(GetSettings().GetStyleSettings().GetFieldColor());
1608         const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
1609         if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFieldColor())
1610         {
1611             setBackgroundColor(aColor);
1612             Invalidate();
1613         }
1614     }
1615 }
1616 
1617 void BreakPointWindow::setBackgroundColor(Color aColor)
1618 {
1619     SetBackground(Wallpaper(aColor));
1620 }
1621 
1622 namespace {
1623 
1624 struct WatchItem
1625 {
1626     OUString        maName;
1627     OUString        maDisplayName;
1628     SbxObjectRef    mpObject;
1629     std::vector<OUString> maMemberList;
1630 
1631     SbxDimArrayRef  mpArray;
1632     int             nDimLevel;  // 0 = Root
1633     int             nDimCount;
1634     std::vector<sal_Int32> vIndices;
1635 
1636     WatchItem*      mpArrayParentItem;
1637 
1638     explicit WatchItem (OUString aName):
1639         maName(std::move(aName)),
1640         nDimLevel(0),
1641         nDimCount(0),
1642         mpArrayParentItem(nullptr)
1643     { }
1644 
1645     void clearWatchItem ()
1646     {
1647         maMemberList.clear();
1648     }
1649 
1650     WatchItem* GetRootItem();
1651     SbxDimArray* GetRootArray();
1652 };
1653 
1654 }
1655 
1656 WatchWindow::WatchWindow(Layout* pParent)
1657     : DockingWindow(pParent, "modules/BasicIDE/ui/dockingwatch.ui", "DockingWatch")
1658     , m_nUpdateWatchesId(nullptr)
1659 {
1660     m_xTitleArea = m_xBuilder->weld_container("titlearea");
1661 
1662     nVirtToolBoxHeight = m_xTitleArea->get_preferred_size().Height();
1663 
1664     m_xTitle = m_xBuilder->weld_label("title");
1665     m_xTitle->set_label(IDEResId(RID_STR_REMOVEWATCH));
1666 
1667     m_xEdit = m_xBuilder->weld_entry("edit");
1668     m_xRemoveWatchButton = m_xBuilder->weld_button("remove");
1669     m_xTreeListBox = m_xBuilder->weld_tree_view("treeview");
1670 
1671     m_xEdit->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1672     m_xEdit->set_help_id(HID_BASICIDE_WATCHWINDOW_EDIT);
1673     m_xEdit->set_size_request(LogicToPixel(Size(80, 0), MapMode(MapUnit::MapAppFont)).Width(), -1);
1674     m_xEdit->connect_activate(LINK( this, WatchWindow, ActivateHdl));
1675     m_xEdit->connect_key_press(LINK( this, WatchWindow, KeyInputHdl));
1676     m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1677 
1678     m_xRemoveWatchButton->set_sensitive(false);
1679     m_xRemoveWatchButton->connect_clicked(LINK( this, WatchWindow, ButtonHdl));
1680     m_xRemoveWatchButton->set_help_id(HID_BASICIDE_REMOVEWATCH);
1681     m_xRemoveWatchButton->set_tooltip_text(IDEResId(RID_STR_REMOVEWATCHTIP));
1682 
1683     m_xTreeListBox->set_help_id(HID_BASICIDE_WATCHWINDOW_LIST);
1684     m_xTreeListBox->connect_editing(LINK(this, WatchWindow, EditingEntryHdl),
1685                                     LINK(this, WatchWindow, EditedEntryHdl));
1686     m_xTreeListBox->connect_changed( LINK( this, WatchWindow, TreeListHdl ) );
1687     m_xTreeListBox->connect_expanding(LINK(this, WatchWindow, RequestingChildrenHdl));
1688 
1689     // VarTabWidth, ValueTabWidth, TypeTabWidth
1690     std::vector<int> aWidths { 220, 100, 1250 };
1691     std::vector<bool> aEditables { false, true, false };
1692     m_xTreeListBox->set_column_fixed_widths(aWidths);
1693     m_xTreeListBox->set_column_editables(aEditables);
1694 
1695     SetText(IDEResId(RID_STR_WATCHNAME));
1696 
1697     SetHelpId( HID_BASICIDE_WATCHWINDOW );
1698 
1699     // make watch window keyboard accessible
1700     GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1701 }
1702 
1703 WatchWindow::~WatchWindow()
1704 {
1705     disposeOnce();
1706 }
1707 
1708 void WatchWindow::dispose()
1709 {
1710     if (m_nUpdateWatchesId)
1711     {
1712         Application::RemoveUserEvent(m_nUpdateWatchesId);
1713         m_nUpdateWatchesId = nullptr;
1714     }
1715 
1716     // Destroy user data
1717     m_xTreeListBox->all_foreach([this](weld::TreeIter& rEntry){
1718         WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
1719         delete pItem;
1720         return false;
1721     });
1722 
1723     m_xTitle.reset();
1724     m_xEdit.reset();
1725     m_xRemoveWatchButton.reset();
1726     m_xTitleArea.reset();
1727     m_xTreeListBox.reset();
1728     GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1729     DockingWindow::dispose();
1730 }
1731 
1732 void WatchWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1733 {
1734     lcl_DrawIDEWindowFrame(this, rRenderContext);
1735 }
1736 
1737 void WatchWindow::Resize()
1738 {
1739     Size aSz = GetOutputSizePixel();
1740     Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1741 
1742     if ( aBoxSz.Width() < 4 )
1743         aBoxSz.setWidth( 0 );
1744     if ( aBoxSz.Height() < 4 )
1745         aBoxSz.setHeight( 0 );
1746 
1747     m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1748 
1749     Invalidate();
1750 }
1751 
1752 WatchItem* WatchItem::GetRootItem()
1753 {
1754     WatchItem* pItem = mpArrayParentItem;
1755     while( pItem )
1756     {
1757         if( pItem->mpArray.is() )
1758             break;
1759         pItem = pItem->mpArrayParentItem;
1760     }
1761     return pItem;
1762 }
1763 
1764 SbxDimArray* WatchItem::GetRootArray()
1765 {
1766     WatchItem* pRootItem = GetRootItem();
1767     SbxDimArray* pRet = nullptr;
1768     if( pRootItem )
1769         pRet = pRootItem->mpArray.get();
1770     return pRet;
1771 }
1772 
1773 void WatchWindow::AddWatch( const OUString& rVName )
1774 {
1775     OUString aVar, aIndex;
1776     lcl_SeparateNameAndIndex( rVName, aVar, aIndex );
1777     WatchItem* pWatchItem = new WatchItem(aVar);
1778 
1779     OUString sId(weld::toId(pWatchItem));
1780     std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
1781     m_xTreeListBox->insert(nullptr, -1, &aVar, &sId, nullptr, nullptr, false, xRet.get());
1782     m_xTreeListBox->set_text(*xRet, "", 1);
1783     m_xTreeListBox->set_text(*xRet, "", 2);
1784 
1785     m_xTreeListBox->set_cursor(*xRet);
1786     m_xTreeListBox->select(*xRet);
1787     m_xTreeListBox->scroll_to_row(*xRet);
1788     m_xRemoveWatchButton->set_sensitive(true);
1789 
1790     UpdateWatches(false);
1791 }
1792 
1793 void WatchWindow::RemoveSelectedWatch()
1794 {
1795     std::unique_ptr<weld::TreeIter> xEntry = m_xTreeListBox->make_iterator();
1796     bool bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1797     if (bEntry)
1798     {
1799         m_xTreeListBox->remove(*xEntry);
1800         bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1801         if (bEntry)
1802             m_xEdit->set_text(weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xEntry))->maName);
1803         else
1804             m_xEdit->set_text(OUString());
1805         if ( !m_xTreeListBox->n_children() )
1806             m_xRemoveWatchButton->set_sensitive(false);
1807     }
1808 }
1809 
1810 IMPL_STATIC_LINK_NOARG(WatchWindow, ButtonHdl, weld::Button&, void)
1811 {
1812     if (SfxDispatcher* pDispatcher = GetDispatcher())
1813         pDispatcher->Execute(SID_BASICIDE_REMOVEWATCH);
1814 }
1815 
1816 IMPL_LINK_NOARG(WatchWindow, TreeListHdl, weld::TreeView&, void)
1817 {
1818     std::unique_ptr<weld::TreeIter> xCurEntry = m_xTreeListBox->make_iterator();
1819     bool bCurEntry = m_xTreeListBox->get_cursor(xCurEntry.get());
1820     if (!bCurEntry)
1821         return;
1822     WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xCurEntry));
1823     if (!pItem)
1824         return;
1825     m_xEdit->set_text(pItem->maName);
1826 }
1827 
1828 IMPL_LINK_NOARG(WatchWindow, ActivateHdl, weld::Entry&, bool)
1829 {
1830     OUString aCurText(m_xEdit->get_text());
1831     if (!aCurText.isEmpty())
1832     {
1833         AddWatch(aCurText);
1834         m_xEdit->select_region(0, -1);
1835     }
1836     return true;
1837 }
1838 
1839 IMPL_LINK(WatchWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1840 {
1841     bool bHandled = false;
1842 
1843     sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode();
1844     if (nKeyCode == KEY_ESCAPE)
1845     {
1846         m_xEdit->set_text(OUString());
1847         bHandled = true;
1848     }
1849 
1850     return bHandled;
1851 }
1852 
1853 // StackWindow
1854 StackWindow::StackWindow(Layout* pParent)
1855     : DockingWindow(pParent, "modules/BasicIDE/ui/dockingstack.ui", "DockingStack")
1856 {
1857     m_xTitle = m_xBuilder->weld_label("title");
1858     m_xTitle->set_label(IDEResId(RID_STR_STACK));
1859 
1860     m_xTitle->set_size_request(-1, nVirtToolBoxHeight); // so the two title areas are the same height
1861 
1862     m_xTreeListBox = m_xBuilder->weld_tree_view("stack");
1863 
1864     m_xTreeListBox->set_help_id(HID_BASICIDE_STACKWINDOW_LIST);
1865     m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_STACKNAME));
1866     m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1867     m_xTreeListBox->append_text(OUString());
1868 
1869     SetText(IDEResId(RID_STR_STACKNAME));
1870 
1871     SetHelpId( HID_BASICIDE_STACKWINDOW );
1872 
1873     // make stack window keyboard accessible
1874     GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1875 }
1876 
1877 StackWindow::~StackWindow()
1878 {
1879     disposeOnce();
1880 }
1881 
1882 void StackWindow::dispose()
1883 {
1884     GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1885     m_xTitle.reset();
1886     m_xTreeListBox.reset();
1887     DockingWindow::dispose();
1888 }
1889 
1890 void StackWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1891 {
1892     lcl_DrawIDEWindowFrame(this, rRenderContext);
1893 }
1894 
1895 void StackWindow::Resize()
1896 {
1897     Size aSz = GetOutputSizePixel();
1898     Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1899 
1900     if ( aBoxSz.Width() < 4 )
1901         aBoxSz.setWidth( 0 );
1902     if ( aBoxSz.Height() < 4 )
1903         aBoxSz.setHeight( 0 );
1904 
1905     m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1906 
1907     Invalidate();
1908 }
1909 
1910 void StackWindow::UpdateCalls()
1911 {
1912     m_xTreeListBox->freeze();
1913     m_xTreeListBox->clear();
1914 
1915     if (StarBASIC::IsRunning())
1916     {
1917         ErrCode eOld = SbxBase::GetError();
1918         m_xTreeListBox->set_selection_mode(SelectionMode::Single);
1919 
1920         sal_Int32 nScope = 0;
1921         SbMethod* pMethod = StarBASIC::GetActiveMethod( nScope );
1922         while ( pMethod )
1923         {
1924             OUStringBuffer aEntry( OUString::number(nScope ));
1925             if ( aEntry.getLength() < 2 )
1926                 aEntry.insert(0, " ");
1927             aEntry.append(": " + pMethod->GetName());
1928             SbxArray* pParams = pMethod->GetParameters();
1929             SbxInfo* pInfo = pMethod->GetInfo();
1930             if ( pParams )
1931             {
1932                 aEntry.append("(");
1933                 // 0 is the sub's name...
1934                 for (sal_uInt32 nParam = 1; nParam < pParams->Count(); nParam++)
1935                 {
1936                     SbxVariable* pVar = pParams->Get(nParam);
1937                     assert(pVar && "Parameter?!");
1938                     if ( !pVar->GetName().isEmpty() )
1939                     {
1940                         aEntry.append(pVar->GetName());
1941                     }
1942                     else if ( pInfo )
1943                     {
1944                         assert(nParam <= std::numeric_limits<sal_uInt16>::max());
1945                         const SbxParamInfo* pParam = pInfo->GetParam( sal::static_int_cast<sal_uInt16>(nParam) );
1946                         if ( pParam )
1947                         {
1948                             aEntry.append(pParam->aName);
1949                         }
1950                     }
1951                     aEntry.append("=");
1952                     SbxDataType eType = pVar->GetType();
1953                     if( eType & SbxARRAY )
1954                     {
1955                         aEntry.append("...");
1956                     }
1957                     else if( eType != SbxOBJECT )
1958                     {
1959                         aEntry.append(pVar->GetOUString());
1960                     }
1961                     if (nParam < (pParams->Count() - 1))
1962                     {
1963                         aEntry.append(", ");
1964                     }
1965                 }
1966                 aEntry.append(")");
1967             }
1968             m_xTreeListBox->append_text(aEntry.makeStringAndClear());
1969             nScope++;
1970             pMethod = StarBASIC::GetActiveMethod( nScope );
1971         }
1972 
1973         SbxBase::ResetError();
1974         if( eOld != ERRCODE_NONE )
1975             SbxBase::SetError( eOld );
1976     }
1977     else
1978     {
1979         m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1980         m_xTreeListBox->append_text(OUString());
1981     }
1982 
1983     m_xTreeListBox->thaw();
1984 }
1985 
1986 ComplexEditorWindow::ComplexEditorWindow( ModulWindow* pParent ) :
1987     Window( pParent, WB_3DLOOK | WB_CLIPCHILDREN ),
1988     aBrkWindow(VclPtr<BreakPointWindow>::Create(this, pParent)),
1989     aLineNumberWindow(VclPtr<LineNumberWindow>::Create(this, pParent)),
1990     aEdtWindow(VclPtr<EditorWindow>::Create(this, pParent)),
1991     aEWVScrollBar(VclPtr<ScrollAdaptor>::Create(this, false)),
1992     aEWHScrollBar(VclPtr<ScrollAdaptor>::Create(this, true))
1993 {
1994     aEdtWindow->Show();
1995     aBrkWindow->Show();
1996 
1997     aEWVScrollBar->SetLineSize(nScrollLine);
1998     aEWVScrollBar->SetPageSize(nScrollPage);
1999     aEWVScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2000     aEWVScrollBar->Show();
2001 
2002     aEWHScrollBar->SetLineSize(nScrollLine);
2003     aEWHScrollBar->SetPageSize(nScrollPage);
2004     aEWHScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2005     aEWHScrollBar->Show();
2006 }
2007 
2008 ComplexEditorWindow::~ComplexEditorWindow()
2009 {
2010     disposeOnce();
2011 }
2012 
2013 void ComplexEditorWindow::dispose()
2014 {
2015     aBrkWindow.disposeAndClear();
2016     aLineNumberWindow.disposeAndClear();
2017     aEdtWindow.disposeAndClear();
2018     aEWVScrollBar.disposeAndClear();
2019     aEWHScrollBar.disposeAndClear();
2020     vcl::Window::dispose();
2021 }
2022 
2023 void ComplexEditorWindow::Resize()
2024 {
2025     Size aOutSz = GetOutputSizePixel();
2026     Size aSz(aOutSz);
2027     aSz.AdjustWidth( -(2*DWBORDER) );
2028     aSz.AdjustHeight( -(2*DWBORDER) );
2029     tools::Long nBrkWidth = 20;
2030     tools::Long nSBWidth = aEWVScrollBar->GetSizePixel().Width();
2031     tools::Long nSBHeight = aEWHScrollBar->GetSizePixel().Height();
2032 
2033     Size aBrkSz(nBrkWidth, aSz.Height() - nSBHeight);
2034 
2035     if (aLineNumberWindow->IsVisible())
2036     {
2037         Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height() - nSBHeight);
2038         Size aEWSz(aSz.Width() - nBrkWidth - aLineNumberWindow->GetWidth() - nSBWidth, aSz.Height() - nSBHeight);
2039         aBrkWindow->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBrkSz);
2040         aLineNumberWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aLnSz);
2041         aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth + aLnSz.Width(), DWBORDER), aEWSz);
2042     }
2043     else
2044     {
2045         Size aEWSz(aSz.Width() - nBrkWidth - nSBWidth, aSz.Height() - nSBHeight);
2046         aBrkWindow->SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz );
2047         aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aEWSz);
2048     }
2049 
2050     aEWVScrollBar->SetPosSizePixel(Point(aOutSz.Width() - DWBORDER - nSBWidth, DWBORDER),
2051                                    Size(nSBWidth, aSz.Height() - nSBHeight));
2052     aEWHScrollBar->SetPosSizePixel(Point(DWBORDER, aOutSz.Height() - DWBORDER - nSBHeight),
2053                                    Size(aSz.Width() - nSBWidth, nSBHeight));
2054 }
2055 
2056 IMPL_LINK_NOARG(ComplexEditorWindow, ScrollHdl, weld::Scrollbar&, void)
2057 {
2058     if (aEdtWindow->GetEditView())
2059     {
2060         tools::Long nXDiff = aEdtWindow->GetEditView()->GetStartDocPos().X() - aEWHScrollBar->GetThumbPos();
2061         tools::Long nYDiff = aEdtWindow->GetEditView()->GetStartDocPos().Y() - aEWVScrollBar->GetThumbPos();
2062         aEdtWindow->GetEditView()->Scroll(nXDiff, nYDiff);
2063         aBrkWindow->DoScroll( nYDiff );
2064         aLineNumberWindow->DoScroll( nYDiff );
2065         aEdtWindow->GetEditView()->ShowCursor(false);
2066         aEWVScrollBar->SetThumbPos( aEdtWindow->GetEditView()->GetStartDocPos().Y() );
2067     }
2068 }
2069 
2070 void ComplexEditorWindow::DataChanged(DataChangedEvent const & rDCEvt)
2071 {
2072     Window::DataChanged(rDCEvt);
2073     if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
2074         && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
2075     {
2076         Color aColor(GetSettings().GetStyleSettings().GetFaceColor());
2077         const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
2078         if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFaceColor())
2079         {
2080             SetBackground(Wallpaper(aColor));
2081             Invalidate();
2082         }
2083     }
2084 }
2085 
2086 void ComplexEditorWindow::SetLineNumberDisplay(bool b)
2087 {
2088     aLineNumberWindow->Show(b);
2089     Resize();
2090 }
2091 
2092 uno::Reference< awt::XVclWindowPeer >
2093 EditorWindow::GetComponentInterface(bool bCreate)
2094 {
2095     uno::Reference< awt::XVclWindowPeer > xPeer(
2096         Window::GetComponentInterface(false));
2097     if (!xPeer.is() && bCreate)
2098     {
2099         // Make sure edit engine and view are available:
2100         if (!pEditEngine)
2101             CreateEditEngine();
2102 
2103         xPeer = createTextWindowPeer(*GetEditView());
2104         SetComponentInterface(xPeer);
2105     }
2106     return xPeer;
2107 }
2108 
2109 static sal_uInt32 getCorrectedPropCount(SbxArray* p)
2110 {
2111     sal_uInt32 nPropCount = p->Count();
2112     if (nPropCount >= 3 && p->Get(nPropCount - 1)->GetName().equalsIgnoreAsciiCase("Dbg_Methods")
2113         && p->Get(nPropCount - 2)->GetName().equalsIgnoreAsciiCase("Dbg_Properties")
2114         && p->Get(nPropCount - 3)->GetName().equalsIgnoreAsciiCase("Dbg_SupportedInterfaces"))
2115     {
2116         nPropCount -= 3;
2117     }
2118     return nPropCount;
2119 }
2120 
2121 IMPL_LINK(WatchWindow, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool)
2122 {
2123     if( !StarBASIC::IsRunning() )
2124         return true;
2125 
2126     if (m_xTreeListBox->iter_has_child(rParent))
2127         return true;
2128 
2129     WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rParent));
2130     std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
2131 
2132     SbxDimArray* pArray = pItem->mpArray.get();
2133     SbxDimArray* pRootArray = pItem->GetRootArray();
2134     bool bArrayIsRootArray = false;
2135     if( !pArray && pRootArray )
2136     {
2137         pArray = pRootArray;
2138         bArrayIsRootArray = true;
2139     }
2140 
2141     SbxObject* pObj = pItem->mpObject.get();
2142     if( pObj )
2143     {
2144         createAllObjectProperties( pObj );
2145         SbxArray* pProps = pObj->GetProperties();
2146         const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2147         pItem->maMemberList.reserve(nPropCount);
2148 
2149         for( sal_uInt32 i = 0 ; i < nPropCount ; ++i )
2150         {
2151             SbxVariable* pVar = pProps->Get(i);
2152 
2153             pItem->maMemberList.push_back(pVar->GetName());
2154             OUString const& rName = pItem->maMemberList.back();
2155 
2156             WatchItem* pWatchItem = new WatchItem(rName);
2157             OUString sId(weld::toId(pWatchItem));
2158 
2159             m_xTreeListBox->insert(&rParent, -1, &rName, &sId, nullptr, nullptr, false, xRet.get());
2160             m_xTreeListBox->set_text(*xRet, "", 1);
2161             m_xTreeListBox->set_text(*xRet, "", 2);
2162         }
2163 
2164         if (nPropCount > 0 && !m_nUpdateWatchesId)
2165         {
2166             m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2167         }
2168     }
2169     else if( pArray )
2170     {
2171         sal_uInt16 nElementCount = 0;
2172 
2173         // Loop through indices of current level
2174         int nParentLevel = bArrayIsRootArray ? pItem->nDimLevel : 0;
2175         int nThisLevel = nParentLevel + 1;
2176         sal_Int32 nMin, nMax;
2177         if (pArray->GetDim(nThisLevel, nMin, nMax))
2178         {
2179             for (sal_Int32 i = nMin; i <= nMax; i++)
2180             {
2181                 WatchItem* pChildItem = new WatchItem(pItem->maName);
2182 
2183                 // Copy data and create name
2184 
2185                 OUStringBuffer aIndexStr = "(";
2186                 pChildItem->mpArrayParentItem = pItem;
2187                 pChildItem->nDimLevel = nThisLevel;
2188                 pChildItem->nDimCount = pItem->nDimCount;
2189                 pChildItem->vIndices.resize(pChildItem->nDimCount);
2190                 sal_Int32 j;
2191                 for (j = 0; j < nParentLevel; j++)
2192                 {
2193                     sal_Int32 n = pChildItem->vIndices[j] = pItem->vIndices[j];
2194                     aIndexStr.append( OUString::number(n) + "," );
2195                 }
2196                 pChildItem->vIndices[nParentLevel] = i;
2197                 aIndexStr.append( OUString::number(i) + ")" );
2198 
2199                 OUString aDisplayName;
2200                 WatchItem* pArrayRootItem = pChildItem->GetRootItem();
2201                 if (pArrayRootItem && pArrayRootItem->mpArrayParentItem)
2202                     aDisplayName = pItem->maDisplayName;
2203                 else
2204                     aDisplayName = pItem->maName;
2205                 aDisplayName += aIndexStr;
2206                 pChildItem->maDisplayName = aDisplayName;
2207 
2208                 OUString sId(weld::toId(pChildItem));
2209 
2210                 m_xTreeListBox->insert(&rParent, -1, &aDisplayName, &sId, nullptr, nullptr, false,
2211                                        xRet.get());
2212                 m_xTreeListBox->set_text(*xRet, "", 1);
2213                 m_xTreeListBox->set_text(*xRet, "", 2);
2214 
2215                 nElementCount++;
2216             }
2217         }
2218         if (nElementCount > 0 && !m_nUpdateWatchesId)
2219         {
2220             m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2221         }
2222     }
2223 
2224     return true;
2225 }
2226 
2227 IMPL_LINK_NOARG(WatchWindow, ExecuteUpdateWatches, void*, void)
2228 {
2229     m_nUpdateWatchesId = nullptr;
2230     UpdateWatches();
2231 }
2232 
2233 SbxBase* WatchWindow::ImplGetSBXForEntry(const weld::TreeIter& rEntry, bool& rbArrayElement)
2234 {
2235     SbxBase* pSBX = nullptr;
2236     rbArrayElement = false;
2237 
2238     WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2239     OUString aVName( pItem->maName );
2240 
2241     std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeListBox->make_iterator(&rEntry);
2242     bool bParentEntry = m_xTreeListBox->iter_parent(*xParentEntry);
2243     WatchItem* pParentItem = bParentEntry ? weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xParentEntry)) : nullptr;
2244     if( pParentItem )
2245     {
2246         SbxObject* pObj = pParentItem->mpObject.get();
2247         SbxDimArray* pArray;
2248         if( pObj )
2249         {
2250             pSBX = pObj->Find( aVName, SbxClassType::DontCare );
2251             if (SbxVariable const* pVar = IsSbxVariable(pSBX))
2252             {
2253                 // Force getting value
2254                 SbxValues aRes;
2255                 aRes.eType = SbxVOID;
2256                 pVar->Get( aRes );
2257             }
2258         }
2259         // Array?
2260         else if( (pArray = pItem->GetRootArray()) != nullptr )
2261         {
2262             rbArrayElement = true;
2263             if( pParentItem->nDimLevel + 1 == pParentItem->nDimCount )
2264                 pSBX = pArray->Get(pItem->vIndices.empty() ? nullptr : &*pItem->vIndices.begin());
2265         }
2266     }
2267     else
2268     {
2269         pSBX = StarBASIC::FindSBXInCurrentScope( aVName );
2270     }
2271     return pSBX;
2272 }
2273 
2274 IMPL_LINK(WatchWindow, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
2275 {
2276     WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rIter));
2277 
2278     bool bEdit = false;
2279     if (StarBASIC::IsRunning() && StarBASIC::GetActiveMethod() && !SbxBase::IsError())
2280     {
2281         // No out of scope entries
2282         bool bArrayElement;
2283         SbxBase* pSbx = ImplGetSBXForEntry(rIter, bArrayElement);
2284         if (IsSbxVariable(pSbx) || bArrayElement)
2285         {
2286             // Accept no objects and only end nodes of arrays for editing
2287             if( !pItem->mpObject.is() && ( !pItem->mpArray.is() || pItem->nDimLevel == pItem->nDimCount ) )
2288             {
2289                 aEditingRes = m_xTreeListBox->get_text(rIter, 1);
2290                 aEditingRes = comphelper::string::strip(aEditingRes, ' ');
2291                 bEdit = true;
2292             }
2293         }
2294     }
2295 
2296     return bEdit;
2297 }
2298 
2299 IMPL_LINK(WatchWindow, EditedEntryHdl, const IterString&, rIterString, bool)
2300 {
2301     const weld::TreeIter& rIter = rIterString.first;
2302     OUString aResult = comphelper::string::strip(rIterString.second, ' ');
2303 
2304     sal_uInt16 nResultLen = aResult.getLength();
2305     sal_Unicode cFirst = aResult[0];
2306     sal_Unicode cLast  = aResult[ nResultLen - 1 ];
2307     if( cFirst == '\"' && cLast == '\"' )
2308         aResult = aResult.copy( 1, nResultLen - 2 );
2309 
2310     if (aResult == aEditingRes)
2311         return false;
2312 
2313     bool bArrayElement;
2314     SbxBase* pSBX = ImplGetSBXForEntry(rIter, bArrayElement);
2315 
2316     if (SbxVariable* pVar = IsSbxVariable(pSBX))
2317     {
2318         SbxDataType eType = pVar->GetType();
2319         if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxOBJECT)
2320              && ( eType & SbxARRAY ) == 0 )
2321         {
2322             // If the type is variable, the conversion of the SBX does not matter,
2323             // else the string is converted.
2324             pVar->PutStringExt( aResult );
2325         }
2326     }
2327 
2328     if ( SbxBase::IsError() )
2329     {
2330         SbxBase::ResetError();
2331     }
2332 
2333     UpdateWatches();
2334 
2335     // The text should never be taken/copied 1:1,
2336     // as the UpdateWatches will be lost
2337     return false;
2338 }
2339 
2340 namespace
2341 {
2342 
2343 void implCollapseModifiedObjectEntry(const weld::TreeIter& rParent, weld::TreeView& rTree)
2344 {
2345     rTree.collapse_row(rParent);
2346 
2347     std::unique_ptr<weld::TreeIter> xDeleteEntry = rTree.make_iterator(&rParent);
2348 
2349     while (rTree.iter_children(*xDeleteEntry))
2350     {
2351         implCollapseModifiedObjectEntry(*xDeleteEntry, rTree);
2352 
2353         WatchItem* pItem = weld::fromId<WatchItem*>(rTree.get_id(*xDeleteEntry));
2354         delete pItem;
2355         rTree.remove(*xDeleteEntry);
2356         rTree.copy_iterator(rParent, *xDeleteEntry);
2357     }
2358 }
2359 
2360 OUString implCreateTypeStringForDimArray( WatchItem* pItem, SbxDataType eType )
2361 {
2362     OUString aRetStr = getBasicTypeName( eType );
2363 
2364     SbxDimArray* pArray = pItem->mpArray.get();
2365     if( !pArray )
2366         pArray = pItem->GetRootArray();
2367     if( pArray )
2368     {
2369         int nDimLevel = pItem->nDimLevel;
2370         int nDims = pItem->nDimCount;
2371         if( nDimLevel < nDims )
2372         {
2373             aRetStr += "(";
2374             for( int i = nDimLevel ; i < nDims ; i++ )
2375             {
2376                 sal_Int32 nMin, nMax;
2377                 pArray->GetDim(sal::static_int_cast<sal_Int32>(i + 1), nMin, nMax);
2378                 aRetStr += OUString::number(nMin) + " to "  + OUString::number(nMax);
2379                 if( i < nDims - 1 )
2380                     aRetStr += ", ";
2381             }
2382             aRetStr += ")";
2383         }
2384     }
2385     return aRetStr;
2386 }
2387 
2388 } // namespace
2389 
2390 void WatchWindow::implEnableChildren(const weld::TreeIter& rEntry, bool bEnable)
2391 {
2392     if (bEnable)
2393     {
2394         if (!m_xTreeListBox->get_row_expanded(rEntry))
2395             m_xTreeListBox->set_children_on_demand(rEntry, true);
2396     }
2397     else
2398     {
2399         assert(!m_xTreeListBox->get_row_expanded(rEntry));
2400         m_xTreeListBox->set_children_on_demand(rEntry, false);
2401     }
2402 }
2403 
2404 void WatchWindow::UpdateWatches(bool bBasicStopped)
2405 {
2406     SbMethod* pCurMethod = StarBASIC::GetActiveMethod();
2407 
2408     ErrCode eOld = SbxBase::GetError();
2409     setBasicWatchMode( true );
2410 
2411     m_xTreeListBox->all_foreach([this, pCurMethod, bBasicStopped](weld::TreeIter& rEntry){
2412         WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2413         DBG_ASSERT( !pItem->maName.isEmpty(), "Var? - Must not be empty!" );
2414         OUString aWatchStr;
2415         OUString aTypeStr;
2416         if ( pCurMethod )
2417         {
2418             bool bCollapse = false;
2419             TriState eEnableChildren = TRISTATE_INDET;
2420 
2421             bool bArrayElement;
2422             SbxBase* pSBX = ImplGetSBXForEntry(rEntry, bArrayElement);
2423 
2424             // Array? If no end node create type string
2425             if( bArrayElement && pItem->nDimLevel < pItem->nDimCount )
2426             {
2427                 SbxDimArray* pRootArray = pItem->GetRootArray();
2428                 SbxDataType eType = pRootArray->GetType();
2429                 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2430                 eEnableChildren = TRISTATE_TRUE;
2431             }
2432 
2433             if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pSBX))
2434             {
2435                 // extra treatment of arrays
2436                 SbxDataType eType = pVar->GetType();
2437                 if ( eType & SbxARRAY )
2438                 {
2439                     // consider multidimensional arrays!
2440                     if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(pVar->GetObject()))
2441                     {
2442                         SbxDimArray* pOldArray = pItem->mpArray.get();
2443 
2444                         bool bArrayChanged = false;
2445                         if (pOldArray != nullptr)
2446                         {
2447                             // Compare Array dimensions to see if array has changed
2448                             // Can be a copy, so comparing pointers does not work
2449                             sal_Int32 nOldDims = pOldArray->GetDims();
2450                             sal_Int32 nNewDims = pNewArray->GetDims();
2451                             if( nOldDims != nNewDims )
2452                             {
2453                                 bArrayChanged = true;
2454                             }
2455                             else
2456                             {
2457                                 for( sal_Int32 i = 0 ; i < nOldDims ; i++ )
2458                                 {
2459                                     sal_Int32 nOldMin, nOldMax;
2460                                     sal_Int32 nNewMin, nNewMax;
2461 
2462                                     pOldArray->GetDim(i + 1, nOldMin, nOldMax);
2463                                     pNewArray->GetDim(i + 1, nNewMin, nNewMax);
2464                                     if( nOldMin != nNewMin || nOldMax != nNewMax )
2465                                     {
2466                                         bArrayChanged = true;
2467                                         break;
2468                                     }
2469                                 }
2470                             }
2471                         }
2472                         else
2473                         {
2474                             bArrayChanged = true;
2475                         }
2476                         eEnableChildren = TRISTATE_TRUE;
2477                         // #i37227 Clear always and replace array
2478                         if( pNewArray != pOldArray )
2479                         {
2480                             pItem->clearWatchItem();
2481                             eEnableChildren = TRISTATE_TRUE;
2482 
2483                             pItem->mpArray = pNewArray;
2484                             sal_Int32 nDims = pNewArray->GetDims();
2485                             pItem->nDimLevel = 0;
2486                             pItem->nDimCount = nDims;
2487                         }
2488                         if( bArrayChanged && pOldArray != nullptr )
2489                         {
2490                             bCollapse = true;
2491                         }
2492                         aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2493                     }
2494                     else
2495                     {
2496                         aWatchStr += "<?>";
2497                     }
2498                 }
2499                 else if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
2500                 {
2501                     if (SbxObject* pObj = dynamic_cast<SbxObject*>(pVar->GetObject()))
2502                     {
2503                         if ( pItem->mpObject.is() && !pItem->maMemberList.empty() )
2504                         {
2505                             createAllObjectProperties(pObj);
2506                             SbxArray* pProps = pObj->GetProperties();
2507                             const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2508                             // Check if member list has changed
2509                             bCollapse = pItem->maMemberList.size() != nPropCount;
2510                             for( sal_uInt32 i = 0 ; !bCollapse && i < nPropCount ; i++ )
2511                             {
2512                                 SbxVariable* pVar_ = pProps->Get(i);
2513                                 if( pItem->maMemberList[i] != pVar_->GetName() )
2514                                     bCollapse = true;
2515                             }
2516                         }
2517 
2518                         pItem->mpObject = pObj;
2519                         eEnableChildren = TRISTATE_TRUE;
2520                         aTypeStr = getBasicObjectTypeName( pObj );
2521                     }
2522                     else
2523                     {
2524                         aWatchStr = "Null";
2525                         if( pItem->mpObject.is() )
2526                         {
2527                             bCollapse = true;
2528                             eEnableChildren = TRISTATE_FALSE;
2529                         }
2530                     }
2531                 }
2532                 else
2533                 {
2534                     if( pItem->mpObject.is() )
2535                     {
2536                         bCollapse = true;
2537                         eEnableChildren = TRISTATE_FALSE;
2538                     }
2539 
2540                     bool bString = (static_cast<sal_uInt8>(eType) == sal_uInt8(SbxSTRING));
2541                     OUString aStrStr( "\"" );
2542                     if( bString )
2543                     {
2544                         aWatchStr += aStrStr;
2545                     }
2546                     // tdf#57308 - avoid a second call to retrieve the data
2547                     const SbxFlagBits nFlags = pVar->GetFlags();
2548                     pVar->SetFlag(SbxFlagBits::NoBroadcast);
2549                     aWatchStr += pVar->GetOUString();
2550                     pVar->SetFlags(nFlags);
2551                     if( bString )
2552                     {
2553                         aWatchStr += aStrStr;
2554                     }
2555                 }
2556                 if( aTypeStr.isEmpty() )
2557                 {
2558                     if( !pVar->IsFixed() )
2559                     {
2560                         aTypeStr = "Variant/";
2561                     }
2562                     aTypeStr += getBasicTypeName( pVar->GetType() );
2563                 }
2564             }
2565             else if( !bArrayElement )
2566             {
2567                 aWatchStr += "<Out of Scope>";
2568             }
2569 
2570             if( bCollapse )
2571             {
2572                 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2573                 pItem->clearWatchItem();
2574             }
2575 
2576             if (eEnableChildren != TRISTATE_INDET)
2577                 implEnableChildren(rEntry, eEnableChildren == TRISTATE_TRUE);
2578         }
2579         else if( bBasicStopped )
2580         {
2581             if( pItem->mpObject.is() || pItem->mpArray.is() )
2582             {
2583                 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2584                 pItem->mpObject.clear();
2585                 pItem->mpArray.clear();
2586             }
2587             pItem->clearWatchItem();
2588         }
2589 
2590         m_xTreeListBox->set_text(rEntry, aWatchStr, 1);
2591         m_xTreeListBox->set_text(rEntry, aTypeStr, 2);
2592 
2593         return false;
2594     });
2595 
2596     SbxBase::ResetError();
2597     if( eOld != ERRCODE_NONE )
2598         SbxBase::SetError( eOld );
2599     setBasicWatchMode( false );
2600 }
2601 
2602 IMPL_LINK_NOARG(CodeCompleteWindow, ImplDoubleClickHdl, weld::TreeView&, bool)
2603 {
2604     InsertSelectedEntry();
2605     return true;
2606 }
2607 
2608 IMPL_LINK_NOARG(CodeCompleteWindow, ImplSelectHdl, weld::TreeView&, void)
2609 {
2610     //give back the focus to the parent
2611     pParent->GrabFocus();
2612 }
2613 
2614 TextView* CodeCompleteWindow::GetParentEditView()
2615 {
2616     return pParent->GetEditView();
2617 }
2618 
2619 void CodeCompleteWindow::InsertSelectedEntry()
2620 {
2621     OUString sSelectedEntry = m_xListBox->get_selected_text();
2622 
2623     if( !aFuncBuffer.isEmpty() )
2624     {
2625         // if the user typed in something: remove, and insert
2626         GetParentEditView()->SetSelection(pParent->GetLastHighlightPortionTextSelection());
2627         GetParentEditView()->DeleteSelected();
2628 
2629         if (!sSelectedEntry.isEmpty())
2630         {
2631             // if the user selected something
2632             GetParentEditView()->InsertText(sSelectedEntry);
2633         }
2634     }
2635     else
2636     {
2637         if (!sSelectedEntry.isEmpty())
2638         {
2639             // if the user selected something
2640             GetParentEditView()->InsertText(sSelectedEntry);
2641         }
2642     }
2643     HideAndRestoreFocus();
2644 }
2645 
2646 void CodeCompleteWindow::SetMatchingEntries()
2647 {
2648     for (sal_Int32 i = 0, nEntryCount = m_xListBox->n_children(); i< nEntryCount; ++i)
2649     {
2650         OUString sEntry = m_xListBox->get_text(i);
2651         if (sEntry.startsWithIgnoreAsciiCase(aFuncBuffer))
2652         {
2653             m_xListBox->select(i);
2654             break;
2655         }
2656     }
2657 }
2658 
2659 IMPL_LINK(CodeCompleteWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
2660 {
2661     return HandleKeyInput(rKEvt);
2662 }
2663 
2664 bool CodeCompleteWindow::HandleKeyInput( const KeyEvent& rKeyEvt )
2665 {
2666     bool bHandled = true;
2667 
2668     sal_Unicode aChar = rKeyEvt.GetKeyCode().GetCode();
2669     if( (( aChar >= KEY_A ) && ( aChar <= KEY_Z ))
2670         || ((aChar >= KEY_0) && (aChar <= KEY_9)) )
2671     {
2672         aFuncBuffer.append(rKeyEvt.GetCharCode());
2673         SetMatchingEntries();
2674     }
2675     else
2676     {
2677         switch( aChar )
2678         {
2679             case KEY_POINT:
2680                 break;
2681             case KEY_ESCAPE: // hide, do nothing
2682                 HideAndRestoreFocus();
2683                 break;
2684             case KEY_RIGHT:
2685             {
2686                 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2687                 if( aTextSelection.GetEnd().GetPara() != GetTextSelection().GetEnd().GetPara()-1 )
2688                 {
2689                     HideAndRestoreFocus();
2690                 }
2691                 break;
2692             }
2693             case KEY_LEFT:
2694             {
2695                 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2696                 if( aTextSelection.GetStart().GetIndex()-1 < GetTextSelection().GetStart().GetIndex() )
2697                 {//leave the cursor where it is
2698                     HideAndRestoreFocus();
2699                 }
2700                 break;
2701             }
2702             case KEY_TAB:
2703             {
2704                 TextSelection aTextSelection = pParent->GetLastHighlightPortionTextSelection();
2705                 OUString sTypedText = pParent->GetEditEngine()->GetText(aTextSelection);
2706                 if( !aFuncBuffer.isEmpty() )
2707                 {
2708                     sal_Int32 nInd = m_xListBox->get_selected_index();
2709                     if (nInd != -1)
2710                     {
2711                         int nEntryCount = m_xListBox->n_children();
2712                         //if there is something selected
2713                         bool bFound = false;
2714                         for (sal_Int32 i = nInd; i != nEntryCount; ++i)
2715                         {
2716                             OUString sEntry = m_xListBox->get_text(i);
2717                             if( sEntry.startsWithIgnoreAsciiCase( aFuncBuffer )
2718                                 && (std::u16string_view(aFuncBuffer) != sTypedText) && (i != nInd) )
2719                             {
2720                                 m_xListBox->select(i);
2721                                 bFound = true;
2722                                 break;
2723                             }
2724                         }
2725                         if( !bFound )
2726                             SetMatchingEntries();
2727 
2728                         GetParentEditView()->SetSelection( aTextSelection );
2729                         GetParentEditView()->DeleteSelected();
2730                         GetParentEditView()->InsertText(m_xListBox->get_selected_text());
2731                     }
2732                 }
2733                 break;
2734             }
2735             case KEY_SPACE:
2736                 HideAndRestoreFocus();
2737                 break;
2738             case KEY_BACKSPACE: case KEY_DELETE:
2739                 if( !aFuncBuffer.isEmpty() )
2740                 {
2741                     //if there was something inserted by tab: add it to aFuncBuffer
2742                     TextSelection aSel( GetParentEditView()->GetSelection() );
2743                     TextPaM aEnd( GetParentEditView()->CursorEndOfLine(GetTextSelection().GetEnd()) );
2744                     GetParentEditView()->SetSelection(TextSelection(GetTextSelection().GetStart(), aEnd ) );
2745                     OUString aTabInsertedStr( GetParentEditView()->GetSelected() );
2746                     GetParentEditView()->SetSelection( aSel );
2747 
2748                     if( !aTabInsertedStr.isEmpty() && aTabInsertedStr != std::u16string_view(aFuncBuffer) )
2749                     {
2750                         aFuncBuffer = aTabInsertedStr;
2751                     }
2752                     aFuncBuffer.remove(aFuncBuffer.getLength()-1, 1);
2753                     SetMatchingEntries();
2754                 }
2755                 else
2756                 {
2757                     ClearAndHide();
2758                     bHandled = false;
2759                 }
2760                 break;
2761             case KEY_RETURN:
2762                 InsertSelectedEntry();
2763                 break;
2764             case KEY_UP:
2765             {
2766                 int nInd = m_xListBox->get_selected_index();
2767                 if (nInd)
2768                     m_xListBox->select(nInd - 1);
2769                 break;
2770             }
2771             case KEY_DOWN:
2772             {
2773                 int nInd = m_xListBox->get_selected_index();
2774                 if (nInd + 1 < m_xListBox->n_children())
2775                     m_xListBox->select(nInd + 1);
2776                 break;
2777             }
2778             default:
2779                 bHandled = false;
2780                 break;
2781         }
2782     }
2783 
2784     return bHandled;
2785 }
2786 
2787 void CodeCompleteWindow::HideAndRestoreFocus()
2788 {
2789     Hide();
2790     pParent->GrabFocus();
2791 }
2792 
2793 CodeCompleteWindow::CodeCompleteWindow(EditorWindow* pPar)
2794     : InterimItemWindow(pPar, "modules/BasicIDE/ui/codecomplete.ui", "CodeComplete")
2795     , pParent(pPar)
2796     , m_xListBox(m_xBuilder->weld_tree_view("treeview"))
2797 {
2798     m_xListBox->connect_row_activated(LINK(this, CodeCompleteWindow, ImplDoubleClickHdl));
2799     m_xListBox->connect_changed(LINK(this, CodeCompleteWindow, ImplSelectHdl));
2800     m_xListBox->connect_key_press(LINK(this, CodeCompleteWindow, KeyInputHdl));
2801     m_xListBox->make_sorted();
2802 
2803     m_xListBox->set_size_request(150, 150); // default, this will adopt the line length
2804     SetSizePixel(m_xContainer->get_preferred_size());
2805 }
2806 
2807 CodeCompleteWindow::~CodeCompleteWindow()
2808 {
2809     disposeOnce();
2810 }
2811 
2812 void CodeCompleteWindow::dispose()
2813 {
2814     m_xListBox.reset();
2815     pParent.clear();
2816     InterimItemWindow::dispose();
2817 }
2818 
2819 void CodeCompleteWindow::InsertEntry( const OUString& aStr )
2820 {
2821     m_xListBox->append_text(aStr);
2822 }
2823 
2824 void CodeCompleteWindow::ClearListBox()
2825 {
2826     m_xListBox->clear();
2827     aFuncBuffer.setLength(0);
2828 }
2829 
2830 void CodeCompleteWindow::SetTextSelection( const TextSelection& aSel )
2831 {
2832     m_aTextSelection = aSel;
2833 }
2834 
2835 void CodeCompleteWindow::ResizeAndPositionListBox()
2836 {
2837     if (m_xListBox->n_children() < 1)
2838         return;
2839 
2840     // if there is at least one element inside
2841     // calculate basic position: under the current line
2842     tools::Rectangle aRect = static_cast<TextEngine*>(pParent->GetEditEngine())->PaMtoEditCursor( pParent->GetEditView()->GetSelection().GetEnd() );
2843     tools::Long nViewYOffset = pParent->GetEditView()->GetStartDocPos().Y();
2844     Point aPos = aRect.BottomRight();// this variable will be used later (if needed)
2845     aPos.setY( (aPos.Y() - nViewYOffset) + nBasePad );
2846 
2847     // get line count
2848     const sal_uInt16 nLines = static_cast<sal_uInt16>(std::min(6, m_xListBox->n_children()));
2849 
2850     m_xListBox->set_size_request(-1, m_xListBox->get_height_rows(nLines));
2851 
2852     Size aSize = m_xContainer->get_preferred_size();
2853     //set the size
2854     SetSizePixel( aSize );
2855 
2856     //calculate position
2857     const tools::Rectangle aVisArea( pParent->GetEditView()->GetStartDocPos(), pParent->GetOutputSizePixel() ); //the visible area
2858     const Point& aBottomPoint = aVisArea.BottomRight();
2859 
2860     if( aVisArea.TopRight().getY() + aPos.getY() + aSize.getHeight() > aBottomPoint.getY() )
2861     {//clipped at the bottom: move it up
2862         const tools::Long& nParentFontHeight = pParent->GetEditEngine()->GetFont().GetFontHeight(); //parent's font (in the IDE): needed for height
2863         aPos.AdjustY( -(aSize.getHeight() + nParentFontHeight + nCursorPad) );
2864     }
2865 
2866     if( aVisArea.TopLeft().getX() + aPos.getX() + aSize.getWidth() > aBottomPoint.getX() )
2867     {//clipped at the right side, move it a bit left
2868         aPos.AdjustX( -(aSize.getWidth() + aVisArea.TopLeft().getX()) );
2869     }
2870     //set the position
2871     SetPosPixel( aPos );
2872 }
2873 
2874 void CodeCompleteWindow::SelectFirstEntry()
2875 {
2876     if (m_xListBox->n_children() > 0)
2877         m_xListBox->select(0);
2878 }
2879 
2880 void CodeCompleteWindow::ClearAndHide()
2881 {
2882     ClearListBox();
2883     HideAndRestoreFocus();
2884 }
2885 
2886 UnoTypeCodeCompletetor::UnoTypeCodeCompletetor( const std::vector< OUString >& aVect, const OUString& sVarType )
2887 : bCanComplete( true )
2888 {
2889     if( aVect.empty() || sVarType.isEmpty() )
2890     {
2891         bCanComplete = false;//invalid parameters, nothing to code complete
2892         return;
2893     }
2894 
2895     try
2896     {
2897         // Get the base class for reflection:
2898         xClass = css::reflection::theCoreReflection::get(
2899             comphelper::getProcessComponentContext())->forName(sVarType);
2900     }
2901     catch( const Exception& )
2902     {
2903         bCanComplete = false;
2904         return;
2905     }
2906 
2907     //start from aVect[1]: aVect[0] is the variable name
2908     bCanComplete = std::none_of(aVect.begin() + 1, aVect.end(), [this](const OUString& rMethName) {
2909         return (!CodeCompleteOptions::IsExtendedTypeDeclaration() || !CheckMethod(rMethName)) && !CheckField(rMethName); });
2910 }
2911 
2912 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassMethods() const
2913 {
2914     std::vector< OUString > aRetVect;
2915     if( bCanComplete && ( xClass != nullptr ) )
2916     {
2917         const Sequence< Reference< reflection::XIdlMethod > > aMethods = xClass->getMethods();
2918         for(Reference< reflection::XIdlMethod > const & rMethod : aMethods)
2919         {
2920             aRetVect.push_back( rMethod->getName() );
2921         }
2922     }
2923     return aRetVect;//this is empty when cannot code complete
2924 }
2925 
2926 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassFields() const
2927 {
2928     std::vector< OUString > aRetVect;
2929     if( bCanComplete && ( xClass != nullptr ) )
2930     {
2931         const Sequence< Reference< reflection::XIdlField > > aFields = xClass->getFields();
2932         for(Reference< reflection::XIdlField > const & rxField : aFields)
2933         {
2934             aRetVect.push_back( rxField->getName() );
2935         }
2936     }
2937     return aRetVect;//this is empty when cannot code complete
2938 }
2939 
2940 
2941 bool UnoTypeCodeCompletetor::CheckField( const OUString& sFieldName )
2942 {// modifies xClass!!!
2943 
2944     if ( xClass == nullptr )
2945         return false;
2946 
2947     Reference< reflection::XIdlField> xField = xClass->getField( sFieldName );
2948     if( xField != nullptr )
2949     {
2950         xClass = xField->getType();
2951         if( xClass != nullptr )
2952         {
2953             return true;
2954         }
2955     }
2956     return false;
2957 }
2958 
2959 bool UnoTypeCodeCompletetor::CheckMethod( const OUString& sMethName )
2960 {// modifies xClass!!!
2961 
2962 
2963     if ( xClass == nullptr )
2964         return false;
2965 
2966     Reference< reflection::XIdlMethod> xMethod = xClass->getMethod( sMethName );
2967     if( xMethod != nullptr ) //method OK, check return type
2968     {
2969         xClass = xMethod->getReturnType();
2970         if( xClass != nullptr )
2971         {
2972             return true;
2973         }
2974     }
2975     return false;
2976 }
2977 
2978 } // namespace basctl
2979 
2980 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2981