android: Update app-specific/MIME type icons
[LibreOffice.git] / basctl / source / basicide / baside2b.cxx
blobf1c1a7303878f2774d3b76fd806f19d88917b536
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cassert>
23 #include <string_view>
25 #include <helpids.h>
26 #include <iderid.hxx>
27 #include <strings.hrc>
28 #include <bitmaps.hlst>
30 #include "baside2.hxx"
31 #include "brkdlg.hxx"
32 #include <basidesh.hxx>
33 #include <basobj.hxx>
34 #include <iderdll.hxx>
36 #include <basic/sbmeth.hxx>
37 #include <basic/sbuno.hxx>
38 #include <com/sun/star/beans/XMultiPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
40 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
41 #include <com/sun/star/script/XLibraryContainer2.hpp>
42 #include <comphelper/string.hxx>
43 #include <comphelper/diagnose_ex.hxx>
44 #include <o3tl/string_view.hxx>
45 #include <officecfg/Office/Common.hxx>
46 #include <sfx2/dispatch.hxx>
47 #include <sfx2/progress.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <tools/debug.hxx>
50 #include <utility>
51 #include <vcl/image.hxx>
52 #include <vcl/weld.hxx>
53 #include <vcl/weldutils.hxx>
54 #include <svl/urihelper.hxx>
55 #include <svx/svxids.hrc>
56 #include <vcl/commandevent.hxx>
57 #include <vcl/xtextedt.hxx>
58 #include <vcl/textview.hxx>
59 #include <vcl/txtattr.hxx>
60 #include <vcl/settings.hxx>
61 #include <vcl/ptrstyle.hxx>
62 #include <vcl/event.hxx>
63 #include <vcl/svapp.hxx>
64 #include <vcl/taskpanelist.hxx>
65 #include <vcl/help.hxx>
66 #include <cppuhelper/implbase.hxx>
67 #include <vector>
68 #include <com/sun/star/reflection/theCoreReflection.hpp>
69 #include <unotools/charclass.hxx>
70 #include "textwindowpeer.hxx"
71 #include "uiobject.hxx"
72 #include <basegfx/utils/zoomtools.hxx>
74 namespace basctl
77 using namespace ::com::sun::star;
78 using namespace ::com::sun::star::uno;
80 namespace
83 sal_uInt16 const NoMarker = 0xFFFF;
84 tools::Long const nBasePad = 2;
85 tools::Long const nCursorPad = 5;
87 tools::Long nVirtToolBoxHeight; // inited in WatchWindow, used in Stackwindow
89 // Returns pBase converted to SbxVariable if valid and is not an SbxMethod.
90 SbxVariable* IsSbxVariable (SbxBase* pBase)
92 if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pBase))
93 if (!dynamic_cast<SbxMethod*>(pVar))
94 return pVar;
95 return nullptr;
98 Image GetImage(const OUString& rId)
100 return Image(StockImage::Yes, rId);
103 int const nScrollLine = 12;
104 int const nScrollPage = 60;
105 int const DWBORDER = 3;
107 std::u16string_view const cSuffixes = u"%&!#@$";
109 } // namespace
113 * Helper functions to get/set text in TextEngine using
114 * the stream interface.
116 * get/setText() only supports tools Strings limited to 64K).
118 OUString getTextEngineText (ExtTextEngine& rEngine)
120 SvMemoryStream aMemStream;
121 aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
122 aMemStream.SetLineDelimiter( LINEEND_LF );
123 rEngine.Write( aMemStream );
124 std::size_t nSize = aMemStream.Tell();
125 OUString aText( static_cast<const char*>(aMemStream.GetData()),
126 nSize, RTL_TEXTENCODING_UTF8 );
127 return aText;
130 void setTextEngineText (ExtTextEngine& rEngine, std::u16string_view aStr)
132 rEngine.SetText(OUString());
133 OString aUTF8Str = OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 );
134 SvMemoryStream aMemStream( const_cast<char *>(aUTF8Str.getStr()), aUTF8Str.getLength(),
135 StreamMode::READ );
136 aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
137 aMemStream.SetLineDelimiter( LINEEND_LF );
138 rEngine.Read(aMemStream);
141 namespace
144 void lcl_DrawIDEWindowFrame(DockingWindow const * pWin, vcl::RenderContext& rRenderContext)
146 if (pWin->IsFloatingMode())
147 return;
149 Size aSz(pWin->GetOutputSizePixel());
150 const Color aOldLineColor(rRenderContext.GetLineColor());
151 rRenderContext.SetLineColor(COL_WHITE);
152 // White line on top
153 rRenderContext.DrawLine(Point(0, 0), Point(aSz.Width(), 0));
154 // Black line at bottom
155 rRenderContext.SetLineColor(COL_BLACK);
156 rRenderContext.DrawLine(Point(0, aSz.Height() - 1),
157 Point(aSz.Width(), aSz.Height() - 1));
158 rRenderContext.SetLineColor(aOldLineColor);
161 void lcl_SeparateNameAndIndex( const OUString& rVName, OUString& rVar, OUString& rIndex )
163 rVar = rVName;
164 rIndex.clear();
165 sal_Int32 nIndexStart = rVar.indexOf( '(' );
166 if ( nIndexStart != -1 )
168 sal_Int32 nIndexEnd = rVar.indexOf( ')', nIndexStart );
169 if (nIndexEnd != -1)
171 rIndex = rVar.copy(nIndexStart + 1, nIndexEnd - nIndexStart - 1);
172 rVar = rVar.copy(0, nIndexStart);
173 rVar = comphelper::string::stripEnd(rVar, ' ');
174 rIndex = comphelper::string::strip(rIndex, ' ');
178 if ( !rVar.isEmpty() )
180 sal_uInt16 nLastChar = rVar.getLength()-1;
181 if ( cSuffixes.find(rVar[ nLastChar ] ) != std::u16string_view::npos )
182 rVar = rVar.replaceAt( nLastChar, 1, u"" );
184 if ( !rIndex.isEmpty() )
186 sal_uInt16 nLastChar = rIndex.getLength()-1;
187 if ( cSuffixes.find(rIndex[ nLastChar ] ) != std::u16string_view::npos )
188 rIndex = rIndex.replaceAt( nLastChar, 1, u"" );
192 } // namespace
195 // EditorWindow
198 class EditorWindow::ChangesListener:
199 public cppu::WeakImplHelper< beans::XPropertiesChangeListener >
201 public:
202 explicit ChangesListener(EditorWindow & editor): editor_(editor) {}
204 private:
205 virtual ~ChangesListener() override {}
207 virtual void SAL_CALL disposing(lang::EventObject const &) override
209 std::unique_lock g(editor_.mutex_);
210 editor_.notifier_.clear();
213 virtual void SAL_CALL propertiesChange(
214 Sequence< beans::PropertyChangeEvent > const &) override
216 SolarMutexGuard g;
217 editor_.ImplSetFont();
220 EditorWindow & editor_;
223 class EditorWindow::ProgressInfo : public SfxProgress
225 public:
226 ProgressInfo (SfxObjectShell* pObjSh, OUString const& rText, sal_uInt32 nRange) :
227 SfxProgress(pObjSh, rText, nRange),
228 nCurState(0)
231 void StepProgress ()
233 SetState(++nCurState);
236 private:
237 sal_uInt32 nCurState;
240 EditorWindow::EditorWindow (vcl::Window* pParent, ModulWindow* pModulWindow) :
241 Window(pParent, WB_BORDER),
242 rModulWindow(*pModulWindow),
243 nCurTextWidth(0),
244 m_nSetSourceInBasicId(nullptr),
245 aHighlighter(HighlighterLanguage::Basic),
246 aSyntaxIdle( "basctl EditorWindow aSyntaxIdle" ),
247 bHighlighting(false),
248 bDoSyntaxHighlight(true),
249 bDelayHighlight(true),
250 pCodeCompleteWnd(VclPtr<CodeCompleteWindow>::Create(this))
252 set_id("EditorWindow");
253 const Wallpaper aBackground(rModulWindow.GetLayout().GetSyntaxBackgroundColor());
254 SetBackground(aBackground);
255 GetWindow(GetWindowType::Border)->SetBackground(aBackground);
256 SetPointer( PointerStyle::Text );
257 SetHelpId( HID_BASICIDE_EDITORWINDOW );
259 listener_ = new ChangesListener(*this);
260 Reference< beans::XMultiPropertySet > n(
261 officecfg::Office::Common::Font::SourceViewFont::get(),
262 UNO_QUERY_THROW);
264 std::unique_lock g(mutex_);
265 notifier_ = n;
268 // The zoom level applied to the editor window is the zoom slider value in the shell
269 nCurrentZoomLevel = GetShell()->GetCurrentZoomSliderValue();
271 const Sequence<OUString> aPropertyNames{"FontHeight", "FontName"};
272 n->addPropertiesChangeListener(aPropertyNames, listener_);
276 EditorWindow::~EditorWindow()
278 disposeOnce();
281 void EditorWindow::dispose()
283 if (m_nSetSourceInBasicId)
285 Application::RemoveUserEvent(m_nSetSourceInBasicId);
286 m_nSetSourceInBasicId = nullptr;
289 Reference< beans::XMultiPropertySet > n;
291 std::unique_lock g(mutex_);
292 n = notifier_;
294 if (n.is()) {
295 n->removePropertiesChangeListener(listener_);
298 aSyntaxIdle.Stop();
300 if ( pEditEngine )
302 EndListening( *pEditEngine );
303 pEditEngine->RemoveView(pEditView.get());
305 pCodeCompleteWnd.disposeAndClear();
306 vcl::Window::dispose();
309 OUString EditorWindow::GetWordAtCursor()
311 OUString aWord;
313 if ( pEditView )
315 TextEngine* pTextEngine = pEditView->GetTextEngine();
316 if ( pTextEngine )
318 // check first, if the cursor is at a help URL
319 const TextSelection& rSelection = pEditView->GetSelection();
320 const TextPaM& rSelStart = rSelection.GetStart();
321 const TextPaM& rSelEnd = rSelection.GetEnd();
322 OUString aText = pTextEngine->GetText( rSelEnd.GetPara() );
323 CharClass aClass( ::comphelper::getProcessComponentContext() , Application::GetSettings().GetLanguageTag() );
324 sal_Int32 nSelStart = rSelStart.GetIndex();
325 sal_Int32 nSelEnd = rSelEnd.GetIndex();
326 sal_Int32 nLength = aText.getLength();
327 sal_Int32 nStart = 0;
328 sal_Int32 nEnd = nLength;
329 while ( nStart < nLength )
331 OUString aURL( URIHelper::FindFirstURLInText( aText, nStart, nEnd, aClass ) );
332 INetURLObject aURLObj( aURL );
333 if ( aURLObj.GetProtocol() == INetProtocol::VndSunStarHelp
334 && nSelStart >= nStart && nSelStart <= nEnd && nSelEnd >= nStart && nSelEnd <= nEnd )
336 aWord = aURL;
337 break;
339 nStart = nEnd;
340 nEnd = nLength;
343 // Not the selected range, but at the CursorPosition,
344 // if a word is partially selected.
345 if ( aWord.isEmpty() )
346 aWord = pTextEngine->GetWord( rSelEnd );
348 // Can be empty when full word selected, as Cursor behind it
349 if ( aWord.isEmpty() && pEditView->HasSelection() )
350 aWord = pTextEngine->GetWord( rSelStart );
354 return aWord;
357 void EditorWindow::RequestHelp( const HelpEvent& rHEvt )
359 bool bDone = false;
361 // Should have been activated at some point
362 if ( pEditEngine )
364 if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
366 OUString aKeyword = GetWordAtCursor();
367 Application::GetHelp()->SearchKeyword( aKeyword );
368 bDone = true;
370 else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
372 OUString aHelpText;
373 tools::Rectangle aHelpRect;
374 if ( StarBASIC::IsRunning() )
376 Point aWindowPos = rHEvt.GetMousePosPixel();
377 aWindowPos = ScreenToOutputPixel( aWindowPos );
378 Point aDocPos = GetEditView()->GetDocPos( aWindowPos );
379 TextPaM aCursor = GetEditView()->GetTextEngine()->GetPaM(aDocPos);
380 TextPaM aStartOfWord;
381 OUString aWord = GetEditView()->GetTextEngine()->GetWord( aCursor, &aStartOfWord );
382 if ( !aWord.isEmpty() && !comphelper::string::isdigitAsciiString(aWord) )
384 sal_uInt16 nLastChar = aWord.getLength() - 1;
385 if ( cSuffixes.find(aWord[ nLastChar ] ) != std::u16string_view::npos )
386 aWord = aWord.replaceAt( nLastChar, 1, u"" );
387 SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord );
388 if (SbxVariable const* pVar = IsSbxVariable(pSBX))
390 SbxDataType eType = pVar->GetType();
391 if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
392 // might cause a crash e. g. at the selections-object
393 // Type == Object does not mean pVar == Object!
394 ; // aHelpText = ((SbxObject*)pVar)->GetClassName();
395 else if ( eType & SbxARRAY )
396 ; // aHelpText = "{...}";
397 else if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxEMPTY) )
399 aHelpText = pVar->GetName();
400 if ( aHelpText.isEmpty() ) // name is not copied with the passed parameters
401 aHelpText = aWord;
402 aHelpText += "=" + pVar->GetOUString();
405 if ( !aHelpText.isEmpty() )
407 tools::Rectangle aStartWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aStartOfWord));
408 TextPaM aEndOfWord(aStartOfWord.GetPara(), aStartOfWord.GetIndex() + aWord.getLength());
409 tools::Rectangle aEndWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aEndOfWord));
410 aHelpRect = aStartWordRect.GetUnion(aEndWordRect);
412 Point aTopLeft = GetEditView()->GetWindowPos(aHelpRect.TopLeft());
413 aTopLeft = GetEditView()->GetWindow()->OutputToScreenPixel(aTopLeft);
415 aHelpRect.SetPos(aTopLeft);
419 Help::ShowQuickHelp( this, aHelpRect, aHelpText, QuickHelpFlags::NONE);
420 bDone = true;
424 if ( !bDone )
425 Window::RequestHelp( rHEvt );
429 void EditorWindow::Resize()
431 // ScrollBars, etc. happens in Adjust...
432 if ( !pEditView )
433 return;
435 tools::Long nVisY = pEditView->GetStartDocPos().Y();
437 pEditView->ShowCursor();
438 Size aOutSz( GetOutputSizePixel() );
439 tools::Long nMaxVisAreaStart = pEditView->GetTextEngine()->GetTextHeight() - aOutSz.Height();
440 if ( nMaxVisAreaStart < 0 )
441 nMaxVisAreaStart = 0;
442 if ( pEditView->GetStartDocPos().Y() > nMaxVisAreaStart )
444 Point aStartDocPos( pEditView->GetStartDocPos() );
445 aStartDocPos.setY( nMaxVisAreaStart );
446 pEditView->SetStartDocPos( aStartDocPos );
447 pEditView->ShowCursor();
448 rModulWindow.GetBreakPointWindow().GetCurYOffset() = aStartDocPos.Y();
449 rModulWindow.GetLineNumberWindow().GetCurYOffset() = aStartDocPos.Y();
451 InitScrollBars();
452 if ( nVisY != pEditView->GetStartDocPos().Y() )
453 Invalidate();
457 void EditorWindow::MouseMove( const MouseEvent &rEvt )
459 if ( pEditView )
460 pEditView->MouseMove( rEvt );
464 void EditorWindow::MouseButtonUp( const MouseEvent &rEvt )
466 if ( pEditView )
468 pEditView->MouseButtonUp( rEvt );
469 if (SfxBindings* pBindings = GetBindingsPtr())
471 pBindings->Invalidate( SID_BASICIDE_STAT_POS );
472 pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
477 void EditorWindow::MouseButtonDown( const MouseEvent &rEvt )
479 GrabFocus();
480 if (!pEditView)
481 return;
482 pEditView->MouseButtonDown(rEvt);
483 if( pCodeCompleteWnd->IsVisible() )
485 if (pEditView->GetSelection() != pCodeCompleteWnd->GetTextSelection())
487 //selection changed, code complete window should be hidden
488 pCodeCompleteWnd->HideAndRestoreFocus();
493 void EditorWindow::Command( const CommandEvent& rCEvt )
495 if ( !pEditView )
496 return;
498 pEditView->Command( rCEvt );
499 if ( ( rCEvt.GetCommand() == CommandEventId::Wheel ) ||
500 ( rCEvt.GetCommand() == CommandEventId::StartAutoScroll ) ||
501 ( rCEvt.GetCommand() == CommandEventId::AutoScroll ) )
503 const CommandWheelData* pData = rCEvt.GetWheelData();
505 // Check if it is a Ctrl+Wheel zoom command
506 if (pData && pData->IsMod1())
508 const sal_uInt16 nOldZoom = GetCurrentZoom();
509 sal_uInt16 nNewZoom;
510 if( pData->GetDelta() < 0 )
511 nNewZoom = std::max<sal_uInt16>(basctl::Shell::GetMinZoom(),
512 basegfx::zoomtools::zoomOut(nOldZoom));
513 else
514 nNewZoom = std::min<sal_uInt16>(basctl::Shell::GetMaxZoom(),
515 basegfx::zoomtools::zoomIn(nOldZoom));
516 GetShell()->SetGlobalEditorZoomLevel(nNewZoom);
518 else
519 HandleScrollCommand(rCEvt, &rModulWindow.GetEditHScrollBar(), &rModulWindow.GetEditVScrollBar());
521 else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) {
522 SfxDispatcher* pDispatcher = GetDispatcher();
523 if ( pDispatcher )
525 SfxDispatcher::ExecutePopup();
527 if( pCodeCompleteWnd->IsVisible() ) // hide the code complete window
528 pCodeCompleteWnd->ClearAndHide();
532 bool EditorWindow::ImpCanModify()
534 bool bCanModify = true;
535 if ( StarBASIC::IsRunning() && rModulWindow.GetBasicStatus().bIsRunning )
537 // If in Trace-mode, abort the trace or refuse input
538 // Remove markers in the modules in Notify at Basic::Stopped
539 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
540 VclMessageType::Question, VclButtonsType::OkCancel,
541 IDEResId(RID_STR_WILLSTOPPRG)));
542 if (xQueryBox->run() == RET_OK)
544 rModulWindow.GetBasicStatus().bIsRunning = false;
545 StopBasic();
547 else
548 bCanModify = false;
550 return bCanModify;
553 void EditorWindow::KeyInput( const KeyEvent& rKEvt )
555 if ( !pEditView ) // Happens in Win95
556 return;
558 bool const bWasModified = pEditEngine->IsModified();
559 // see if there is an accelerator to be processed first
560 SfxViewShell *pVS( SfxViewShell::Current());
561 bool bDone = pVS && pVS->KeyInput( rKEvt );
563 if (pCodeCompleteWnd->IsVisible() && CodeCompleteOptions::IsCodeCompleteOn())
565 if (pCodeCompleteWnd->HandleKeyInput(rKEvt))
566 return;
569 if( (rKEvt.GetKeyCode().GetCode() == KEY_SPACE ||
570 rKEvt.GetKeyCode().GetCode() == KEY_TAB ||
571 rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) && CodeCompleteOptions::IsAutoCorrectOn() )
573 HandleAutoCorrect();
576 if( rKEvt.GetCharCode() == '"' && CodeCompleteOptions::IsAutoCloseQuotesOn() )
577 {//autoclose double quotes
578 HandleAutoCloseDoubleQuotes();
581 if( rKEvt.GetCharCode() == '(' && CodeCompleteOptions::IsAutoCloseParenthesisOn() )
582 {//autoclose parenthesis
583 HandleAutoCloseParen();
586 if( rKEvt.GetKeyCode().GetCode() == KEY_RETURN && CodeCompleteOptions::IsProcedureAutoCompleteOn() )
587 {//autoclose implementation
588 HandleProcedureCompletion();
591 if( rKEvt.GetKeyCode().GetCode() == KEY_POINT && CodeCompleteOptions::IsCodeCompleteOn() )
593 HandleCodeCompletion();
595 if ( !bDone && ( !TextEngine::DoesKeyChangeText( rKEvt ) || ImpCanModify() ) )
597 if ( ( rKEvt.GetKeyCode().GetCode() == KEY_TAB ) && !rKEvt.GetKeyCode().IsMod1() &&
598 !rKEvt.GetKeyCode().IsMod2() && !GetEditView()->IsReadOnly() )
600 TextSelection aSel( pEditView->GetSelection() );
601 if ( aSel.GetStart().GetPara() != aSel.GetEnd().GetPara() )
603 bDelayHighlight = false;
604 if ( !rKEvt.GetKeyCode().IsShift() )
605 pEditView->IndentBlock();
606 else
607 pEditView->UnindentBlock();
608 bDelayHighlight = true;
609 bDone = true;
612 if ( !bDone )
613 bDone = pEditView->KeyInput( rKEvt );
615 if ( !bDone )
617 Window::KeyInput( rKEvt );
619 else
621 if (SfxBindings* pBindings = GetBindingsPtr())
623 pBindings->Invalidate( SID_BASICIDE_STAT_POS );
624 pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
625 if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR )
627 pBindings->Update( SID_BASICIDE_STAT_POS );
628 pBindings->Update( SID_BASICIDE_STAT_TITLE );
630 if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ||
631 rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM )
633 // If the module is read-only, warn that it can't be edited
634 if ( rModulWindow.IsReadOnly() )
635 rModulWindow.ShowReadOnlyInfoBar();
637 if ( !bWasModified && pEditEngine->IsModified() )
639 pBindings->Invalidate( SID_SAVEDOC );
640 pBindings->Invalidate( SID_DOC_MODIFIED );
641 pBindings->Invalidate( SID_UNDO );
643 if ( rKEvt.GetKeyCode().GetCode() == KEY_INSERT )
644 pBindings->Invalidate( SID_ATTR_INSERT );
649 void EditorWindow::HandleAutoCorrect()
651 TextSelection aSel = GetEditView()->GetSelection();
652 const sal_uInt32 nLine = aSel.GetStart().GetPara();
653 const sal_Int32 nIndex = aSel.GetStart().GetIndex();
654 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
655 const OUString& sActSubName = GetActualSubName( nLine ); // the actual procedure
657 std::vector<HighlightPortion> aPortions;
658 aHighlighter.getHighlightPortions( aLine, aPortions );
660 if( aPortions.empty() )
661 return;
663 HighlightPortion& r = aPortions.back();
664 if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
665 {//cursor is not standing at the end of the line
666 for (auto const& portion : aPortions)
668 if( portion.nEnd == nIndex )
670 r = portion;
671 break;
676 OUString sStr = aLine.copy( r.nBegin, r.nEnd - r.nBegin );
677 //if WS or empty string: stop, nothing to do
678 if( ( r.tokenType == TokenType::Whitespace ) || sStr.isEmpty() )
679 return;
680 //create the appropriate TextSelection, and update the cache
681 TextPaM aStart( nLine, r.nBegin );
682 TextPaM aEnd( nLine, r.nBegin + sStr.getLength() );
683 TextSelection sTextSelection( aStart, aEnd );
684 rModulWindow.UpdateModule();
685 rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse( aCodeCompleteCache );
686 // correct the last entered keyword
687 if( r.tokenType == TokenType::Keywords )
689 sStr = sStr.toAsciiLowerCase();
690 if( !SbModule::GetKeywordCase(sStr).isEmpty() )
691 // if it is a keyword, get its correct case
692 sStr = SbModule::GetKeywordCase(sStr);
693 else
694 // else capitalize first letter/select the correct one, and replace
695 sStr = sStr.replaceAt( 0, 1, OUString(sStr[0]).toAsciiUpperCase() );
697 pEditEngine->ReplaceText( sTextSelection, sStr );
698 pEditView->SetSelection( aSel );
700 if( r.tokenType != TokenType::Identifier )
701 return;
703 // correct variables
704 if( !aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName ).isEmpty() )
706 sStr = aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName );
707 pEditEngine->ReplaceText( sTextSelection, sStr );
708 pEditView->SetSelection( aSel );
710 else
712 //autocorrect procedures
713 SbxArray* pArr = rModulWindow.GetSbModule()->GetMethods().get();
714 for (sal_uInt32 i = 0; i < pArr->Count(); ++i)
716 if (pArr->Get(i)->GetName().equalsIgnoreAsciiCase(sStr))
718 sStr = pArr->Get(i)->GetName(); //if found, get the correct case
719 pEditEngine->ReplaceText( sTextSelection, sStr );
720 pEditView->SetSelection( aSel );
721 return;
727 TextSelection EditorWindow::GetLastHighlightPortionTextSelection() const
728 {//creates a text selection from the highlight portion on the cursor
729 const sal_uInt32 nLine = GetEditView()->GetSelection().GetStart().GetPara();
730 const sal_Int32 nIndex = GetEditView()->GetSelection().GetStart().GetIndex();
731 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
732 std::vector<HighlightPortion> aPortions;
733 aHighlighter.getHighlightPortions( aLine, aPortions );
735 assert(!aPortions.empty());
736 HighlightPortion& r = aPortions.back();
737 if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
738 {//cursor is not standing at the end of the line
739 for (auto const& portion : aPortions)
741 if( portion.nEnd == nIndex )
743 r = portion;
744 break;
749 if( aPortions.empty() )
750 return TextSelection();
752 std::u16string_view sStr = aLine.subView( r.nBegin, r.nEnd - r.nBegin );
753 TextPaM aStart( nLine, r.nBegin );
754 TextPaM aEnd( nLine, r.nBegin + sStr.size() );
755 return TextSelection( aStart, aEnd );
758 void EditorWindow::HandleAutoCloseParen()
760 TextSelection aSel = GetEditView()->GetSelection();
761 const sal_uInt32 nLine = aSel.GetStart().GetPara();
762 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
764 if( aLine.getLength() > 0 && aLine[aSel.GetEnd().GetIndex()-1] != '(' )
766 GetEditView()->InsertText(")");
767 //leave the cursor on its place: inside the parenthesis
768 TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
769 GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
773 void EditorWindow::HandleAutoCloseDoubleQuotes()
775 TextSelection aSel = GetEditView()->GetSelection();
776 const sal_uInt32 nLine = aSel.GetStart().GetPara();
777 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
779 std::vector<HighlightPortion> aPortions;
780 aHighlighter.getHighlightPortions( aLine, aPortions );
782 if( aPortions.empty() )
783 return;
785 if( aLine.getLength() > 0 && !aLine.endsWith("\"") && (aPortions.back().tokenType != TokenType::String) )
787 GetEditView()->InsertText("\"");
788 //leave the cursor on its place: inside the two double quotes
789 TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
790 GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
794 void EditorWindow::HandleProcedureCompletion()
797 TextSelection aSel = GetEditView()->GetSelection();
798 const sal_uInt32 nLine = aSel.GetStart().GetPara();
799 OUString aLine( pEditEngine->GetText( nLine ) );
801 OUString sProcType;
802 OUString sProcName;
803 bool bFoundName = GetProcedureName(aLine, sProcType, sProcName);
804 if (!bFoundName)
805 return;
807 OUString sText("\nEnd ");
808 aSel = GetEditView()->GetSelection();
809 if( sProcType.equalsIgnoreAsciiCase("function") )
810 sText += "Function\n";
811 if( sProcType.equalsIgnoreAsciiCase("sub") )
812 sText += "Sub\n";
814 if( nLine+1 == pEditEngine->GetParagraphCount() )
816 pEditView->InsertText( sText );//append to the end
817 GetEditView()->SetSelection(aSel);
819 else
821 for( sal_uInt32 i = nLine+1; i < pEditEngine->GetParagraphCount(); ++i )
822 {//searching forward for end token, or another sub/function definition
823 OUString aCurrLine = pEditEngine->GetText( i );
824 std::vector<HighlightPortion> aCurrPortions;
825 aHighlighter.getHighlightPortions( aCurrLine, aCurrPortions );
827 if( aCurrPortions.size() >= 3 )
828 {//at least 3 tokens: (sub|function) whitespace identifier...
829 HighlightPortion& r = aCurrPortions.front();
830 std::u16string_view sStr = aCurrLine.subView(r.nBegin, r.nEnd - r.nBegin);
832 if( r.tokenType == TokenType::Keywords )
834 if( o3tl::equalsIgnoreAsciiCase(sStr, u"sub") || o3tl::equalsIgnoreAsciiCase(sStr, u"function") )
836 pEditView->InsertText( sText );//append to the end
837 GetEditView()->SetSelection(aSel);
838 break;
840 if( o3tl::equalsIgnoreAsciiCase(sStr, u"end") )
841 break;
848 bool EditorWindow::GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const
850 std::vector<HighlightPortion> aPortions;
851 aHighlighter.getHighlightPortions(rLine, aPortions);
853 if( aPortions.empty() )
854 return false;
856 bool bFoundType = false;
857 bool bFoundName = false;
859 for (auto const& portion : aPortions)
861 std::u16string_view sTokStr = rLine.substr(portion.nBegin, portion.nEnd - portion.nBegin);
863 if( portion.tokenType == TokenType::Keywords && ( o3tl::equalsIgnoreAsciiCase(sTokStr, u"sub")
864 || o3tl::equalsIgnoreAsciiCase(sTokStr, u"function")) )
866 rProcType = sTokStr;
867 bFoundType = true;
869 if( portion.tokenType == TokenType::Identifier && bFoundType )
871 rProcName = sTokStr;
872 bFoundName = true;
873 break;
877 if( !bFoundType || !bFoundName )
878 return false;// no sub/function keyword or there is no identifier
880 return true;
884 void EditorWindow::HandleCodeCompletion()
886 rModulWindow.UpdateModule();
887 rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse(aCodeCompleteCache);
888 TextSelection aSel = GetEditView()->GetSelection();
889 const sal_uInt32 nLine = aSel.GetStart().GetPara();
890 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
891 std::vector< OUString > aVect; //vector to hold the base variable+methods for the nested reflection
893 std::vector<HighlightPortion> aPortions;
894 aLine = aLine.copy(0, aSel.GetEnd().GetIndex());
895 aHighlighter.getHighlightPortions( aLine, aPortions );
896 if( aPortions.empty() )
897 return;
899 //use the syntax highlighter to grab out nested reflection calls, eg. aVar.aMethod("aa").aOtherMethod ..
900 for( std::vector<HighlightPortion>::reverse_iterator i(
901 aPortions.rbegin());
902 i != aPortions.rend(); ++i)
904 if( i->tokenType == TokenType::Whitespace ) // a whitespace: stop; if there is no ws, it goes to the beginning of the line
905 break;
906 if( i->tokenType == TokenType::Identifier || i->tokenType == TokenType::Keywords ) // extract the identifiers(methods, base variable)
907 /* an example: Dim aLocVar2 as com.sun.star.beans.PropertyValue
908 * here, aLocVar2.Name, and PropertyValue's Name field is treated as a keyword(?!)
909 * */
910 aVect.insert( aVect.begin(), aLine.copy(i->nBegin, i->nEnd - i->nBegin) );
913 if( aVect.empty() )//nothing to do
914 return;
916 OUString sBaseName = aVect[aVect.size()-1];//variable name
917 OUString sVarType = aCodeCompleteCache.GetVarType( sBaseName );
919 if( !sVarType.isEmpty() && CodeCompleteOptions::IsAutoCorrectOn() )
920 {//correct variable name, if autocorrection on
921 const OUString& sStr = aCodeCompleteCache.GetCorrectCaseVarName( sBaseName, GetActualSubName(nLine) );
922 if( !sStr.isEmpty() )
924 TextPaM aStart(nLine, aSel.GetStart().GetIndex() - sStr.getLength() );
925 TextSelection sTextSelection(aStart, TextPaM(nLine, aSel.GetStart().GetIndex()));
926 pEditEngine->ReplaceText( sTextSelection, sStr );
927 pEditView->SetSelection( aSel );
931 UnoTypeCodeCompletetor aTypeCompletor( aVect, sVarType );
933 if( !aTypeCompletor.CanCodeComplete() )
934 return;
936 std::vector< OUString > aEntryVect;//entries to be inserted into the list
937 std::vector< OUString > aFieldVect = aTypeCompletor.GetXIdlClassFields();//fields
938 aEntryVect.insert(aEntryVect.end(), aFieldVect.begin(), aFieldVect.end() );
939 if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
940 {// if extended types on, reflect classes, else just the structs (XIdlClass without methods)
941 std::vector< OUString > aMethVect = aTypeCompletor.GetXIdlClassMethods();//methods
942 aEntryVect.insert(aEntryVect.end(), aMethVect.begin(), aMethVect.end() );
944 if( !aEntryVect.empty() )
945 SetupAndShowCodeCompleteWnd( aEntryVect, aSel );
948 void EditorWindow::SetupAndShowCodeCompleteWnd( const std::vector< OUString >& aEntryVect, TextSelection aSel )
950 // clear the listbox
951 pCodeCompleteWnd->ClearListBox();
952 // fill the listbox
953 for(const auto & l : aEntryVect)
955 pCodeCompleteWnd->InsertEntry( l );
957 // show it
958 pCodeCompleteWnd->Show();
959 pCodeCompleteWnd->ResizeAndPositionListBox();
960 pCodeCompleteWnd->SelectFirstEntry();
961 // correct text selection, and set it
962 ++aSel.GetStart().GetIndex();
963 ++aSel.GetEnd().GetIndex();
964 pCodeCompleteWnd->SetTextSelection( aSel );
965 //give the focus to the EditView
966 pEditView->GetWindow()->GrabFocus();
969 void EditorWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
971 if (!pEditEngine) // We need it now at latest
972 CreateEditEngine();
974 pEditView->Paint(rRenderContext, rRect);
977 void EditorWindow::LoseFocus()
979 // tdf#114258 wait until the next event loop cycle to do this so it doesn't
980 // happen during a mouse down/up selection in the treeview whose contents
981 // this may update
982 if (!m_nSetSourceInBasicId)
983 m_nSetSourceInBasicId = Application::PostUserEvent(LINK(this, EditorWindow, SetSourceInBasicHdl));
984 Window::LoseFocus();
987 IMPL_LINK_NOARG(EditorWindow, SetSourceInBasicHdl, void*, void)
989 m_nSetSourceInBasicId = nullptr;
990 SetSourceInBasic();
993 void EditorWindow::SetSourceInBasic()
995 if ( pEditEngine && pEditEngine->IsModified()
996 && !GetEditView()->IsReadOnly() ) // Added for #i60626, otherwise
997 // any read only bug in the text engine could lead to a crash later
999 if ( !StarBASIC::IsRunning() ) // Not at runtime!
1001 rModulWindow.UpdateModule();
1006 // Returns the position of the last character of any of the following
1007 // EOL char combinations: CR, CR/LF, LF, return -1 if no EOL is found
1008 sal_Int32 searchEOL( std::u16string_view rStr, sal_Int32 fromIndex )
1010 size_t iLF = rStr.find( LINE_SEP, fromIndex );
1011 if( iLF != std::u16string_view::npos )
1012 return iLF;
1014 size_t iCR = rStr.find( LINE_SEP_CR, fromIndex );
1015 return iCR == std::u16string_view::npos ? -1 : iCR;
1018 void EditorWindow::CreateEditEngine()
1020 if (pEditEngine)
1021 return;
1023 pEditEngine.reset(new ExtTextEngine);
1024 pEditView.reset(new TextView(pEditEngine.get(), this));
1025 pEditView->SetAutoIndentMode(true);
1026 pEditEngine->SetUpdateMode(false);
1027 pEditEngine->InsertView(pEditView.get());
1029 ImplSetFont();
1031 aSyntaxIdle.SetInvokeHandler( LINK( this, EditorWindow, SyntaxTimerHdl ) );
1033 bool bWasDoSyntaxHighlight = bDoSyntaxHighlight;
1034 bDoSyntaxHighlight = false; // too slow for large texts...
1035 OUString aOUSource(rModulWindow.GetModule());
1036 sal_Int32 nLines = 0;
1037 sal_Int32 nIndex = -1;
1040 nLines++;
1041 nIndex = searchEOL( aOUSource, nIndex+1 );
1043 while (nIndex >= 0);
1045 // nLines*4: SetText+Formatting+DoHighlight+Formatting
1046 // it could be cut down on one formatting but you would wait even longer
1047 // for the text then if the source code is long...
1048 pProgress.reset(new ProgressInfo(GetShell()->GetViewFrame().GetObjectShell(),
1049 IDEResId(RID_STR_GENERATESOURCE),
1050 nLines * 4));
1051 setTextEngineText(*pEditEngine, aOUSource);
1053 pEditView->SetStartDocPos(Point(0, 0));
1054 pEditView->SetSelection(TextSelection());
1055 rModulWindow.GetBreakPointWindow().GetCurYOffset() = 0;
1056 rModulWindow.GetLineNumberWindow().GetCurYOffset() = 0;
1057 pEditEngine->SetUpdateMode(true);
1058 rModulWindow.PaintImmediately(); // has only been invalidated at UpdateMode = true
1060 pEditView->ShowCursor();
1062 StartListening(*pEditEngine);
1064 aSyntaxIdle.Stop();
1065 bDoSyntaxHighlight = bWasDoSyntaxHighlight;
1067 for (sal_Int32 nLine = 0; nLine < nLines; nLine++)
1068 aSyntaxLineTable.insert(nLine);
1069 ForceSyntaxTimeout();
1071 pProgress.reset();
1073 pEditEngine->SetModified( false );
1074 pEditEngine->EnableUndo( true );
1076 InitScrollBars();
1078 if (SfxBindings* pBindings = GetBindingsPtr())
1080 pBindings->Invalidate(SID_BASICIDE_STAT_POS);
1081 pBindings->Invalidate(SID_BASICIDE_STAT_TITLE);
1084 DBG_ASSERT(rModulWindow.GetBreakPointWindow().GetCurYOffset() == 0, "CreateEditEngine: breakpoints moved?");
1086 // set readonly mode for readonly libraries
1087 ScriptDocument aDocument(rModulWindow.GetDocument());
1088 OUString aOULibName(rModulWindow.GetLibName());
1089 Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY );
1090 if (xModLibContainer.is()
1091 && xModLibContainer->hasByName(aOULibName)
1092 && xModLibContainer->isLibraryReadOnly(aOULibName))
1094 rModulWindow.SetReadOnly(true);
1097 if (aDocument.isDocument() && aDocument.isReadOnly())
1098 rModulWindow.SetReadOnly(true);
1101 void EditorWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1103 TextHint const* pTextHint = dynamic_cast<TextHint const*>(&rHint);
1104 if (!pTextHint)
1105 return;
1107 TextHint const& rTextHint = *pTextHint;
1108 if( rTextHint.GetId() == SfxHintId::TextViewScrolled )
1110 rModulWindow.GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() );
1111 rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1112 rModulWindow.GetBreakPointWindow().DoScroll
1113 ( rModulWindow.GetBreakPointWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1114 rModulWindow.GetLineNumberWindow().DoScroll
1115 ( rModulWindow.GetLineNumberWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1117 else if( rTextHint.GetId() == SfxHintId::TextHeightChanged )
1119 if ( pEditView->GetStartDocPos().Y() )
1121 tools::Long nOutHeight = GetOutputSizePixel().Height();
1122 tools::Long nTextHeight = pEditEngine->GetTextHeight();
1123 if ( nTextHeight < nOutHeight )
1124 pEditView->Scroll( 0, pEditView->GetStartDocPos().Y() );
1126 rModulWindow.GetLineNumberWindow().Invalidate();
1129 SetScrollBarRanges();
1131 else if( rTextHint.GetId() == SfxHintId::TextFormatted )
1134 const tools::Long nWidth = pEditEngine->CalcTextWidth();
1135 if ( nWidth != nCurTextWidth )
1137 nCurTextWidth = nWidth;
1138 rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1) );
1139 rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1141 tools::Long nPrevTextWidth = nCurTextWidth;
1142 nCurTextWidth = pEditEngine->CalcTextWidth();
1143 if ( nCurTextWidth != nPrevTextWidth )
1144 SetScrollBarRanges();
1146 else if( rTextHint.GetId() == SfxHintId::TextParaInserted )
1148 ParagraphInsertedDeleted( rTextHint.GetValue(), true );
1149 DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1151 else if( rTextHint.GetId() == SfxHintId::TextParaRemoved )
1153 ParagraphInsertedDeleted( rTextHint.GetValue(), false );
1155 else if( rTextHint.GetId() == SfxHintId::TextParaContentChanged )
1157 DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1159 else if( rTextHint.GetId() == SfxHintId::TextViewSelectionChanged )
1161 if (SfxBindings* pBindings = GetBindingsPtr())
1163 pBindings->Invalidate( SID_CUT );
1164 pBindings->Invalidate( SID_COPY );
1169 OUString EditorWindow::GetActualSubName( sal_uInt32 nLine )
1171 SbxArrayRef pMethods = rModulWindow.GetSbModule()->GetMethods();
1172 for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
1174 SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i));
1175 if( pMeth )
1177 sal_uInt16 l1,l2;
1178 pMeth->GetLineRange(l1,l2);
1179 if( (l1 <= nLine+1) && (nLine+1 <= l2) )
1181 return pMeth->GetName();
1185 return OUString();
1188 void EditorWindow::SetScrollBarRanges()
1190 // extra method, not InitScrollBars, because for EditEngine events too
1191 if ( !pEditEngine )
1192 return;
1194 rModulWindow.GetEditVScrollBar().SetRange( Range( 0, pEditEngine->GetTextHeight()-1 ) );
1195 rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1 ) );
1198 void EditorWindow::InitScrollBars()
1200 if (!pEditEngine)
1201 return;
1203 SetScrollBarRanges();
1204 Size aOutSz(GetOutputSizePixel());
1205 rModulWindow.GetEditVScrollBar().SetVisibleSize(aOutSz.Height());
1206 rModulWindow.GetEditVScrollBar().SetPageSize(aOutSz.Height() * 8 / 10);
1207 rModulWindow.GetEditVScrollBar().SetLineSize(GetTextHeight());
1208 rModulWindow.GetEditVScrollBar().SetThumbPos(pEditView->GetStartDocPos().Y());
1209 rModulWindow.GetEditVScrollBar().Show();
1211 rModulWindow.GetEditHScrollBar().SetVisibleSize(aOutSz.Width());
1212 rModulWindow.GetEditHScrollBar().SetPageSize(aOutSz.Width() * 8 / 10);
1213 rModulWindow.GetEditHScrollBar().SetLineSize(GetTextWidth( "x" ));
1214 rModulWindow.GetEditHScrollBar().SetThumbPos(pEditView->GetStartDocPos().X());
1215 rModulWindow.GetEditHScrollBar().Show();
1218 void EditorWindow::ImpDoHighlight( sal_uInt32 nLine )
1220 if ( !bDoSyntaxHighlight )
1221 return;
1223 OUString aLine( pEditEngine->GetText( nLine ) );
1224 bool const bWasModified = pEditEngine->IsModified();
1225 pEditEngine->RemoveAttribs( nLine );
1226 std::vector<HighlightPortion> aPortions;
1227 aHighlighter.getHighlightPortions( aLine, aPortions );
1229 for (auto const& portion : aPortions)
1231 Color const aColor = rModulWindow.GetLayout().GetSyntaxColor(portion.tokenType);
1232 pEditEngine->SetAttrib(TextAttribFontColor(aColor), nLine, portion.nBegin, portion.nEnd);
1235 pEditEngine->SetModified(bWasModified);
1238 void EditorWindow::ChangeFontColor( Color aColor )
1240 if (pEditEngine)
1242 vcl::Font aFont(pEditEngine->GetFont());
1243 aFont.SetColor(aColor);
1244 pEditEngine->SetFont(aFont);
1248 void EditorWindow::UpdateSyntaxHighlighting ()
1250 const sal_uInt32 nCount = pEditEngine->GetParagraphCount();
1251 for (sal_uInt32 i = 0; i < nCount; ++i)
1252 DoDelayedSyntaxHighlight(i);
1255 void EditorWindow::ImplSetFont()
1257 // Get default font name and height defined in the Options dialog
1258 OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString()));
1259 if (sFontName.isEmpty())
1261 vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED,
1262 Application::GetSettings().GetUILanguageTag().getLanguageType(),
1263 GetDefaultFontFlags::NONE, GetOutDev()));
1264 sFontName = aTmpFont.GetFamilyName();
1266 sal_uInt16 nDefaultFontHeight = officecfg::Office::Common::Font::SourceViewFont::FontHeight::get();
1268 // Calculate font size considering zoom level
1269 sal_uInt16 nNewFontHeight = nDefaultFontHeight * (static_cast<float>(nCurrentZoomLevel) / 100);
1270 Size aFontSize(0, nNewFontHeight);
1272 vcl::Font aFont(sFontName, aFontSize);
1273 aFont.SetColor(rModulWindow.GetLayout().GetFontColor());
1274 SetPointFont(*GetOutDev(), aFont); // FIXME RenderContext
1275 aFont = GetFont();
1277 rModulWindow.GetBreakPointWindow().SetFont(aFont);
1278 rModulWindow.GetLineNumberWindow().SetFont(aFont);
1279 rModulWindow.Invalidate();
1281 if (pEditEngine)
1283 bool const bModified = pEditEngine->IsModified();
1284 pEditEngine->SetFont(aFont);
1285 pEditEngine->SetModified(bModified);
1288 // Update controls
1289 if (SfxBindings* pBindings = GetBindingsPtr())
1291 pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM );
1292 pBindings->Invalidate( SID_ATTR_ZOOMSLIDER );
1296 void EditorWindow::SetEditorZoomLevel(sal_uInt16 nNewZoomLevel)
1298 if (nCurrentZoomLevel == nNewZoomLevel)
1299 return;
1301 if (nNewZoomLevel < MIN_ZOOM_LEVEL || nNewZoomLevel > MAX_ZOOM_LEVEL)
1302 return;
1304 nCurrentZoomLevel = nNewZoomLevel;
1305 ImplSetFont();
1308 void EditorWindow::DoSyntaxHighlight( sal_uInt32 nPara )
1310 // because of the DelayedSyntaxHighlight it's possible
1311 // that this line does not exist anymore!
1312 if ( nPara < pEditEngine->GetParagraphCount() )
1314 // unfortunately I'm not sure that exactly this line does Modified()...
1315 if ( pProgress )
1316 pProgress->StepProgress();
1317 ImpDoHighlight( nPara );
1321 void EditorWindow::DoDelayedSyntaxHighlight( sal_uInt32 nPara )
1323 // line is only added to list, processed in TimerHdl
1324 // => don't manipulate breaks while EditEngine is formatting
1325 if ( pProgress )
1326 pProgress->StepProgress();
1328 if ( !bHighlighting && bDoSyntaxHighlight )
1330 if ( bDelayHighlight )
1332 aSyntaxLineTable.insert( nPara );
1333 aSyntaxIdle.Start();
1335 else
1336 DoSyntaxHighlight( nPara );
1340 IMPL_LINK_NOARG(EditorWindow, SyntaxTimerHdl, Timer *, void)
1342 DBG_ASSERT( pEditView, "Not yet a View, but Syntax-Highlight?!" );
1344 bool const bWasModified = pEditEngine->IsModified();
1345 //pEditEngine->SetUpdateMode(false);
1347 bHighlighting = true;
1348 for (auto const& syntaxLine : aSyntaxLineTable)
1350 DoSyntaxHighlight(syntaxLine);
1353 // #i45572#
1354 if ( pEditView )
1355 pEditView->ShowCursor( false );
1357 pEditEngine->SetModified( bWasModified );
1359 aSyntaxLineTable.clear();
1360 bHighlighting = false;
1363 void EditorWindow::ParagraphInsertedDeleted( sal_uInt32 nPara, bool bInserted )
1365 if ( pProgress )
1366 pProgress->StepProgress();
1368 if ( !bInserted && ( nPara == TEXT_PARA_ALL ) )
1370 rModulWindow.GetBreakPoints().reset();
1371 rModulWindow.GetBreakPointWindow().Invalidate();
1372 rModulWindow.GetLineNumberWindow().Invalidate();
1374 else
1376 rModulWindow.GetBreakPoints().AdjustBreakPoints( static_cast<sal_uInt16>(nPara)+1, bInserted );
1378 tools::Long nLineHeight = GetTextHeight();
1379 Size aSz = rModulWindow.GetBreakPointWindow().GetOutDev()->GetOutputSize();
1380 tools::Rectangle aInvRect( Point( 0, 0 ), aSz );
1381 tools::Long nY = nPara*nLineHeight - rModulWindow.GetBreakPointWindow().GetCurYOffset();
1382 aInvRect.SetTop( nY );
1383 rModulWindow.GetBreakPointWindow().Invalidate( aInvRect );
1385 Size aLnSz(rModulWindow.GetLineNumberWindow().GetWidth(),
1386 GetOutputSizePixel().Height() - 2 * DWBORDER);
1387 rModulWindow.GetLineNumberWindow().SetPosSizePixel(Point(DWBORDER + 19, DWBORDER), aLnSz);
1388 rModulWindow.GetLineNumberWindow().Invalidate();
1392 void EditorWindow::CreateProgress( const OUString& rText, sal_uInt32 nRange )
1394 DBG_ASSERT( !pProgress, "ProgressInfo exists already" );
1395 pProgress.reset(new ProgressInfo(
1396 GetShell()->GetViewFrame().GetObjectShell(),
1397 rText,
1398 nRange
1402 void EditorWindow::DestroyProgress()
1404 pProgress.reset();
1407 void EditorWindow::ForceSyntaxTimeout()
1409 aSyntaxIdle.Stop();
1410 aSyntaxIdle.Invoke();
1413 FactoryFunction EditorWindow::GetUITestFactory() const
1415 return EditorWindowUIObject::create;
1419 // BreakPointWindow
1421 BreakPointWindow::BreakPointWindow (vcl::Window* pParent, ModulWindow* pModulWindow)
1422 : Window(pParent, WB_BORDER)
1423 , rModulWindow(*pModulWindow)
1424 , nCurYOffset(0) // memorize nCurYOffset and not take it from EditEngine
1425 , nMarkerPos(NoMarker)
1426 , bErrorMarker(false)
1428 setBackgroundColor(GetSettings().GetStyleSettings().GetFieldColor());
1429 SetHelpId(HID_BASICIDE_BREAKPOINTWINDOW);
1432 void BreakPointWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1434 if (SyncYOffset())
1435 return;
1437 Size const aOutSz = rRenderContext.GetOutputSize();
1438 tools::Long const nLineHeight = rRenderContext.GetTextHeight();
1440 Image const aBrk[2] =
1442 GetImage(RID_BMP_BRKDISABLED),
1443 GetImage(RID_BMP_BRKENABLED)
1446 Size const aBmpSz = rRenderContext.PixelToLogic(aBrk[1].GetSizePixel());
1447 Point const aBmpOff((aOutSz.Width() - aBmpSz.Width()) / 2,
1448 (nLineHeight - aBmpSz.Height()) / 2);
1450 for (size_t i = 0, n = GetBreakPoints().size(); i < n; ++i)
1452 BreakPoint& rBrk = GetBreakPoints().at(i);
1453 sal_uInt16 const nLine = rBrk.nLine - 1;
1454 size_t const nY = nLine*nLineHeight - nCurYOffset;
1455 rRenderContext.DrawImage(Point(0, nY) + aBmpOff, aBrk[rBrk.bEnabled]);
1458 ShowMarker(rRenderContext);
1461 void BreakPointWindow::ShowMarker(vcl::RenderContext& rRenderContext)
1463 if (nMarkerPos == NoMarker)
1464 return;
1466 Size const aOutSz = GetOutDev()->GetOutputSize();
1467 tools::Long const nLineHeight = GetTextHeight();
1469 Image aMarker = GetImage(bErrorMarker ? OUString(RID_BMP_ERRORMARKER) : OUString(RID_BMP_STEPMARKER));
1471 Size aMarkerSz(aMarker.GetSizePixel());
1472 aMarkerSz = rRenderContext.PixelToLogic(aMarkerSz);
1473 Point aMarkerOff(0, 0);
1474 aMarkerOff.setX( (aOutSz.Width() - aMarkerSz.Width()) / 2 );
1475 aMarkerOff.setY( (nLineHeight - aMarkerSz.Height()) / 2 );
1477 tools::Long nY = nMarkerPos * nLineHeight - nCurYOffset;
1478 Point aPos(0, nY);
1479 aPos += aMarkerOff;
1481 rRenderContext.DrawImage(aPos, aMarker);
1484 void BreakPointWindow::DoScroll( tools::Long nVertScroll )
1486 nCurYOffset -= nVertScroll;
1487 Window::Scroll( 0, nVertScroll );
1490 void BreakPointWindow::SetMarkerPos( sal_uInt16 nLine, bool bError )
1492 if ( SyncYOffset() )
1493 PaintImmediately();
1495 nMarkerPos = nLine;
1496 bErrorMarker = bError;
1497 Invalidate();
1500 void BreakPointWindow::SetNoMarker ()
1502 SetMarkerPos(NoMarker);
1505 BreakPoint* BreakPointWindow::FindBreakPoint( const Point& rMousePos )
1507 size_t nLineHeight = GetTextHeight();
1508 nLineHeight = nLineHeight > 0 ? nLineHeight : 1;
1509 size_t nYPos = rMousePos.Y() + nCurYOffset;
1511 for ( size_t i = 0, n = GetBreakPoints().size(); i < n ; ++i )
1513 BreakPoint& rBrk = GetBreakPoints().at( i );
1514 sal_uInt16 nLine = rBrk.nLine-1;
1515 size_t nY = nLine*nLineHeight;
1516 if ( ( nYPos > nY ) && ( nYPos < ( nY + nLineHeight ) ) )
1517 return &rBrk;
1519 return nullptr;
1522 void BreakPointWindow::MouseButtonDown( const MouseEvent& rMEvt )
1524 if ( rMEvt.GetClicks() == 2 )
1526 Point aMousePos( PixelToLogic( rMEvt.GetPosPixel() ) );
1527 tools::Long nLineHeight = GetTextHeight();
1528 if(nLineHeight)
1530 tools::Long nYPos = aMousePos.Y() + nCurYOffset;
1531 tools::Long nLine = nYPos / nLineHeight + 1;
1532 rModulWindow.ToggleBreakPoint( static_cast<sal_uInt16>(nLine) );
1533 Invalidate();
1538 void BreakPointWindow::Command( const CommandEvent& rCEvt )
1540 if ( rCEvt.GetCommand() != CommandEventId::ContextMenu )
1541 return;
1543 Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) );
1544 tools::Rectangle aRect(aPos, Size(1, 1));
1545 weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
1547 std::unique_ptr<weld::Builder> xUIBuilder(Application::CreateBuilder(pPopupParent, "modules/BasicIDE/ui/breakpointmenus.ui"));
1549 Point aEventPos( PixelToLogic( aPos ) );
1550 BreakPoint* pBrk = rCEvt.IsMouseEvent() ? FindBreakPoint( aEventPos ) : nullptr;
1551 if ( pBrk )
1553 // test if break point is enabled...
1554 std::unique_ptr<weld::Menu> xBrkPropMenu = xUIBuilder->weld_menu("breakmenu");
1555 xBrkPropMenu->set_active("active", pBrk->bEnabled);
1556 OUString sCommand = xBrkPropMenu->popup_at_rect(pPopupParent, aRect);
1557 if (sCommand == "active")
1559 pBrk->bEnabled = !pBrk->bEnabled;
1560 rModulWindow.UpdateBreakPoint( *pBrk );
1561 Invalidate();
1563 else if (sCommand == "properties")
1565 BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1566 aBrkDlg.SetCurrentBreakPoint( *pBrk );
1567 aBrkDlg.run();
1568 Invalidate();
1571 else
1573 std::unique_ptr<weld::Menu> xBrkListMenu = xUIBuilder->weld_menu("breaklistmenu");
1574 OUString sCommand = xBrkListMenu->popup_at_rect(pPopupParent, aRect);
1575 if (sCommand == "manage")
1577 BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1578 aBrkDlg.run();
1579 Invalidate();
1584 bool BreakPointWindow::SyncYOffset()
1586 TextView* pView = rModulWindow.GetEditView();
1587 if ( pView )
1589 tools::Long nViewYOffset = pView->GetStartDocPos().Y();
1590 if ( nCurYOffset != nViewYOffset )
1592 nCurYOffset = nViewYOffset;
1593 Invalidate();
1594 return true;
1597 return false;
1600 // virtual
1601 void BreakPointWindow::DataChanged(DataChangedEvent const & rDCEvt)
1603 Window::DataChanged(rDCEvt);
1604 if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
1605 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
1607 Color aColor(GetSettings().GetStyleSettings().GetFieldColor());
1608 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
1609 if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFieldColor())
1611 setBackgroundColor(aColor);
1612 Invalidate();
1617 void BreakPointWindow::setBackgroundColor(Color aColor)
1619 SetBackground(Wallpaper(aColor));
1622 namespace {
1624 struct WatchItem
1626 OUString maName;
1627 OUString maDisplayName;
1628 SbxObjectRef mpObject;
1629 std::vector<OUString> maMemberList;
1631 SbxDimArrayRef mpArray;
1632 int nDimLevel; // 0 = Root
1633 int nDimCount;
1634 std::vector<sal_Int32> vIndices;
1636 WatchItem* mpArrayParentItem;
1638 explicit WatchItem (OUString aName):
1639 maName(std::move(aName)),
1640 nDimLevel(0),
1641 nDimCount(0),
1642 mpArrayParentItem(nullptr)
1645 void clearWatchItem ()
1647 maMemberList.clear();
1650 WatchItem* GetRootItem();
1651 SbxDimArray* GetRootArray();
1656 WatchWindow::WatchWindow(Layout* pParent)
1657 : DockingWindow(pParent, "modules/BasicIDE/ui/dockingwatch.ui", "DockingWatch")
1658 , m_nUpdateWatchesId(nullptr)
1660 m_xTitleArea = m_xBuilder->weld_container("titlearea");
1662 nVirtToolBoxHeight = m_xTitleArea->get_preferred_size().Height();
1664 m_xTitle = m_xBuilder->weld_label("title");
1665 m_xTitle->set_label(IDEResId(RID_STR_REMOVEWATCH));
1667 m_xEdit = m_xBuilder->weld_entry("edit");
1668 m_xRemoveWatchButton = m_xBuilder->weld_button("remove");
1669 m_xTreeListBox = m_xBuilder->weld_tree_view("treeview");
1671 m_xEdit->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1672 m_xEdit->set_help_id(HID_BASICIDE_WATCHWINDOW_EDIT);
1673 m_xEdit->set_size_request(LogicToPixel(Size(80, 0), MapMode(MapUnit::MapAppFont)).Width(), -1);
1674 m_xEdit->connect_activate(LINK( this, WatchWindow, ActivateHdl));
1675 m_xEdit->connect_key_press(LINK( this, WatchWindow, KeyInputHdl));
1676 m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1678 m_xRemoveWatchButton->set_sensitive(false);
1679 m_xRemoveWatchButton->connect_clicked(LINK( this, WatchWindow, ButtonHdl));
1680 m_xRemoveWatchButton->set_help_id(HID_BASICIDE_REMOVEWATCH);
1681 m_xRemoveWatchButton->set_tooltip_text(IDEResId(RID_STR_REMOVEWATCHTIP));
1683 m_xTreeListBox->set_help_id(HID_BASICIDE_WATCHWINDOW_LIST);
1684 m_xTreeListBox->connect_editing(LINK(this, WatchWindow, EditingEntryHdl),
1685 LINK(this, WatchWindow, EditedEntryHdl));
1686 m_xTreeListBox->connect_changed( LINK( this, WatchWindow, TreeListHdl ) );
1687 m_xTreeListBox->connect_expanding(LINK(this, WatchWindow, RequestingChildrenHdl));
1689 // VarTabWidth, ValueTabWidth, TypeTabWidth
1690 std::vector<int> aWidths { 220, 100, 1250 };
1691 std::vector<bool> aEditables { false, true, false };
1692 m_xTreeListBox->set_column_fixed_widths(aWidths);
1693 m_xTreeListBox->set_column_editables(aEditables);
1695 SetText(IDEResId(RID_STR_WATCHNAME));
1697 SetHelpId( HID_BASICIDE_WATCHWINDOW );
1699 // make watch window keyboard accessible
1700 GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1703 WatchWindow::~WatchWindow()
1705 disposeOnce();
1708 void WatchWindow::dispose()
1710 if (m_nUpdateWatchesId)
1712 Application::RemoveUserEvent(m_nUpdateWatchesId);
1713 m_nUpdateWatchesId = nullptr;
1716 // Destroy user data
1717 m_xTreeListBox->all_foreach([this](weld::TreeIter& rEntry){
1718 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
1719 delete pItem;
1720 return false;
1723 m_xTitle.reset();
1724 m_xEdit.reset();
1725 m_xRemoveWatchButton.reset();
1726 m_xTitleArea.reset();
1727 m_xTreeListBox.reset();
1728 GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1729 DockingWindow::dispose();
1732 void WatchWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1734 lcl_DrawIDEWindowFrame(this, rRenderContext);
1737 void WatchWindow::Resize()
1739 Size aSz = GetOutputSizePixel();
1740 Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1742 if ( aBoxSz.Width() < 4 )
1743 aBoxSz.setWidth( 0 );
1744 if ( aBoxSz.Height() < 4 )
1745 aBoxSz.setHeight( 0 );
1747 m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1749 Invalidate();
1752 WatchItem* WatchItem::GetRootItem()
1754 WatchItem* pItem = mpArrayParentItem;
1755 while( pItem )
1757 if( pItem->mpArray.is() )
1758 break;
1759 pItem = pItem->mpArrayParentItem;
1761 return pItem;
1764 SbxDimArray* WatchItem::GetRootArray()
1766 WatchItem* pRootItem = GetRootItem();
1767 SbxDimArray* pRet = nullptr;
1768 if( pRootItem )
1769 pRet = pRootItem->mpArray.get();
1770 return pRet;
1773 void WatchWindow::AddWatch( const OUString& rVName )
1775 OUString aVar, aIndex;
1776 lcl_SeparateNameAndIndex( rVName, aVar, aIndex );
1777 WatchItem* pWatchItem = new WatchItem(aVar);
1779 OUString sId(weld::toId(pWatchItem));
1780 std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
1781 m_xTreeListBox->insert(nullptr, -1, &aVar, &sId, nullptr, nullptr, false, xRet.get());
1782 m_xTreeListBox->set_text(*xRet, "", 1);
1783 m_xTreeListBox->set_text(*xRet, "", 2);
1785 m_xTreeListBox->set_cursor(*xRet);
1786 m_xTreeListBox->select(*xRet);
1787 m_xTreeListBox->scroll_to_row(*xRet);
1788 m_xRemoveWatchButton->set_sensitive(true);
1790 UpdateWatches(false);
1793 void WatchWindow::RemoveSelectedWatch()
1795 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeListBox->make_iterator();
1796 bool bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1797 if (bEntry)
1799 m_xTreeListBox->remove(*xEntry);
1800 bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1801 if (bEntry)
1802 m_xEdit->set_text(weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xEntry))->maName);
1803 else
1804 m_xEdit->set_text(OUString());
1805 if ( !m_xTreeListBox->n_children() )
1806 m_xRemoveWatchButton->set_sensitive(false);
1810 IMPL_STATIC_LINK_NOARG(WatchWindow, ButtonHdl, weld::Button&, void)
1812 if (SfxDispatcher* pDispatcher = GetDispatcher())
1813 pDispatcher->Execute(SID_BASICIDE_REMOVEWATCH);
1816 IMPL_LINK_NOARG(WatchWindow, TreeListHdl, weld::TreeView&, void)
1818 std::unique_ptr<weld::TreeIter> xCurEntry = m_xTreeListBox->make_iterator();
1819 bool bCurEntry = m_xTreeListBox->get_cursor(xCurEntry.get());
1820 if (!bCurEntry)
1821 return;
1822 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xCurEntry));
1823 if (!pItem)
1824 return;
1825 m_xEdit->set_text(pItem->maName);
1828 IMPL_LINK_NOARG(WatchWindow, ActivateHdl, weld::Entry&, bool)
1830 OUString aCurText(m_xEdit->get_text());
1831 if (!aCurText.isEmpty())
1833 AddWatch(aCurText);
1834 m_xEdit->select_region(0, -1);
1836 return true;
1839 IMPL_LINK(WatchWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1841 bool bHandled = false;
1843 sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode();
1844 if (nKeyCode == KEY_ESCAPE)
1846 m_xEdit->set_text(OUString());
1847 bHandled = true;
1850 return bHandled;
1853 // StackWindow
1854 StackWindow::StackWindow(Layout* pParent)
1855 : DockingWindow(pParent, "modules/BasicIDE/ui/dockingstack.ui", "DockingStack")
1857 m_xTitle = m_xBuilder->weld_label("title");
1858 m_xTitle->set_label(IDEResId(RID_STR_STACK));
1860 m_xTitle->set_size_request(-1, nVirtToolBoxHeight); // so the two title areas are the same height
1862 m_xTreeListBox = m_xBuilder->weld_tree_view("stack");
1864 m_xTreeListBox->set_help_id(HID_BASICIDE_STACKWINDOW_LIST);
1865 m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_STACKNAME));
1866 m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1867 m_xTreeListBox->append_text(OUString());
1869 SetText(IDEResId(RID_STR_STACKNAME));
1871 SetHelpId( HID_BASICIDE_STACKWINDOW );
1873 // make stack window keyboard accessible
1874 GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1877 StackWindow::~StackWindow()
1879 disposeOnce();
1882 void StackWindow::dispose()
1884 GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1885 m_xTitle.reset();
1886 m_xTreeListBox.reset();
1887 DockingWindow::dispose();
1890 void StackWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1892 lcl_DrawIDEWindowFrame(this, rRenderContext);
1895 void StackWindow::Resize()
1897 Size aSz = GetOutputSizePixel();
1898 Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1900 if ( aBoxSz.Width() < 4 )
1901 aBoxSz.setWidth( 0 );
1902 if ( aBoxSz.Height() < 4 )
1903 aBoxSz.setHeight( 0 );
1905 m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1907 Invalidate();
1910 void StackWindow::UpdateCalls()
1912 m_xTreeListBox->freeze();
1913 m_xTreeListBox->clear();
1915 if (StarBASIC::IsRunning())
1917 ErrCode eOld = SbxBase::GetError();
1918 m_xTreeListBox->set_selection_mode(SelectionMode::Single);
1920 sal_Int32 nScope = 0;
1921 SbMethod* pMethod = StarBASIC::GetActiveMethod( nScope );
1922 while ( pMethod )
1924 OUStringBuffer aEntry( OUString::number(nScope ));
1925 if ( aEntry.getLength() < 2 )
1926 aEntry.insert(0, " ");
1927 aEntry.append(": " + pMethod->GetName());
1928 SbxArray* pParams = pMethod->GetParameters();
1929 SbxInfo* pInfo = pMethod->GetInfo();
1930 if ( pParams )
1932 aEntry.append("(");
1933 // 0 is the sub's name...
1934 for (sal_uInt32 nParam = 1; nParam < pParams->Count(); nParam++)
1936 SbxVariable* pVar = pParams->Get(nParam);
1937 assert(pVar && "Parameter?!");
1938 if ( !pVar->GetName().isEmpty() )
1940 aEntry.append(pVar->GetName());
1942 else if ( pInfo )
1944 assert(nParam <= std::numeric_limits<sal_uInt16>::max());
1945 const SbxParamInfo* pParam = pInfo->GetParam( sal::static_int_cast<sal_uInt16>(nParam) );
1946 if ( pParam )
1948 aEntry.append(pParam->aName);
1951 aEntry.append("=");
1952 SbxDataType eType = pVar->GetType();
1953 if( eType & SbxARRAY )
1955 aEntry.append("...");
1957 else if( eType != SbxOBJECT )
1959 aEntry.append(pVar->GetOUString());
1961 if (nParam < (pParams->Count() - 1))
1963 aEntry.append(", ");
1966 aEntry.append(")");
1968 m_xTreeListBox->append_text(aEntry.makeStringAndClear());
1969 nScope++;
1970 pMethod = StarBASIC::GetActiveMethod( nScope );
1973 SbxBase::ResetError();
1974 if( eOld != ERRCODE_NONE )
1975 SbxBase::SetError( eOld );
1977 else
1979 m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1980 m_xTreeListBox->append_text(OUString());
1983 m_xTreeListBox->thaw();
1986 ComplexEditorWindow::ComplexEditorWindow( ModulWindow* pParent ) :
1987 Window( pParent, WB_3DLOOK | WB_CLIPCHILDREN ),
1988 aBrkWindow(VclPtr<BreakPointWindow>::Create(this, pParent)),
1989 aLineNumberWindow(VclPtr<LineNumberWindow>::Create(this, pParent)),
1990 aEdtWindow(VclPtr<EditorWindow>::Create(this, pParent)),
1991 aEWVScrollBar(VclPtr<ScrollAdaptor>::Create(this, false)),
1992 aEWHScrollBar(VclPtr<ScrollAdaptor>::Create(this, true))
1994 aEdtWindow->Show();
1995 aBrkWindow->Show();
1997 aEWVScrollBar->SetLineSize(nScrollLine);
1998 aEWVScrollBar->SetPageSize(nScrollPage);
1999 aEWVScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2000 aEWVScrollBar->Show();
2002 aEWHScrollBar->SetLineSize(nScrollLine);
2003 aEWHScrollBar->SetPageSize(nScrollPage);
2004 aEWHScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2005 aEWHScrollBar->Show();
2008 ComplexEditorWindow::~ComplexEditorWindow()
2010 disposeOnce();
2013 void ComplexEditorWindow::dispose()
2015 aBrkWindow.disposeAndClear();
2016 aLineNumberWindow.disposeAndClear();
2017 aEdtWindow.disposeAndClear();
2018 aEWVScrollBar.disposeAndClear();
2019 aEWHScrollBar.disposeAndClear();
2020 vcl::Window::dispose();
2023 void ComplexEditorWindow::Resize()
2025 Size aOutSz = GetOutputSizePixel();
2026 Size aSz(aOutSz);
2027 aSz.AdjustWidth( -(2*DWBORDER) );
2028 aSz.AdjustHeight( -(2*DWBORDER) );
2029 tools::Long nBrkWidth = 20;
2030 tools::Long nSBWidth = aEWVScrollBar->GetSizePixel().Width();
2031 tools::Long nSBHeight = aEWHScrollBar->GetSizePixel().Height();
2033 Size aBrkSz(nBrkWidth, aSz.Height() - nSBHeight);
2035 if (aLineNumberWindow->IsVisible())
2037 Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height() - nSBHeight);
2038 Size aEWSz(aSz.Width() - nBrkWidth - aLineNumberWindow->GetWidth() - nSBWidth, aSz.Height() - nSBHeight);
2039 aBrkWindow->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBrkSz);
2040 aLineNumberWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aLnSz);
2041 aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth + aLnSz.Width(), DWBORDER), aEWSz);
2043 else
2045 Size aEWSz(aSz.Width() - nBrkWidth - nSBWidth, aSz.Height() - nSBHeight);
2046 aBrkWindow->SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz );
2047 aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aEWSz);
2050 aEWVScrollBar->SetPosSizePixel(Point(aOutSz.Width() - DWBORDER - nSBWidth, DWBORDER),
2051 Size(nSBWidth, aSz.Height() - nSBHeight));
2052 aEWHScrollBar->SetPosSizePixel(Point(DWBORDER, aOutSz.Height() - DWBORDER - nSBHeight),
2053 Size(aSz.Width() - nSBWidth, nSBHeight));
2056 IMPL_LINK_NOARG(ComplexEditorWindow, ScrollHdl, weld::Scrollbar&, void)
2058 if (aEdtWindow->GetEditView())
2060 tools::Long nXDiff = aEdtWindow->GetEditView()->GetStartDocPos().X() - aEWHScrollBar->GetThumbPos();
2061 tools::Long nYDiff = aEdtWindow->GetEditView()->GetStartDocPos().Y() - aEWVScrollBar->GetThumbPos();
2062 aEdtWindow->GetEditView()->Scroll(nXDiff, nYDiff);
2063 aBrkWindow->DoScroll( nYDiff );
2064 aLineNumberWindow->DoScroll( nYDiff );
2065 aEdtWindow->GetEditView()->ShowCursor(false);
2066 aEWVScrollBar->SetThumbPos( aEdtWindow->GetEditView()->GetStartDocPos().Y() );
2070 void ComplexEditorWindow::DataChanged(DataChangedEvent const & rDCEvt)
2072 Window::DataChanged(rDCEvt);
2073 if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
2074 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
2076 Color aColor(GetSettings().GetStyleSettings().GetFaceColor());
2077 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
2078 if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFaceColor())
2080 SetBackground(Wallpaper(aColor));
2081 Invalidate();
2086 void ComplexEditorWindow::SetLineNumberDisplay(bool b)
2088 aLineNumberWindow->Show(b);
2089 Resize();
2092 uno::Reference< awt::XVclWindowPeer >
2093 EditorWindow::GetComponentInterface(bool bCreate)
2095 uno::Reference< awt::XVclWindowPeer > xPeer(
2096 Window::GetComponentInterface(false));
2097 if (!xPeer.is() && bCreate)
2099 // Make sure edit engine and view are available:
2100 if (!pEditEngine)
2101 CreateEditEngine();
2103 xPeer = createTextWindowPeer(*GetEditView());
2104 SetComponentInterface(xPeer);
2106 return xPeer;
2109 static sal_uInt32 getCorrectedPropCount(SbxArray* p)
2111 sal_uInt32 nPropCount = p->Count();
2112 if (nPropCount >= 3 && p->Get(nPropCount - 1)->GetName().equalsIgnoreAsciiCase("Dbg_Methods")
2113 && p->Get(nPropCount - 2)->GetName().equalsIgnoreAsciiCase("Dbg_Properties")
2114 && p->Get(nPropCount - 3)->GetName().equalsIgnoreAsciiCase("Dbg_SupportedInterfaces"))
2116 nPropCount -= 3;
2118 return nPropCount;
2121 IMPL_LINK(WatchWindow, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool)
2123 if( !StarBASIC::IsRunning() )
2124 return true;
2126 if (m_xTreeListBox->iter_has_child(rParent))
2127 return true;
2129 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rParent));
2130 std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
2132 SbxDimArray* pArray = pItem->mpArray.get();
2133 SbxDimArray* pRootArray = pItem->GetRootArray();
2134 bool bArrayIsRootArray = false;
2135 if( !pArray && pRootArray )
2137 pArray = pRootArray;
2138 bArrayIsRootArray = true;
2141 SbxObject* pObj = pItem->mpObject.get();
2142 if( pObj )
2144 createAllObjectProperties( pObj );
2145 SbxArray* pProps = pObj->GetProperties();
2146 const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2147 pItem->maMemberList.reserve(nPropCount);
2149 for( sal_uInt32 i = 0 ; i < nPropCount ; ++i )
2151 SbxVariable* pVar = pProps->Get(i);
2153 pItem->maMemberList.push_back(pVar->GetName());
2154 OUString const& rName = pItem->maMemberList.back();
2156 WatchItem* pWatchItem = new WatchItem(rName);
2157 OUString sId(weld::toId(pWatchItem));
2159 m_xTreeListBox->insert(&rParent, -1, &rName, &sId, nullptr, nullptr, false, xRet.get());
2160 m_xTreeListBox->set_text(*xRet, "", 1);
2161 m_xTreeListBox->set_text(*xRet, "", 2);
2164 if (nPropCount > 0 && !m_nUpdateWatchesId)
2166 m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2169 else if( pArray )
2171 sal_uInt16 nElementCount = 0;
2173 // Loop through indices of current level
2174 int nParentLevel = bArrayIsRootArray ? pItem->nDimLevel : 0;
2175 int nThisLevel = nParentLevel + 1;
2176 sal_Int32 nMin, nMax;
2177 if (pArray->GetDim(nThisLevel, nMin, nMax))
2179 for (sal_Int32 i = nMin; i <= nMax; i++)
2181 WatchItem* pChildItem = new WatchItem(pItem->maName);
2183 // Copy data and create name
2185 OUStringBuffer aIndexStr = "(";
2186 pChildItem->mpArrayParentItem = pItem;
2187 pChildItem->nDimLevel = nThisLevel;
2188 pChildItem->nDimCount = pItem->nDimCount;
2189 pChildItem->vIndices.resize(pChildItem->nDimCount);
2190 sal_Int32 j;
2191 for (j = 0; j < nParentLevel; j++)
2193 sal_Int32 n = pChildItem->vIndices[j] = pItem->vIndices[j];
2194 aIndexStr.append( OUString::number(n) + "," );
2196 pChildItem->vIndices[nParentLevel] = i;
2197 aIndexStr.append( OUString::number(i) + ")" );
2199 OUString aDisplayName;
2200 WatchItem* pArrayRootItem = pChildItem->GetRootItem();
2201 if (pArrayRootItem && pArrayRootItem->mpArrayParentItem)
2202 aDisplayName = pItem->maDisplayName;
2203 else
2204 aDisplayName = pItem->maName;
2205 aDisplayName += aIndexStr;
2206 pChildItem->maDisplayName = aDisplayName;
2208 OUString sId(weld::toId(pChildItem));
2210 m_xTreeListBox->insert(&rParent, -1, &aDisplayName, &sId, nullptr, nullptr, false,
2211 xRet.get());
2212 m_xTreeListBox->set_text(*xRet, "", 1);
2213 m_xTreeListBox->set_text(*xRet, "", 2);
2215 nElementCount++;
2218 if (nElementCount > 0 && !m_nUpdateWatchesId)
2220 m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2224 return true;
2227 IMPL_LINK_NOARG(WatchWindow, ExecuteUpdateWatches, void*, void)
2229 m_nUpdateWatchesId = nullptr;
2230 UpdateWatches();
2233 SbxBase* WatchWindow::ImplGetSBXForEntry(const weld::TreeIter& rEntry, bool& rbArrayElement)
2235 SbxBase* pSBX = nullptr;
2236 rbArrayElement = false;
2238 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2239 OUString aVName( pItem->maName );
2241 std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeListBox->make_iterator(&rEntry);
2242 bool bParentEntry = m_xTreeListBox->iter_parent(*xParentEntry);
2243 WatchItem* pParentItem = bParentEntry ? weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xParentEntry)) : nullptr;
2244 if( pParentItem )
2246 SbxObject* pObj = pParentItem->mpObject.get();
2247 SbxDimArray* pArray;
2248 if( pObj )
2250 pSBX = pObj->Find( aVName, SbxClassType::DontCare );
2251 if (SbxVariable const* pVar = IsSbxVariable(pSBX))
2253 // Force getting value
2254 SbxValues aRes;
2255 aRes.eType = SbxVOID;
2256 pVar->Get( aRes );
2259 // Array?
2260 else if( (pArray = pItem->GetRootArray()) != nullptr )
2262 rbArrayElement = true;
2263 if( pParentItem->nDimLevel + 1 == pParentItem->nDimCount )
2264 pSBX = pArray->Get(pItem->vIndices.empty() ? nullptr : &*pItem->vIndices.begin());
2267 else
2269 pSBX = StarBASIC::FindSBXInCurrentScope( aVName );
2271 return pSBX;
2274 IMPL_LINK(WatchWindow, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
2276 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rIter));
2278 bool bEdit = false;
2279 if (StarBASIC::IsRunning() && StarBASIC::GetActiveMethod() && !SbxBase::IsError())
2281 // No out of scope entries
2282 bool bArrayElement;
2283 SbxBase* pSbx = ImplGetSBXForEntry(rIter, bArrayElement);
2284 if (IsSbxVariable(pSbx) || bArrayElement)
2286 // Accept no objects and only end nodes of arrays for editing
2287 if( !pItem->mpObject.is() && ( !pItem->mpArray.is() || pItem->nDimLevel == pItem->nDimCount ) )
2289 aEditingRes = m_xTreeListBox->get_text(rIter, 1);
2290 aEditingRes = comphelper::string::strip(aEditingRes, ' ');
2291 bEdit = true;
2296 return bEdit;
2299 IMPL_LINK(WatchWindow, EditedEntryHdl, const IterString&, rIterString, bool)
2301 const weld::TreeIter& rIter = rIterString.first;
2302 OUString aResult = comphelper::string::strip(rIterString.second, ' ');
2304 sal_uInt16 nResultLen = aResult.getLength();
2305 sal_Unicode cFirst = aResult[0];
2306 sal_Unicode cLast = aResult[ nResultLen - 1 ];
2307 if( cFirst == '\"' && cLast == '\"' )
2308 aResult = aResult.copy( 1, nResultLen - 2 );
2310 if (aResult == aEditingRes)
2311 return false;
2313 bool bArrayElement;
2314 SbxBase* pSBX = ImplGetSBXForEntry(rIter, bArrayElement);
2316 if (SbxVariable* pVar = IsSbxVariable(pSBX))
2318 SbxDataType eType = pVar->GetType();
2319 if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxOBJECT)
2320 && ( eType & SbxARRAY ) == 0 )
2322 // If the type is variable, the conversion of the SBX does not matter,
2323 // else the string is converted.
2324 pVar->PutStringExt( aResult );
2328 if ( SbxBase::IsError() )
2330 SbxBase::ResetError();
2333 UpdateWatches();
2335 // The text should never be taken/copied 1:1,
2336 // as the UpdateWatches will be lost
2337 return false;
2340 namespace
2343 void implCollapseModifiedObjectEntry(const weld::TreeIter& rParent, weld::TreeView& rTree)
2345 rTree.collapse_row(rParent);
2347 std::unique_ptr<weld::TreeIter> xDeleteEntry = rTree.make_iterator(&rParent);
2349 while (rTree.iter_children(*xDeleteEntry))
2351 implCollapseModifiedObjectEntry(*xDeleteEntry, rTree);
2353 WatchItem* pItem = weld::fromId<WatchItem*>(rTree.get_id(*xDeleteEntry));
2354 delete pItem;
2355 rTree.remove(*xDeleteEntry);
2356 rTree.copy_iterator(rParent, *xDeleteEntry);
2360 OUString implCreateTypeStringForDimArray( WatchItem* pItem, SbxDataType eType )
2362 OUString aRetStr = getBasicTypeName( eType );
2364 SbxDimArray* pArray = pItem->mpArray.get();
2365 if( !pArray )
2366 pArray = pItem->GetRootArray();
2367 if( pArray )
2369 int nDimLevel = pItem->nDimLevel;
2370 int nDims = pItem->nDimCount;
2371 if( nDimLevel < nDims )
2373 aRetStr += "(";
2374 for( int i = nDimLevel ; i < nDims ; i++ )
2376 sal_Int32 nMin, nMax;
2377 pArray->GetDim(sal::static_int_cast<sal_Int32>(i + 1), nMin, nMax);
2378 aRetStr += OUString::number(nMin) + " to " + OUString::number(nMax);
2379 if( i < nDims - 1 )
2380 aRetStr += ", ";
2382 aRetStr += ")";
2385 return aRetStr;
2388 } // namespace
2390 void WatchWindow::implEnableChildren(const weld::TreeIter& rEntry, bool bEnable)
2392 if (bEnable)
2394 if (!m_xTreeListBox->get_row_expanded(rEntry))
2395 m_xTreeListBox->set_children_on_demand(rEntry, true);
2397 else
2399 assert(!m_xTreeListBox->get_row_expanded(rEntry));
2400 m_xTreeListBox->set_children_on_demand(rEntry, false);
2404 void WatchWindow::UpdateWatches(bool bBasicStopped)
2406 SbMethod* pCurMethod = StarBASIC::GetActiveMethod();
2408 ErrCode eOld = SbxBase::GetError();
2409 setBasicWatchMode( true );
2411 m_xTreeListBox->all_foreach([this, pCurMethod, bBasicStopped](weld::TreeIter& rEntry){
2412 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2413 DBG_ASSERT( !pItem->maName.isEmpty(), "Var? - Must not be empty!" );
2414 OUString aWatchStr;
2415 OUString aTypeStr;
2416 if ( pCurMethod )
2418 bool bCollapse = false;
2419 TriState eEnableChildren = TRISTATE_INDET;
2421 bool bArrayElement;
2422 SbxBase* pSBX = ImplGetSBXForEntry(rEntry, bArrayElement);
2424 // Array? If no end node create type string
2425 if( bArrayElement && pItem->nDimLevel < pItem->nDimCount )
2427 SbxDimArray* pRootArray = pItem->GetRootArray();
2428 SbxDataType eType = pRootArray->GetType();
2429 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2430 eEnableChildren = TRISTATE_TRUE;
2433 if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pSBX))
2435 // extra treatment of arrays
2436 SbxDataType eType = pVar->GetType();
2437 if ( eType & SbxARRAY )
2439 // consider multidimensional arrays!
2440 if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(pVar->GetObject()))
2442 SbxDimArray* pOldArray = pItem->mpArray.get();
2444 bool bArrayChanged = false;
2445 if (pOldArray != nullptr)
2447 // Compare Array dimensions to see if array has changed
2448 // Can be a copy, so comparing pointers does not work
2449 sal_Int32 nOldDims = pOldArray->GetDims();
2450 sal_Int32 nNewDims = pNewArray->GetDims();
2451 if( nOldDims != nNewDims )
2453 bArrayChanged = true;
2455 else
2457 for( sal_Int32 i = 0 ; i < nOldDims ; i++ )
2459 sal_Int32 nOldMin, nOldMax;
2460 sal_Int32 nNewMin, nNewMax;
2462 pOldArray->GetDim(i + 1, nOldMin, nOldMax);
2463 pNewArray->GetDim(i + 1, nNewMin, nNewMax);
2464 if( nOldMin != nNewMin || nOldMax != nNewMax )
2466 bArrayChanged = true;
2467 break;
2472 else
2474 bArrayChanged = true;
2476 eEnableChildren = TRISTATE_TRUE;
2477 // #i37227 Clear always and replace array
2478 if( pNewArray != pOldArray )
2480 pItem->clearWatchItem();
2481 eEnableChildren = TRISTATE_TRUE;
2483 pItem->mpArray = pNewArray;
2484 sal_Int32 nDims = pNewArray->GetDims();
2485 pItem->nDimLevel = 0;
2486 pItem->nDimCount = nDims;
2488 if( bArrayChanged && pOldArray != nullptr )
2490 bCollapse = true;
2492 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2494 else
2496 aWatchStr += "<?>";
2499 else if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
2501 if (SbxObject* pObj = dynamic_cast<SbxObject*>(pVar->GetObject()))
2503 if ( pItem->mpObject.is() && !pItem->maMemberList.empty() )
2505 createAllObjectProperties(pObj);
2506 SbxArray* pProps = pObj->GetProperties();
2507 const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2508 // Check if member list has changed
2509 bCollapse = pItem->maMemberList.size() != nPropCount;
2510 for( sal_uInt32 i = 0 ; !bCollapse && i < nPropCount ; i++ )
2512 SbxVariable* pVar_ = pProps->Get(i);
2513 if( pItem->maMemberList[i] != pVar_->GetName() )
2514 bCollapse = true;
2518 pItem->mpObject = pObj;
2519 eEnableChildren = TRISTATE_TRUE;
2520 aTypeStr = getBasicObjectTypeName( pObj );
2522 else
2524 aWatchStr = "Null";
2525 if( pItem->mpObject.is() )
2527 bCollapse = true;
2528 eEnableChildren = TRISTATE_FALSE;
2532 else
2534 if( pItem->mpObject.is() )
2536 bCollapse = true;
2537 eEnableChildren = TRISTATE_FALSE;
2540 bool bString = (static_cast<sal_uInt8>(eType) == sal_uInt8(SbxSTRING));
2541 OUString aStrStr( "\"" );
2542 if( bString )
2544 aWatchStr += aStrStr;
2546 // tdf#57308 - avoid a second call to retrieve the data
2547 const SbxFlagBits nFlags = pVar->GetFlags();
2548 pVar->SetFlag(SbxFlagBits::NoBroadcast);
2549 aWatchStr += pVar->GetOUString();
2550 pVar->SetFlags(nFlags);
2551 if( bString )
2553 aWatchStr += aStrStr;
2556 if( aTypeStr.isEmpty() )
2558 if( !pVar->IsFixed() )
2560 aTypeStr = "Variant/";
2562 aTypeStr += getBasicTypeName( pVar->GetType() );
2565 else if( !bArrayElement )
2567 aWatchStr += "<Out of Scope>";
2570 if( bCollapse )
2572 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2573 pItem->clearWatchItem();
2576 if (eEnableChildren != TRISTATE_INDET)
2577 implEnableChildren(rEntry, eEnableChildren == TRISTATE_TRUE);
2579 else if( bBasicStopped )
2581 if( pItem->mpObject.is() || pItem->mpArray.is() )
2583 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2584 pItem->mpObject.clear();
2585 pItem->mpArray.clear();
2587 pItem->clearWatchItem();
2590 m_xTreeListBox->set_text(rEntry, aWatchStr, 1);
2591 m_xTreeListBox->set_text(rEntry, aTypeStr, 2);
2593 return false;
2596 SbxBase::ResetError();
2597 if( eOld != ERRCODE_NONE )
2598 SbxBase::SetError( eOld );
2599 setBasicWatchMode( false );
2602 IMPL_LINK_NOARG(CodeCompleteWindow, ImplDoubleClickHdl, weld::TreeView&, bool)
2604 InsertSelectedEntry();
2605 return true;
2608 IMPL_LINK_NOARG(CodeCompleteWindow, ImplSelectHdl, weld::TreeView&, void)
2610 //give back the focus to the parent
2611 pParent->GrabFocus();
2614 TextView* CodeCompleteWindow::GetParentEditView()
2616 return pParent->GetEditView();
2619 void CodeCompleteWindow::InsertSelectedEntry()
2621 OUString sSelectedEntry = m_xListBox->get_selected_text();
2623 if( !aFuncBuffer.isEmpty() )
2625 // if the user typed in something: remove, and insert
2626 GetParentEditView()->SetSelection(pParent->GetLastHighlightPortionTextSelection());
2627 GetParentEditView()->DeleteSelected();
2629 if (!sSelectedEntry.isEmpty())
2631 // if the user selected something
2632 GetParentEditView()->InsertText(sSelectedEntry);
2635 else
2637 if (!sSelectedEntry.isEmpty())
2639 // if the user selected something
2640 GetParentEditView()->InsertText(sSelectedEntry);
2643 HideAndRestoreFocus();
2646 void CodeCompleteWindow::SetMatchingEntries()
2648 for (sal_Int32 i = 0, nEntryCount = m_xListBox->n_children(); i< nEntryCount; ++i)
2650 OUString sEntry = m_xListBox->get_text(i);
2651 if (sEntry.startsWithIgnoreAsciiCase(aFuncBuffer))
2653 m_xListBox->select(i);
2654 break;
2659 IMPL_LINK(CodeCompleteWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
2661 return HandleKeyInput(rKEvt);
2664 bool CodeCompleteWindow::HandleKeyInput( const KeyEvent& rKeyEvt )
2666 bool bHandled = true;
2668 sal_Unicode aChar = rKeyEvt.GetKeyCode().GetCode();
2669 if( (( aChar >= KEY_A ) && ( aChar <= KEY_Z ))
2670 || ((aChar >= KEY_0) && (aChar <= KEY_9)) )
2672 aFuncBuffer.append(rKeyEvt.GetCharCode());
2673 SetMatchingEntries();
2675 else
2677 switch( aChar )
2679 case KEY_POINT:
2680 break;
2681 case KEY_ESCAPE: // hide, do nothing
2682 HideAndRestoreFocus();
2683 break;
2684 case KEY_RIGHT:
2686 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2687 if( aTextSelection.GetEnd().GetPara() != GetTextSelection().GetEnd().GetPara()-1 )
2689 HideAndRestoreFocus();
2691 break;
2693 case KEY_LEFT:
2695 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2696 if( aTextSelection.GetStart().GetIndex()-1 < GetTextSelection().GetStart().GetIndex() )
2697 {//leave the cursor where it is
2698 HideAndRestoreFocus();
2700 break;
2702 case KEY_TAB:
2704 TextSelection aTextSelection = pParent->GetLastHighlightPortionTextSelection();
2705 OUString sTypedText = pParent->GetEditEngine()->GetText(aTextSelection);
2706 if( !aFuncBuffer.isEmpty() )
2708 sal_Int32 nInd = m_xListBox->get_selected_index();
2709 if (nInd != -1)
2711 int nEntryCount = m_xListBox->n_children();
2712 //if there is something selected
2713 bool bFound = false;
2714 for (sal_Int32 i = nInd; i != nEntryCount; ++i)
2716 OUString sEntry = m_xListBox->get_text(i);
2717 if( sEntry.startsWithIgnoreAsciiCase( aFuncBuffer )
2718 && (std::u16string_view(aFuncBuffer) != sTypedText) && (i != nInd) )
2720 m_xListBox->select(i);
2721 bFound = true;
2722 break;
2725 if( !bFound )
2726 SetMatchingEntries();
2728 GetParentEditView()->SetSelection( aTextSelection );
2729 GetParentEditView()->DeleteSelected();
2730 GetParentEditView()->InsertText(m_xListBox->get_selected_text());
2733 break;
2735 case KEY_SPACE:
2736 HideAndRestoreFocus();
2737 break;
2738 case KEY_BACKSPACE: case KEY_DELETE:
2739 if( !aFuncBuffer.isEmpty() )
2741 //if there was something inserted by tab: add it to aFuncBuffer
2742 TextSelection aSel( GetParentEditView()->GetSelection() );
2743 TextPaM aEnd( GetParentEditView()->CursorEndOfLine(GetTextSelection().GetEnd()) );
2744 GetParentEditView()->SetSelection(TextSelection(GetTextSelection().GetStart(), aEnd ) );
2745 OUString aTabInsertedStr( GetParentEditView()->GetSelected() );
2746 GetParentEditView()->SetSelection( aSel );
2748 if( !aTabInsertedStr.isEmpty() && aTabInsertedStr != std::u16string_view(aFuncBuffer) )
2750 aFuncBuffer = aTabInsertedStr;
2752 aFuncBuffer.remove(aFuncBuffer.getLength()-1, 1);
2753 SetMatchingEntries();
2755 else
2757 ClearAndHide();
2758 bHandled = false;
2760 break;
2761 case KEY_RETURN:
2762 InsertSelectedEntry();
2763 break;
2764 case KEY_UP:
2766 int nInd = m_xListBox->get_selected_index();
2767 if (nInd)
2768 m_xListBox->select(nInd - 1);
2769 break;
2771 case KEY_DOWN:
2773 int nInd = m_xListBox->get_selected_index();
2774 if (nInd + 1 < m_xListBox->n_children())
2775 m_xListBox->select(nInd + 1);
2776 break;
2778 default:
2779 bHandled = false;
2780 break;
2784 return bHandled;
2787 void CodeCompleteWindow::HideAndRestoreFocus()
2789 Hide();
2790 pParent->GrabFocus();
2793 CodeCompleteWindow::CodeCompleteWindow(EditorWindow* pPar)
2794 : InterimItemWindow(pPar, "modules/BasicIDE/ui/codecomplete.ui", "CodeComplete")
2795 , pParent(pPar)
2796 , m_xListBox(m_xBuilder->weld_tree_view("treeview"))
2798 m_xListBox->connect_row_activated(LINK(this, CodeCompleteWindow, ImplDoubleClickHdl));
2799 m_xListBox->connect_changed(LINK(this, CodeCompleteWindow, ImplSelectHdl));
2800 m_xListBox->connect_key_press(LINK(this, CodeCompleteWindow, KeyInputHdl));
2801 m_xListBox->make_sorted();
2803 m_xListBox->set_size_request(150, 150); // default, this will adopt the line length
2804 SetSizePixel(m_xContainer->get_preferred_size());
2807 CodeCompleteWindow::~CodeCompleteWindow()
2809 disposeOnce();
2812 void CodeCompleteWindow::dispose()
2814 m_xListBox.reset();
2815 pParent.clear();
2816 InterimItemWindow::dispose();
2819 void CodeCompleteWindow::InsertEntry( const OUString& aStr )
2821 m_xListBox->append_text(aStr);
2824 void CodeCompleteWindow::ClearListBox()
2826 m_xListBox->clear();
2827 aFuncBuffer.setLength(0);
2830 void CodeCompleteWindow::SetTextSelection( const TextSelection& aSel )
2832 m_aTextSelection = aSel;
2835 void CodeCompleteWindow::ResizeAndPositionListBox()
2837 if (m_xListBox->n_children() < 1)
2838 return;
2840 // if there is at least one element inside
2841 // calculate basic position: under the current line
2842 tools::Rectangle aRect = static_cast<TextEngine*>(pParent->GetEditEngine())->PaMtoEditCursor( pParent->GetEditView()->GetSelection().GetEnd() );
2843 tools::Long nViewYOffset = pParent->GetEditView()->GetStartDocPos().Y();
2844 Point aPos = aRect.BottomRight();// this variable will be used later (if needed)
2845 aPos.setY( (aPos.Y() - nViewYOffset) + nBasePad );
2847 // get line count
2848 const sal_uInt16 nLines = static_cast<sal_uInt16>(std::min(6, m_xListBox->n_children()));
2850 m_xListBox->set_size_request(-1, m_xListBox->get_height_rows(nLines));
2852 Size aSize = m_xContainer->get_preferred_size();
2853 //set the size
2854 SetSizePixel( aSize );
2856 //calculate position
2857 const tools::Rectangle aVisArea( pParent->GetEditView()->GetStartDocPos(), pParent->GetOutputSizePixel() ); //the visible area
2858 const Point& aBottomPoint = aVisArea.BottomRight();
2860 if( aVisArea.TopRight().getY() + aPos.getY() + aSize.getHeight() > aBottomPoint.getY() )
2861 {//clipped at the bottom: move it up
2862 const tools::Long& nParentFontHeight = pParent->GetEditEngine()->GetFont().GetFontHeight(); //parent's font (in the IDE): needed for height
2863 aPos.AdjustY( -(aSize.getHeight() + nParentFontHeight + nCursorPad) );
2866 if( aVisArea.TopLeft().getX() + aPos.getX() + aSize.getWidth() > aBottomPoint.getX() )
2867 {//clipped at the right side, move it a bit left
2868 aPos.AdjustX( -(aSize.getWidth() + aVisArea.TopLeft().getX()) );
2870 //set the position
2871 SetPosPixel( aPos );
2874 void CodeCompleteWindow::SelectFirstEntry()
2876 if (m_xListBox->n_children() > 0)
2877 m_xListBox->select(0);
2880 void CodeCompleteWindow::ClearAndHide()
2882 ClearListBox();
2883 HideAndRestoreFocus();
2886 UnoTypeCodeCompletetor::UnoTypeCodeCompletetor( const std::vector< OUString >& aVect, const OUString& sVarType )
2887 : bCanComplete( true )
2889 if( aVect.empty() || sVarType.isEmpty() )
2891 bCanComplete = false;//invalid parameters, nothing to code complete
2892 return;
2897 // Get the base class for reflection:
2898 xClass = css::reflection::theCoreReflection::get(
2899 comphelper::getProcessComponentContext())->forName(sVarType);
2901 catch( const Exception& )
2903 bCanComplete = false;
2904 return;
2907 //start from aVect[1]: aVect[0] is the variable name
2908 bCanComplete = std::none_of(aVect.begin() + 1, aVect.end(), [this](const OUString& rMethName) {
2909 return (!CodeCompleteOptions::IsExtendedTypeDeclaration() || !CheckMethod(rMethName)) && !CheckField(rMethName); });
2912 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassMethods() const
2914 std::vector< OUString > aRetVect;
2915 if( bCanComplete && ( xClass != nullptr ) )
2917 const Sequence< Reference< reflection::XIdlMethod > > aMethods = xClass->getMethods();
2918 for(Reference< reflection::XIdlMethod > const & rMethod : aMethods)
2920 aRetVect.push_back( rMethod->getName() );
2923 return aRetVect;//this is empty when cannot code complete
2926 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassFields() const
2928 std::vector< OUString > aRetVect;
2929 if( bCanComplete && ( xClass != nullptr ) )
2931 const Sequence< Reference< reflection::XIdlField > > aFields = xClass->getFields();
2932 for(Reference< reflection::XIdlField > const & rxField : aFields)
2934 aRetVect.push_back( rxField->getName() );
2937 return aRetVect;//this is empty when cannot code complete
2941 bool UnoTypeCodeCompletetor::CheckField( const OUString& sFieldName )
2942 {// modifies xClass!!!
2944 if ( xClass == nullptr )
2945 return false;
2947 Reference< reflection::XIdlField> xField = xClass->getField( sFieldName );
2948 if( xField != nullptr )
2950 xClass = xField->getType();
2951 if( xClass != nullptr )
2953 return true;
2956 return false;
2959 bool UnoTypeCodeCompletetor::CheckMethod( const OUString& sMethName )
2960 {// modifies xClass!!!
2963 if ( xClass == nullptr )
2964 return false;
2966 Reference< reflection::XIdlMethod> xMethod = xClass->getMethod( sMethName );
2967 if( xMethod != nullptr ) //method OK, check return type
2969 xClass = xMethod->getReturnType();
2970 if( xClass != nullptr )
2972 return true;
2975 return false;
2978 } // namespace basctl
2980 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */