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