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