tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / basctl / source / basicide / baside2b.cxx
blob93e36ab3406148642a687a2761c7a58d20728178
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cassert>
23 #include <string_view>
25 #include <helpids.h>
26 #include <iderid.hxx>
27 #include <strings.hrc>
28 #include <bitmaps.hlst>
30 #include "baside2.hxx"
31 #include "brkdlg.hxx"
32 #include <basidesh.hxx>
33 #include <basobj.hxx>
34 #include <iderdll.hxx>
36 #include <basic/sbmeth.hxx>
37 #include <basic/sbuno.hxx>
38 #include <com/sun/star/beans/XMultiPropertySet.hpp>
39 #include <com/sun/star/beans/XPropertiesChangeListener.hpp>
40 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
41 #include <com/sun/star/script/XLibraryContainer2.hpp>
42 #include <comphelper/string.hxx>
43 #include <comphelper/diagnose_ex.hxx>
44 #include <o3tl/string_view.hxx>
45 #include <officecfg/Office/Common.hxx>
46 #include <sfx2/dispatch.hxx>
47 #include <sfx2/progress.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <tools/debug.hxx>
50 #include <utility>
51 #include <vcl/image.hxx>
52 #include <vcl/weld.hxx>
53 #include <vcl/weldutils.hxx>
54 #include <svl/urihelper.hxx>
55 #include <svx/svxids.hrc>
56 #include <vcl/commandevent.hxx>
57 #include <vcl/xtextedt.hxx>
58 #include <vcl/textview.hxx>
59 #include <vcl/txtattr.hxx>
60 #include <vcl/settings.hxx>
61 #include <vcl/ptrstyle.hxx>
62 #include <vcl/event.hxx>
63 #include <vcl/svapp.hxx>
64 #include <vcl/taskpanelist.hxx>
65 #include <vcl/help.hxx>
66 #include <cppuhelper/implbase.hxx>
67 #include <vector>
68 #include <com/sun/star/reflection/theCoreReflection.hpp>
69 #include <unotools/charclass.hxx>
70 #include "textwindowpeer.hxx"
71 #include "uiobject.hxx"
72 #include <basegfx/utils/zoomtools.hxx>
73 #include <svl/itemset.hxx>
74 #include <BasicColorConfig.hxx>
76 namespace basctl
79 using namespace ::com::sun::star;
80 using namespace ::com::sun::star::uno;
82 namespace
85 sal_uInt16 const NoMarker = 0xFFFF;
86 tools::Long const nBasePad = 2;
87 tools::Long const nCursorPad = 5;
89 tools::Long nVirtToolBoxHeight; // inited in WatchWindow, used in Stackwindow
91 // Returns pBase converted to SbxVariable if valid and is not an SbxMethod.
92 SbxVariable* IsSbxVariable (SbxBase* pBase)
94 if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pBase))
95 if (!dynamic_cast<SbxMethod*>(pVar))
96 return pVar;
97 return nullptr;
100 Image GetImage(const OUString& rId)
102 return Image(StockImage::Yes, rId);
105 int const nScrollLine = 12;
106 int const nScrollPage = 60;
107 int const DWBORDER = 3;
109 std::u16string_view const cSuffixes = u"%&!#@$";
111 } // namespace
115 * Helper functions to get/set text in TextEngine using
116 * the stream interface.
118 * get/setText() only supports tools Strings limited to 64K).
120 OUString getTextEngineText (ExtTextEngine& rEngine)
122 SvMemoryStream aMemStream;
123 aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
124 aMemStream.SetLineDelimiter( LINEEND_LF );
125 rEngine.Write( aMemStream );
126 std::size_t nSize = aMemStream.Tell();
127 OUString aText( static_cast<const char*>(aMemStream.GetData()),
128 nSize, RTL_TEXTENCODING_UTF8 );
129 return aText;
132 void setTextEngineText (ExtTextEngine& rEngine, std::u16string_view aStr)
134 rEngine.SetText(OUString());
135 OString aUTF8Str = OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 );
136 SvMemoryStream aMemStream( const_cast<char *>(aUTF8Str.getStr()), aUTF8Str.getLength(),
137 StreamMode::READ );
138 aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
139 aMemStream.SetLineDelimiter( LINEEND_LF );
140 rEngine.Read(aMemStream);
143 namespace
146 void lcl_DrawIDEWindowFrame(DockingWindow const * pWin, vcl::RenderContext& rRenderContext)
148 if (pWin->IsFloatingMode())
149 return;
151 Size aSz(pWin->GetOutputSizePixel());
152 const Color aOldLineColor(rRenderContext.GetLineColor());
153 rRenderContext.SetLineColor(COL_WHITE);
154 // White line on top
155 rRenderContext.DrawLine(Point(0, 0), Point(aSz.Width(), 0));
156 // Black line at bottom
157 rRenderContext.SetLineColor(COL_BLACK);
158 rRenderContext.DrawLine(Point(0, aSz.Height() - 1),
159 Point(aSz.Width(), aSz.Height() - 1));
160 rRenderContext.SetLineColor(aOldLineColor);
163 void lcl_SeparateNameAndIndex( const OUString& rVName, OUString& rVar, OUString& rIndex )
165 rVar = rVName;
166 rIndex.clear();
167 sal_Int32 nIndexStart = rVar.indexOf( '(' );
168 if ( nIndexStart != -1 )
170 sal_Int32 nIndexEnd = rVar.indexOf( ')', nIndexStart );
171 if (nIndexEnd != -1)
173 rIndex = rVar.copy(nIndexStart + 1, nIndexEnd - nIndexStart - 1);
174 rVar = rVar.copy(0, nIndexStart);
175 rVar = comphelper::string::stripEnd(rVar, ' ');
176 rIndex = comphelper::string::strip(rIndex, ' ');
180 if ( !rVar.isEmpty() )
182 sal_uInt16 nLastChar = rVar.getLength()-1;
183 if ( cSuffixes.find(rVar[ nLastChar ] ) != std::u16string_view::npos )
184 rVar = rVar.replaceAt( nLastChar, 1, u"" );
186 if ( !rIndex.isEmpty() )
188 sal_uInt16 nLastChar = rIndex.getLength()-1;
189 if ( cSuffixes.find(rIndex[ nLastChar ] ) != std::u16string_view::npos )
190 rIndex = rIndex.replaceAt( nLastChar, 1, u"" );
194 } // namespace
197 // EditorWindow
200 class EditorWindow::ChangesListener:
201 public cppu::WeakImplHelper< beans::XPropertiesChangeListener >
203 public:
204 explicit ChangesListener(EditorWindow & editor): editor_(editor) {}
206 private:
207 virtual ~ChangesListener() override {}
209 virtual void SAL_CALL disposing(lang::EventObject const &) override
211 std::unique_lock g(editor_.mutex_);
212 editor_.notifier_.clear();
215 virtual void SAL_CALL propertiesChange(
216 Sequence< beans::PropertyChangeEvent > const &) override
218 SolarMutexGuard g;
219 editor_.ImplSetFont();
222 EditorWindow & editor_;
225 class EditorWindow::ProgressInfo : public SfxProgress
227 public:
228 ProgressInfo (SfxObjectShell* pObjSh, OUString const& rText, sal_uInt32 nRange) :
229 SfxProgress(pObjSh, rText, nRange),
230 nCurState(0)
233 void StepProgress ()
235 SetState(++nCurState);
238 private:
239 sal_uInt32 nCurState;
242 EditorWindow::EditorWindow (vcl::Window* pParent, ModulWindow* pModulWindow) :
243 Window(pParent, WB_BORDER),
244 rModulWindow(*pModulWindow),
245 nCurTextWidth(0),
246 m_nSetSourceInBasicId(nullptr),
247 aHighlighter(HighlighterLanguage::Basic),
248 aSyntaxIdle( "basctl EditorWindow aSyntaxIdle" ),
249 bHighlighting(false),
250 bDoSyntaxHighlight(true),
251 bDelayHighlight(true),
252 m_nLastHighlightPara(0),
253 pCodeCompleteWnd(VclPtr<CodeCompleteWindow>::Create(this))
255 set_id(u"EditorWindow"_ustr);
256 const Wallpaper aBackground(rModulWindow.GetLayout().GetSyntaxBackgroundColor());
257 SetBackground(aBackground);
258 GetWindow(GetWindowType::Border)->SetBackground(aBackground);
259 SetLineHighlightColor(GetShell()->GetColorConfig()->GetCurrentColorScheme().m_aLineHighlightColor);
260 SetPointer( PointerStyle::Text );
261 SetHelpId( HID_BASICIDE_EDITORWINDOW );
263 listener_ = new ChangesListener(*this);
264 Reference< beans::XMultiPropertySet > n(
265 officecfg::Office::Common::Font::SourceViewFont::get(),
266 UNO_QUERY_THROW);
268 std::unique_lock g(mutex_);
269 notifier_ = n;
272 // The zoom level applied to the editor window is the zoom slider value in the shell
273 nCurrentZoomLevel = GetShell()->GetCurrentZoomSliderValue();
275 const Sequence<OUString> aPropertyNames{u"FontHeight"_ustr, u"FontName"_ustr};
276 n->addPropertiesChangeListener(aPropertyNames, listener_);
280 EditorWindow::~EditorWindow()
282 disposeOnce();
285 void EditorWindow::dispose()
287 if (m_nSetSourceInBasicId)
289 Application::RemoveUserEvent(m_nSetSourceInBasicId);
290 m_nSetSourceInBasicId = nullptr;
293 Reference< beans::XMultiPropertySet > n;
295 std::unique_lock g(mutex_);
296 n = notifier_;
298 if (n.is()) {
299 n->removePropertiesChangeListener(listener_);
302 aSyntaxIdle.Stop();
304 if ( pEditEngine )
306 EndListening( *pEditEngine );
307 pEditEngine->RemoveView(pEditView.get());
309 pCodeCompleteWnd.disposeAndClear();
310 vcl::Window::dispose();
313 OUString EditorWindow::GetWordAtCursor()
315 OUString aWord;
317 if ( pEditView )
319 TextEngine* pTextEngine = pEditView->GetTextEngine();
320 if ( pTextEngine )
322 // check first, if the cursor is at a help URL
323 const TextSelection& rSelection = pEditView->GetSelection();
324 const TextPaM& rSelStart = rSelection.GetStart();
325 const TextPaM& rSelEnd = rSelection.GetEnd();
326 OUString aText = pTextEngine->GetText( rSelEnd.GetPara() );
327 CharClass aClass( ::comphelper::getProcessComponentContext() , Application::GetSettings().GetLanguageTag() );
328 sal_Int32 nSelStart = rSelStart.GetIndex();
329 sal_Int32 nSelEnd = rSelEnd.GetIndex();
330 sal_Int32 nLength = aText.getLength();
331 sal_Int32 nStart = 0;
332 sal_Int32 nEnd = nLength;
333 while ( nStart < nLength )
335 OUString aURL( URIHelper::FindFirstURLInText( aText, nStart, nEnd, aClass ) );
336 INetURLObject aURLObj( aURL );
337 if ( aURLObj.GetProtocol() == INetProtocol::VndSunStarHelp
338 && nSelStart >= nStart && nSelStart <= nEnd && nSelEnd >= nStart && nSelEnd <= nEnd )
340 aWord = aURL;
341 break;
343 nStart = nEnd;
344 nEnd = nLength;
347 // Not the selected range, but at the CursorPosition,
348 // if a word is partially selected.
349 if ( aWord.isEmpty() )
350 aWord = pTextEngine->GetWord( rSelEnd );
352 // Can be empty when full word selected, as Cursor behind it
353 if ( aWord.isEmpty() && pEditView->HasSelection() )
354 aWord = pTextEngine->GetWord( rSelStart );
358 return aWord;
361 void EditorWindow::RequestHelp( const HelpEvent& rHEvt )
363 bool bDone = false;
365 // Should have been activated at some point
366 if ( pEditEngine )
368 if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
370 OUString aKeyword = GetWordAtCursor();
371 Application::GetHelp()->SearchKeyword( aKeyword );
372 bDone = true;
374 else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
376 OUString aHelpText;
377 tools::Rectangle aHelpRect;
378 if ( StarBASIC::IsRunning() )
380 Point aWindowPos = rHEvt.GetMousePosPixel();
381 aWindowPos = ScreenToOutputPixel( aWindowPos );
382 Point aDocPos = GetEditView()->GetDocPos( aWindowPos );
383 TextPaM aCursor = GetEditView()->GetTextEngine()->GetPaM(aDocPos);
384 TextPaM aStartOfWord;
385 OUString aWord = GetEditView()->GetTextEngine()->GetWord( aCursor, &aStartOfWord );
386 if ( !aWord.isEmpty() && !comphelper::string::isdigitAsciiString(aWord) )
388 sal_uInt16 nLastChar = aWord.getLength() - 1;
389 if ( cSuffixes.find(aWord[ nLastChar ] ) != std::u16string_view::npos )
390 aWord = aWord.replaceAt( nLastChar, 1, u"" );
391 SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord );
392 if (SbxVariable const* pVar = IsSbxVariable(pSBX))
394 SbxDataType eType = pVar->GetType();
395 if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
396 // might cause a crash e. g. at the selections-object
397 // Type == Object does not mean pVar == Object!
398 ; // aHelpText = ((SbxObject*)pVar)->GetClassName();
399 else if ( eType & SbxARRAY )
400 ; // aHelpText = "{...}";
401 else if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxEMPTY) )
403 aHelpText = pVar->GetName();
404 if ( aHelpText.isEmpty() ) // name is not copied with the passed parameters
405 aHelpText = aWord;
406 aHelpText += "=" + pVar->GetOUString();
409 if ( !aHelpText.isEmpty() )
411 tools::Rectangle aStartWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aStartOfWord));
412 TextPaM aEndOfWord(aStartOfWord.GetPara(), aStartOfWord.GetIndex() + aWord.getLength());
413 tools::Rectangle aEndWordRect(GetEditView()->GetTextEngine()->PaMtoEditCursor(aEndOfWord));
414 aHelpRect = aStartWordRect.GetUnion(aEndWordRect);
416 Point aTopLeft = GetEditView()->GetWindowPos(aHelpRect.TopLeft());
417 aTopLeft = GetEditView()->GetWindow()->OutputToScreenPixel(aTopLeft);
419 aHelpRect.SetPos(aTopLeft);
423 Help::ShowQuickHelp( this, aHelpRect, aHelpText, QuickHelpFlags::NONE);
424 bDone = true;
428 if ( !bDone )
429 Window::RequestHelp( rHEvt );
433 void EditorWindow::Resize()
435 // ScrollBars, etc. happens in Adjust...
436 if ( !pEditView )
437 return;
439 tools::Long nVisY = pEditView->GetStartDocPos().Y();
441 pEditView->ShowCursor();
442 Size aOutSz( GetOutputSizePixel() );
443 tools::Long nMaxVisAreaStart = pEditView->GetTextEngine()->GetTextHeight() - aOutSz.Height();
444 if ( nMaxVisAreaStart < 0 )
445 nMaxVisAreaStart = 0;
446 if ( pEditView->GetStartDocPos().Y() > nMaxVisAreaStart )
448 Point aStartDocPos( pEditView->GetStartDocPos() );
449 aStartDocPos.setY( nMaxVisAreaStart );
450 pEditView->SetStartDocPos( aStartDocPos );
451 pEditView->ShowCursor();
452 rModulWindow.GetBreakPointWindow().GetCurYOffset() = aStartDocPos.Y();
453 rModulWindow.GetLineNumberWindow().GetCurYOffset() = aStartDocPos.Y();
455 InitScrollBars();
456 if ( nVisY != pEditView->GetStartDocPos().Y() )
457 Invalidate();
461 void EditorWindow::MouseMove( const MouseEvent &rEvt )
463 if ( pEditView )
464 pEditView->MouseMove( rEvt );
468 void EditorWindow::MouseButtonUp( const MouseEvent &rEvt )
470 if ( pEditView )
472 pEditView->MouseButtonUp( rEvt );
473 if (SfxBindings* pBindings = GetBindingsPtr())
475 pBindings->Invalidate( SID_BASICIDE_STAT_POS );
476 pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
481 void EditorWindow::MouseButtonDown( const MouseEvent &rEvt )
483 GrabFocus();
484 if (!pEditView)
485 return;
486 pEditView->MouseButtonDown(rEvt);
487 if( pCodeCompleteWnd->IsVisible() )
489 if (pEditView->GetSelection() != pCodeCompleteWnd->GetTextSelection())
491 //selection changed, code complete window should be hidden
492 pCodeCompleteWnd->HideAndRestoreFocus();
497 void EditorWindow::Command( const CommandEvent& rCEvt )
499 if ( !pEditView )
500 return;
502 pEditView->Command( rCEvt );
503 if ( ( rCEvt.GetCommand() == CommandEventId::Wheel ) ||
504 ( rCEvt.GetCommand() == CommandEventId::StartAutoScroll ) ||
505 ( rCEvt.GetCommand() == CommandEventId::AutoScroll ) )
507 const CommandWheelData* pData = rCEvt.GetWheelData();
509 // Check if it is a Ctrl+Wheel zoom command
510 if (pData && pData->IsMod1())
512 const sal_uInt16 nOldZoom = GetCurrentZoom();
513 sal_uInt16 nNewZoom;
514 if( pData->GetDelta() < 0 )
515 nNewZoom = std::max<sal_uInt16>(basctl::Shell::GetMinZoom(),
516 basegfx::zoomtools::zoomOut(nOldZoom));
517 else
518 nNewZoom = std::min<sal_uInt16>(basctl::Shell::GetMaxZoom(),
519 basegfx::zoomtools::zoomIn(nOldZoom));
520 GetShell()->SetGlobalEditorZoomLevel(nNewZoom);
522 else
523 HandleScrollCommand(rCEvt, &rModulWindow.GetEditHScrollBar(), &rModulWindow.GetEditVScrollBar());
525 else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) {
526 SfxDispatcher* pDispatcher = GetDispatcher();
527 if ( pDispatcher )
529 SfxDispatcher::ExecutePopup();
531 if( pCodeCompleteWnd->IsVisible() ) // hide the code complete window
532 pCodeCompleteWnd->ClearAndHide();
536 bool EditorWindow::ImpCanModify()
538 bool bCanModify = true;
539 if ( StarBASIC::IsRunning() && rModulWindow.GetBasicStatus().bIsRunning )
541 // If in Trace-mode, abort the trace or refuse input
542 // Remove markers in the modules in Notify at Basic::Stopped
543 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
544 VclMessageType::Question, VclButtonsType::OkCancel,
545 IDEResId(RID_STR_WILLSTOPPRG)));
546 if (xQueryBox->run() == RET_OK)
548 rModulWindow.GetBasicStatus().bIsRunning = false;
549 StopBasic();
551 else
552 bCanModify = false;
554 return bCanModify;
557 void EditorWindow::KeyInput( const KeyEvent& rKEvt )
559 if ( !pEditView ) // Happens in Win95
560 return;
562 bool const bWasModified = pEditEngine->IsModified();
563 // see if there is an accelerator to be processed first
564 SfxViewShell *pVS( SfxViewShell::Current());
565 bool bDone = pVS && pVS->KeyInput( rKEvt );
567 if (pCodeCompleteWnd->IsVisible() && CodeCompleteOptions::IsCodeCompleteOn())
569 pCodeCompleteWnd->HandleKeyInput(rKEvt);
570 if( rKEvt.GetKeyCode().GetCode() == KEY_UP
571 || rKEvt.GetKeyCode().GetCode() == KEY_DOWN
572 || rKEvt.GetKeyCode().GetCode() == KEY_TAB
573 || rKEvt.GetKeyCode().GetCode() == KEY_POINT)
574 return;
577 if( (rKEvt.GetKeyCode().GetCode() == KEY_SPACE ||
578 rKEvt.GetKeyCode().GetCode() == KEY_TAB ||
579 rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) && CodeCompleteOptions::IsAutoCorrectOn() )
581 HandleAutoCorrect();
584 if( rKEvt.GetCharCode() == '"' && CodeCompleteOptions::IsAutoCloseQuotesOn() )
585 {//autoclose double quotes
586 HandleAutoCloseDoubleQuotes();
589 if( rKEvt.GetCharCode() == '(' && CodeCompleteOptions::IsAutoCloseParenthesisOn() )
590 {//autoclose parenthesis
591 HandleAutoCloseParen();
594 if( rKEvt.GetKeyCode().GetCode() == KEY_RETURN && CodeCompleteOptions::IsProcedureAutoCompleteOn() )
595 {//autoclose implementation
596 HandleProcedureCompletion();
599 if( rKEvt.GetKeyCode().GetCode() == KEY_POINT && CodeCompleteOptions::IsCodeCompleteOn() )
601 HandleCodeCompletion();
603 if ( !bDone && ( !TextEngine::DoesKeyChangeText( rKEvt ) || ImpCanModify() ) )
605 if ( ( rKEvt.GetKeyCode().GetCode() == KEY_TAB ) && !rKEvt.GetKeyCode().IsMod1() &&
606 !rKEvt.GetKeyCode().IsMod2() && !GetEditView()->IsReadOnly() )
608 TextSelection aSel( pEditView->GetSelection() );
609 if ( aSel.GetStart().GetPara() != aSel.GetEnd().GetPara() )
611 bDelayHighlight = false;
612 if ( !rKEvt.GetKeyCode().IsShift() )
613 pEditView->IndentBlock();
614 else
615 pEditView->UnindentBlock();
616 bDelayHighlight = true;
617 bDone = true;
620 if ( !bDone )
621 bDone = pEditView->KeyInput( rKEvt );
623 if ( !bDone )
625 Window::KeyInput( rKEvt );
627 else
629 if (SfxBindings* pBindings = GetBindingsPtr())
631 pBindings->Invalidate( SID_BASICIDE_STAT_POS );
632 pBindings->Invalidate( SID_BASICIDE_STAT_TITLE );
633 if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR )
635 pBindings->Update( SID_BASICIDE_STAT_POS );
636 pBindings->Update( SID_BASICIDE_STAT_TITLE );
638 if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_ALPHA ||
639 rKEvt.GetKeyCode().GetGroup() == KEYGROUP_NUM )
641 // If the module is read-only, warn that it can't be edited
642 if ( rModulWindow.IsReadOnly() )
643 rModulWindow.ShowReadOnlyInfoBar();
645 if ( !bWasModified && pEditEngine->IsModified() )
647 pBindings->Invalidate( SID_SAVEDOC );
648 pBindings->Invalidate( SID_DOC_MODIFIED );
649 pBindings->Invalidate( SID_UNDO );
651 if ( rKEvt.GetKeyCode().GetCode() == KEY_INSERT )
652 pBindings->Invalidate( SID_ATTR_INSERT );
657 void EditorWindow::HandleAutoCorrect()
659 TextSelection aSel = GetEditView()->GetSelection();
660 const sal_uInt32 nLine = aSel.GetStart().GetPara();
661 const sal_Int32 nIndex = aSel.GetStart().GetIndex();
662 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
663 const OUString sActSubName = GetActualSubName( nLine ); // the actual procedure
665 std::vector<HighlightPortion> aPortions;
666 aHighlighter.getHighlightPortions( aLine, aPortions );
668 if( aPortions.empty() )
669 return;
671 HighlightPortion& r = aPortions.back();
672 if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
673 {//cursor is not standing at the end of the line
674 for (auto const& portion : aPortions)
676 if( portion.nEnd == nIndex )
678 r = portion;
679 break;
684 OUString sStr = aLine.copy( r.nBegin, r.nEnd - r.nBegin );
685 //if WS or empty string: stop, nothing to do
686 if( ( r.tokenType == TokenType::Whitespace ) || sStr.isEmpty() )
687 return;
688 //create the appropriate TextSelection, and update the cache
689 TextPaM aStart( nLine, r.nBegin );
690 TextPaM aEnd( nLine, r.nBegin + sStr.getLength() );
691 TextSelection sTextSelection( aStart, aEnd );
692 rModulWindow.UpdateModule();
693 rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse( aCodeCompleteCache );
694 // correct the last entered keyword
695 if( r.tokenType == TokenType::Keywords )
697 sStr = sStr.toAsciiLowerCase();
698 if( !SbModule::GetKeywordCase(sStr).isEmpty() )
699 // if it is a keyword, get its correct case
700 sStr = SbModule::GetKeywordCase(sStr);
701 else
702 // else capitalize first letter/select the correct one, and replace
703 sStr = sStr.replaceAt( 0, 1, OUString(sStr[0]).toAsciiUpperCase() );
705 pEditEngine->ReplaceText( sTextSelection, sStr );
706 pEditView->SetSelection( aSel );
708 if( r.tokenType != TokenType::Identifier )
709 return;
711 // correct variables
712 if( !aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName ).isEmpty() )
714 sStr = aCodeCompleteCache.GetCorrectCaseVarName( sStr, sActSubName );
715 pEditEngine->ReplaceText( sTextSelection, sStr );
716 pEditView->SetSelection( aSel );
718 else
720 //autocorrect procedures
721 SbxArray* pArr = rModulWindow.GetSbModule()->GetMethods().get();
722 for (sal_uInt32 i = 0; i < pArr->Count(); ++i)
724 if (pArr->Get(i)->GetName().equalsIgnoreAsciiCase(sStr))
726 sStr = pArr->Get(i)->GetName(); //if found, get the correct case
727 pEditEngine->ReplaceText( sTextSelection, sStr );
728 pEditView->SetSelection( aSel );
729 return;
735 void EditorWindow::SetLineHighlightColor(Color aColor)
737 m_aLineHighlightColor = aColor;
740 TextSelection EditorWindow::GetLastHighlightPortionTextSelection() const
741 {//creates a text selection from the highlight portion on the cursor
742 const sal_uInt32 nLine = GetEditView()->GetSelection().GetStart().GetPara();
743 const sal_Int32 nIndex = GetEditView()->GetSelection().GetStart().GetIndex();
744 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
745 std::vector<HighlightPortion> aPortions;
746 aHighlighter.getHighlightPortions( aLine, aPortions );
748 assert(!aPortions.empty());
749 HighlightPortion& r = aPortions.back();
750 if( static_cast<size_t>(nIndex) != aPortions.size()-1 )
751 {//cursor is not standing at the end of the line
752 for (auto const& portion : aPortions)
754 if( portion.nEnd == nIndex )
756 r = portion;
757 break;
762 if( aPortions.empty() )
763 return TextSelection();
765 std::u16string_view sStr = aLine.subView( r.nBegin, r.nEnd - r.nBegin );
766 TextPaM aStart( nLine, r.nBegin );
767 TextPaM aEnd( nLine, r.nBegin + sStr.size() );
768 return TextSelection( aStart, aEnd );
771 void EditorWindow::HandleAutoCloseParen()
773 TextSelection aSel = GetEditView()->GetSelection();
774 const sal_uInt32 nLine = aSel.GetStart().GetPara();
775 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
777 if( aLine.getLength() > 0 && aLine[aSel.GetEnd().GetIndex()-1] != '(' )
779 GetEditView()->InsertText(u")"_ustr);
780 //leave the cursor on its place: inside the parenthesis
781 TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
782 GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
786 void EditorWindow::HandleAutoCloseDoubleQuotes()
788 TextSelection aSel = GetEditView()->GetSelection();
789 const sal_uInt32 nLine = aSel.GetStart().GetPara();
790 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
792 std::vector<HighlightPortion> aPortions;
793 aHighlighter.getHighlightPortions( aLine, aPortions );
795 if( aPortions.empty() )
796 return;
798 if( aLine.getLength() > 0 && !aLine.endsWith("\"") && (aPortions.back().tokenType != TokenType::String) )
800 GetEditView()->InsertText(u"\""_ustr);
801 //leave the cursor on its place: inside the two double quotes
802 TextPaM aEnd(nLine, aSel.GetEnd().GetIndex());
803 GetEditView()->SetSelection( TextSelection( aEnd, aEnd ) );
807 void EditorWindow::HandleProcedureCompletion()
810 TextSelection aSel = GetEditView()->GetSelection();
811 const sal_uInt32 nLine = aSel.GetStart().GetPara();
812 OUString aLine( pEditEngine->GetText( nLine ) );
814 OUString sProcType;
815 OUString sProcName;
816 bool bFoundName = GetProcedureName(aLine, sProcType, sProcName);
817 if (!bFoundName)
818 return;
820 OUString sText(u"\nEnd "_ustr);
821 aSel = GetEditView()->GetSelection();
822 if( sProcType.equalsIgnoreAsciiCase("function") )
823 sText += "Function\n";
824 if( sProcType.equalsIgnoreAsciiCase("sub") )
825 sText += "Sub\n";
827 if( nLine+1 == pEditEngine->GetParagraphCount() )
829 pEditView->InsertText( sText );//append to the end
830 GetEditView()->SetSelection(aSel);
832 else
834 for( sal_uInt32 i = nLine+1; i < pEditEngine->GetParagraphCount(); ++i )
835 {//searching forward for end token, or another sub/function definition
836 OUString aCurrLine = pEditEngine->GetText( i );
837 std::vector<HighlightPortion> aCurrPortions;
838 aHighlighter.getHighlightPortions( aCurrLine, aCurrPortions );
840 if( aCurrPortions.size() >= 3 )
841 {//at least 3 tokens: (sub|function) whitespace identifier...
842 HighlightPortion& r = aCurrPortions.front();
843 std::u16string_view sStr = aCurrLine.subView(r.nBegin, r.nEnd - r.nBegin);
845 if( r.tokenType == TokenType::Keywords )
847 if( o3tl::equalsIgnoreAsciiCase(sStr, u"sub") || o3tl::equalsIgnoreAsciiCase(sStr, u"function") )
849 pEditView->InsertText( sText );//append to the end
850 GetEditView()->SetSelection(aSel);
851 break;
853 if( o3tl::equalsIgnoreAsciiCase(sStr, u"end") )
854 break;
861 bool EditorWindow::GetProcedureName(std::u16string_view rLine, OUString& rProcType, OUString& rProcName) const
863 std::vector<HighlightPortion> aPortions;
864 aHighlighter.getHighlightPortions(rLine, aPortions);
866 if( aPortions.empty() )
867 return false;
869 bool bFoundType = false;
870 bool bFoundName = false;
872 for (auto const& portion : aPortions)
874 std::u16string_view sTokStr = rLine.substr(portion.nBegin, portion.nEnd - portion.nBegin);
876 if( portion.tokenType == TokenType::Keywords && ( o3tl::equalsIgnoreAsciiCase(sTokStr, u"sub")
877 || o3tl::equalsIgnoreAsciiCase(sTokStr, u"function")) )
879 rProcType = sTokStr;
880 bFoundType = true;
882 if( portion.tokenType == TokenType::Identifier && bFoundType )
884 rProcName = sTokStr;
885 bFoundName = true;
886 break;
890 if( !bFoundType || !bFoundName )
891 return false;// no sub/function keyword or there is no identifier
893 return true;
897 void EditorWindow::HandleCodeCompletion()
899 rModulWindow.UpdateModule();
900 rModulWindow.GetSbModule()->GetCodeCompleteDataFromParse(aCodeCompleteCache);
901 TextSelection aSel = GetEditView()->GetSelection();
902 const sal_uInt32 nLine = aSel.GetStart().GetPara();
903 OUString aLine( pEditEngine->GetText( nLine ) ); // the line being modified
904 std::vector< OUString > aVect; //vector to hold the base variable+methods for the nested reflection
906 std::vector<HighlightPortion> aPortions;
907 aLine = aLine.copy(0, aSel.GetEnd().GetIndex());
908 aHighlighter.getHighlightPortions( aLine, aPortions );
909 if( aPortions.empty() )
910 return;
912 //use the syntax highlighter to grab out nested reflection calls, eg. aVar.aMethod("aa").aOtherMethod ..
913 for( std::vector<HighlightPortion>::reverse_iterator i(
914 aPortions.rbegin());
915 i != aPortions.rend(); ++i)
917 if( i->tokenType == TokenType::Whitespace ) // a whitespace: stop; if there is no ws, it goes to the beginning of the line
918 break;
919 if( i->tokenType == TokenType::Identifier || i->tokenType == TokenType::Keywords ) // extract the identifiers(methods, base variable)
920 /* an example: Dim aLocVar2 as com.sun.star.beans.PropertyValue
921 * here, aLocVar2.Name, and PropertyValue's Name field is treated as a keyword(?!)
922 * */
923 aVect.insert( aVect.begin(), aLine.copy(i->nBegin, i->nEnd - i->nBegin) );
926 if( aVect.empty() )//nothing to do
927 return;
929 OUString sBaseName = aVect[aVect.size()-1];//variable name
930 OUString sVarType = aCodeCompleteCache.GetVarType( sBaseName );
932 if( !sVarType.isEmpty() && CodeCompleteOptions::IsAutoCorrectOn() )
933 {//correct variable name, if autocorrection on
934 const OUString sStr = aCodeCompleteCache.GetCorrectCaseVarName( sBaseName, GetActualSubName(nLine) );
935 if( !sStr.isEmpty() )
937 TextPaM aStart(nLine, aSel.GetStart().GetIndex() - sStr.getLength() );
938 TextSelection sTextSelection(aStart, TextPaM(nLine, aSel.GetStart().GetIndex()));
939 pEditEngine->ReplaceText( sTextSelection, sStr );
940 pEditView->SetSelection( aSel );
944 UnoTypeCodeCompletetor aTypeCompletor( aVect, sVarType );
946 if( !aTypeCompletor.CanCodeComplete() )
947 return;
949 std::vector< OUString > aEntryVect;//entries to be inserted into the list
950 std::vector< OUString > aFieldVect = aTypeCompletor.GetXIdlClassFields();//fields
951 aEntryVect.insert(aEntryVect.end(), aFieldVect.begin(), aFieldVect.end() );
952 if( CodeCompleteOptions::IsExtendedTypeDeclaration() )
953 {// if extended types on, reflect classes, else just the structs (XIdlClass without methods)
954 std::vector< OUString > aMethVect = aTypeCompletor.GetXIdlClassMethods();//methods
955 aEntryVect.insert(aEntryVect.end(), aMethVect.begin(), aMethVect.end() );
957 if( !aEntryVect.empty() )
958 SetupAndShowCodeCompleteWnd( aEntryVect, aSel );
961 void EditorWindow::SetupAndShowCodeCompleteWnd( const std::vector< OUString >& aEntryVect, TextSelection aSel )
963 // clear the listbox
964 pCodeCompleteWnd->ClearListBox();
965 // fill the listbox
966 for(const auto & l : aEntryVect)
968 pCodeCompleteWnd->InsertEntry( l );
970 // show it
971 pCodeCompleteWnd->Show();
972 pCodeCompleteWnd->ResizeAndPositionListBox();
973 pCodeCompleteWnd->SelectFirstEntry();
974 // correct text selection, and set it
975 ++aSel.GetStart().GetIndex();
976 ++aSel.GetEnd().GetIndex();
977 pCodeCompleteWnd->SetTextSelection( aSel );
978 //give the focus to the EditView
979 pEditView->GetWindow()->GrabFocus();
982 void EditorWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
984 if (!pEditEngine) // We need it now at latest
985 CreateEditEngine();
987 HighlightCurrentLine(rRenderContext);
989 pEditView->Paint(rRenderContext, rRect);
992 void EditorWindow::HighlightCurrentLine(vcl::RenderContext& rRenderContext)
994 // If the cursor is in a single line and nothing is selected, then a highlight color
995 // is applied to the background of the current line
996 TextPaM aStartPaM = pEditView->GetSelection().GetStart();
997 TextPaM aEndPaM = pEditView->GetSelection().GetEnd();
998 if (aStartPaM == aEndPaM)
1000 Size aWinSize(GetOutputSizePixel());
1001 sal_Int16 nDocPosY = pEditView->GetStartDocPos().Y();
1002 sal_Int16 nY1 = pEditEngine->PaMtoEditCursor(aStartPaM).TopLeft().Y();
1003 sal_Int16 nY2 = pEditEngine->PaMtoEditCursor(aStartPaM).BottomRight().Y();
1004 // Only draw if the cursor is in a visible position
1005 if ((nY1 >= nDocPosY && nY1 <= nDocPosY + aWinSize.Height())
1006 || (nY2 >= nDocPosY && nY2 <= nDocPosY + aWinSize.Height()))
1008 tools::Rectangle aRect(Point(0, nY1 - nDocPosY), Point(aWinSize.Width(), nY2 - nDocPosY));
1009 rRenderContext.SetFillColor(m_aLineHighlightColor);
1010 rRenderContext.DrawRect(aRect);
1015 void EditorWindow::LoseFocus()
1017 // tdf#114258 wait until the next event loop cycle to do this so it doesn't
1018 // happen during a mouse down/up selection in the treeview whose contents
1019 // this may update
1020 if (!m_nSetSourceInBasicId)
1021 m_nSetSourceInBasicId = Application::PostUserEvent(LINK(this, EditorWindow, SetSourceInBasicHdl));
1022 Window::LoseFocus();
1025 IMPL_LINK_NOARG(EditorWindow, SetSourceInBasicHdl, void*, void)
1027 m_nSetSourceInBasicId = nullptr;
1028 SetSourceInBasic();
1031 void EditorWindow::SetSourceInBasic()
1033 if ( pEditEngine && pEditEngine->IsModified()
1034 && !GetEditView()->IsReadOnly() ) // Added for #i60626, otherwise
1035 // any read only bug in the text engine could lead to a crash later
1037 if ( !StarBASIC::IsRunning() ) // Not at runtime!
1039 rModulWindow.UpdateModule();
1044 // Returns the position of the last character of any of the following
1045 // EOL char combinations: CR, CR/LF, LF, return -1 if no EOL is found
1046 sal_Int32 searchEOL( std::u16string_view rStr, sal_Int32 fromIndex )
1048 size_t iLF = rStr.find( LINE_SEP, fromIndex );
1049 if( iLF != std::u16string_view::npos )
1050 return iLF;
1052 size_t iCR = rStr.find( LINE_SEP_CR, fromIndex );
1053 return iCR == std::u16string_view::npos ? -1 : iCR;
1056 void EditorWindow::CreateEditEngine()
1058 if (pEditEngine)
1059 return;
1061 pEditEngine.reset(new ExtTextEngine);
1062 pEditView.reset(new TextView(pEditEngine.get(), this));
1063 pEditView->SetAutoIndentMode(true);
1064 pEditEngine->SetUpdateMode(false);
1065 pEditEngine->InsertView(pEditView.get());
1067 ImplSetFont();
1069 aSyntaxIdle.SetInvokeHandler( LINK( this, EditorWindow, SyntaxTimerHdl ) );
1071 bool bWasDoSyntaxHighlight = bDoSyntaxHighlight;
1072 bDoSyntaxHighlight = false; // too slow for large texts...
1073 OUString aOUSource(rModulWindow.GetModule());
1074 sal_Int32 nLines = 0;
1075 sal_Int32 nIndex = -1;
1078 nLines++;
1079 nIndex = searchEOL( aOUSource, nIndex+1 );
1081 while (nIndex >= 0);
1083 // nLines*4: SetText+Formatting+DoHighlight+Formatting
1084 // it could be cut down on one formatting but you would wait even longer
1085 // for the text then if the source code is long...
1086 pProgress.reset(new ProgressInfo(GetShell()->GetViewFrame().GetObjectShell(),
1087 IDEResId(RID_STR_GENERATESOURCE),
1088 nLines * 4));
1089 setTextEngineText(*pEditEngine, aOUSource);
1091 pEditView->SetStartDocPos(Point(0, 0));
1092 pEditView->SetSelection(TextSelection());
1093 rModulWindow.GetBreakPointWindow().GetCurYOffset() = 0;
1094 rModulWindow.GetLineNumberWindow().GetCurYOffset() = 0;
1095 pEditEngine->SetUpdateMode(true);
1096 rModulWindow.PaintImmediately(); // has only been invalidated at UpdateMode = true
1098 pEditView->ShowCursor();
1100 StartListening(*pEditEngine);
1102 aSyntaxIdle.Stop();
1103 bDoSyntaxHighlight = bWasDoSyntaxHighlight;
1105 for (sal_Int32 nLine = 0; nLine < nLines; nLine++)
1106 aSyntaxLineTable.insert(nLine);
1107 ForceSyntaxTimeout();
1109 pProgress.reset();
1111 pEditEngine->SetModified( false );
1112 pEditEngine->EnableUndo( true );
1114 InitScrollBars();
1116 if (SfxBindings* pBindings = GetBindingsPtr())
1118 pBindings->Invalidate(SID_BASICIDE_STAT_POS);
1119 pBindings->Invalidate(SID_BASICIDE_STAT_TITLE);
1122 DBG_ASSERT(rModulWindow.GetBreakPointWindow().GetCurYOffset() == 0, "CreateEditEngine: breakpoints moved?");
1124 // set readonly mode for readonly libraries
1125 ScriptDocument aDocument(rModulWindow.GetDocument());
1126 OUString aOULibName(rModulWindow.GetLibName());
1127 Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY );
1128 if (xModLibContainer.is()
1129 && xModLibContainer->hasByName(aOULibName)
1130 && xModLibContainer->isLibraryReadOnly(aOULibName))
1132 rModulWindow.SetReadOnly(true);
1135 if (aDocument.isDocument() && aDocument.isReadOnly())
1136 rModulWindow.SetReadOnly(true);
1139 void EditorWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
1141 if( rHint.GetId() == SfxHintId::TextViewScrolled )
1143 rModulWindow.GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() );
1144 rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1145 rModulWindow.GetBreakPointWindow().DoScroll
1146 ( rModulWindow.GetBreakPointWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1147 rModulWindow.GetLineNumberWindow().DoScroll
1148 ( rModulWindow.GetLineNumberWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
1150 else if( rHint.GetId() == SfxHintId::TextHeightChanged )
1152 if ( pEditView->GetStartDocPos().Y() )
1154 tools::Long nOutHeight = GetOutputSizePixel().Height();
1155 tools::Long nTextHeight = pEditEngine->GetTextHeight();
1156 if ( nTextHeight < nOutHeight )
1157 pEditView->Scroll( 0, pEditView->GetStartDocPos().Y() );
1159 rModulWindow.GetLineNumberWindow().Invalidate();
1162 SetScrollBarRanges();
1164 else if( rHint.GetId() == SfxHintId::TextFormatted )
1167 const tools::Long nWidth = pEditEngine->CalcTextWidth();
1168 if ( nWidth != nCurTextWidth )
1170 nCurTextWidth = nWidth;
1171 rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1) );
1172 rModulWindow.GetEditHScrollBar().SetThumbPos( pEditView->GetStartDocPos().X() );
1174 tools::Long nPrevTextWidth = nCurTextWidth;
1175 nCurTextWidth = pEditEngine->CalcTextWidth();
1176 if ( nCurTextWidth != nPrevTextWidth )
1177 SetScrollBarRanges();
1179 else if( rHint.GetId() == SfxHintId::TextParaInserted )
1181 TextHint const & rTextHint = static_cast<TextHint const&>(rHint);
1182 ParagraphInsertedDeleted( rTextHint.GetValue(), true );
1183 DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1185 else if( rHint.GetId() == SfxHintId::TextParaRemoved )
1187 TextHint const & rTextHint = static_cast<TextHint const&>(rHint);
1188 ParagraphInsertedDeleted( rTextHint.GetValue(), false );
1190 else if( rHint.GetId() == SfxHintId::TextParaContentChanged )
1192 TextHint const & rTextHint = static_cast<TextHint const&>(rHint);
1193 DoDelayedSyntaxHighlight( rTextHint.GetValue() );
1195 else if( rHint.GetId() == SfxHintId::TextViewSelectionChanged )
1197 if (SfxBindings* pBindings = GetBindingsPtr())
1199 pBindings->Invalidate( SID_CUT );
1200 pBindings->Invalidate( SID_COPY );
1203 else if( rHint.GetId() == SfxHintId::TextViewCaretChanged )
1205 // Check whether the line number where the caret is has changed and the
1206 // highlight needs to be redrawn
1207 sal_uInt32 nStartPara = pEditView->GetSelection().GetStart().GetPara();
1208 sal_uInt32 nEndPara = pEditView->GetSelection().GetEnd().GetPara();
1209 if (nStartPara == nEndPara && nStartPara != m_nLastHighlightPara)
1211 m_nLastHighlightPara = nStartPara;
1212 Invalidate();
1213 rModulWindow.GetLineNumberWindow().Invalidate();
1215 else if (nStartPara != nEndPara)
1217 // If multiple lines are selected, then update the line number window
1218 rModulWindow.GetLineNumberWindow().Invalidate();
1223 OUString EditorWindow::GetActualSubName( sal_uInt32 nLine )
1225 SbxArrayRef pMethods = rModulWindow.GetSbModule()->GetMethods();
1226 for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
1228 SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i));
1229 if( pMeth )
1231 sal_uInt16 l1,l2;
1232 pMeth->GetLineRange(l1,l2);
1233 if( (l1 <= nLine+1) && (nLine+1 <= l2) )
1235 return pMeth->GetName();
1239 return OUString();
1242 void EditorWindow::SetScrollBarRanges()
1244 // extra method, not InitScrollBars, because for EditEngine events too
1245 if ( !pEditEngine )
1246 return;
1248 rModulWindow.GetEditVScrollBar().SetRange( Range( 0, pEditEngine->GetTextHeight()-1 ) );
1249 rModulWindow.GetEditHScrollBar().SetRange( Range( 0, nCurTextWidth-1 ) );
1252 void EditorWindow::InitScrollBars()
1254 if (!pEditEngine)
1255 return;
1257 SetScrollBarRanges();
1258 Size aOutSz(GetOutputSizePixel());
1259 rModulWindow.GetEditVScrollBar().SetVisibleSize(aOutSz.Height());
1260 rModulWindow.GetEditVScrollBar().SetPageSize(aOutSz.Height() * 8 / 10);
1261 rModulWindow.GetEditVScrollBar().SetLineSize(GetTextHeight());
1262 rModulWindow.GetEditVScrollBar().SetThumbPos(pEditView->GetStartDocPos().Y());
1263 rModulWindow.GetEditVScrollBar().Show();
1265 rModulWindow.GetEditHScrollBar().SetVisibleSize(aOutSz.Width());
1266 rModulWindow.GetEditHScrollBar().SetPageSize(aOutSz.Width() * 8 / 10);
1267 rModulWindow.GetEditHScrollBar().SetLineSize(GetTextWidth( u"x"_ustr ));
1268 rModulWindow.GetEditHScrollBar().SetThumbPos(pEditView->GetStartDocPos().X());
1269 rModulWindow.GetEditHScrollBar().Show();
1272 void EditorWindow::ImpDoHighlight( sal_uInt32 nLine )
1274 if ( !bDoSyntaxHighlight )
1275 return;
1277 OUString aLine( pEditEngine->GetText( nLine ) );
1278 bool const bWasModified = pEditEngine->IsModified();
1279 pEditEngine->RemoveAttribs( nLine );
1280 std::vector<HighlightPortion> aPortions;
1281 aHighlighter.getHighlightPortions( aLine, aPortions );
1283 for (auto const& portion : aPortions)
1285 Color const aColor = rModulWindow.GetLayout().GetSyntaxColor(portion.tokenType);
1286 pEditEngine->SetAttrib(TextAttribFontColor(aColor), nLine, portion.nBegin, portion.nEnd);
1289 pEditEngine->SetModified(bWasModified);
1292 void EditorWindow::ChangeFontColor( Color aColor )
1294 if (pEditEngine)
1296 vcl::Font aFont(pEditEngine->GetFont());
1297 aFont.SetColor(aColor);
1298 pEditEngine->SetFont(aFont);
1302 void EditorWindow::UpdateSyntaxHighlighting ()
1304 if (pEditEngine)
1306 const sal_uInt32 nCount = pEditEngine->GetParagraphCount();
1307 for (sal_uInt32 i = 0; i < nCount; ++i)
1308 DoDelayedSyntaxHighlight(i);
1312 void EditorWindow::ImplSetFont()
1314 // Get default font name and height defined in the Options dialog
1315 OUString sFontName(officecfg::Office::Common::Font::SourceViewFont::FontName::get().value_or(OUString()));
1316 if (sFontName.isEmpty())
1318 vcl::Font aTmpFont(OutputDevice::GetDefaultFont(DefaultFontType::FIXED,
1319 Application::GetSettings().GetUILanguageTag().getLanguageType(),
1320 GetDefaultFontFlags::NONE, GetOutDev()));
1321 sFontName = aTmpFont.GetFamilyName();
1323 sal_uInt16 nDefaultFontHeight = officecfg::Office::Common::Font::SourceViewFont::FontHeight::get();
1325 // Calculate font size considering zoom level
1326 sal_uInt16 nNewFontHeight = nDefaultFontHeight * (static_cast<float>(nCurrentZoomLevel) / 100);
1327 Size aFontSize(0, nNewFontHeight);
1329 vcl::Font aFont(sFontName, aFontSize);
1330 aFont.SetColor(rModulWindow.GetLayout().GetFontColor());
1331 SetPointFont(*GetOutDev(), aFont); // FIXME RenderContext
1332 aFont = GetFont();
1334 rModulWindow.GetBreakPointWindow().SetFont(aFont);
1335 rModulWindow.GetLineNumberWindow().SetFont(aFont);
1336 rModulWindow.Invalidate();
1338 if (pEditEngine)
1340 bool const bModified = pEditEngine->IsModified();
1341 pEditEngine->SetFont(aFont);
1342 pEditEngine->SetModified(bModified);
1345 // Update controls
1346 if (SfxBindings* pBindings = GetBindingsPtr())
1348 pBindings->Invalidate( SID_BASICIDE_CURRENT_ZOOM );
1349 pBindings->Invalidate( SID_ATTR_ZOOMSLIDER );
1353 void EditorWindow::SetEditorZoomLevel(sal_uInt16 nNewZoomLevel)
1355 if (nCurrentZoomLevel == nNewZoomLevel)
1356 return;
1358 if (nNewZoomLevel < MIN_ZOOM_LEVEL || nNewZoomLevel > MAX_ZOOM_LEVEL)
1359 return;
1361 nCurrentZoomLevel = nNewZoomLevel;
1362 ImplSetFont();
1365 void EditorWindow::DoSyntaxHighlight( sal_uInt32 nPara )
1367 // because of the DelayedSyntaxHighlight it's possible
1368 // that this line does not exist anymore!
1369 if ( nPara < pEditEngine->GetParagraphCount() )
1371 // unfortunately I'm not sure that exactly this line does Modified()...
1372 if ( pProgress )
1373 pProgress->StepProgress();
1374 ImpDoHighlight( nPara );
1378 void EditorWindow::DoDelayedSyntaxHighlight( sal_uInt32 nPara )
1380 // line is only added to list, processed in TimerHdl
1381 // => don't manipulate breaks while EditEngine is formatting
1382 if ( pProgress )
1383 pProgress->StepProgress();
1385 if ( !bHighlighting && bDoSyntaxHighlight )
1387 if ( bDelayHighlight )
1389 aSyntaxLineTable.insert( nPara );
1390 aSyntaxIdle.Start();
1392 else
1393 DoSyntaxHighlight( nPara );
1397 IMPL_LINK_NOARG(EditorWindow, SyntaxTimerHdl, Timer *, void)
1399 DBG_ASSERT( pEditView, "Not yet a View, but Syntax-Highlight?!" );
1401 bool const bWasModified = pEditEngine->IsModified();
1402 //pEditEngine->SetUpdateMode(false);
1404 bHighlighting = true;
1405 for (auto const& syntaxLine : aSyntaxLineTable)
1407 DoSyntaxHighlight(syntaxLine);
1410 // #i45572#
1411 if ( pEditView )
1412 pEditView->ShowCursor( false );
1414 pEditEngine->SetModified( bWasModified );
1416 aSyntaxLineTable.clear();
1417 bHighlighting = false;
1420 void EditorWindow::ParagraphInsertedDeleted( sal_uInt32 nPara, bool bInserted )
1422 if ( pProgress )
1423 pProgress->StepProgress();
1425 if ( !bInserted && ( nPara == TEXT_PARA_ALL ) )
1427 rModulWindow.GetBreakPoints().reset();
1428 rModulWindow.GetBreakPointWindow().Invalidate();
1429 rModulWindow.GetLineNumberWindow().Invalidate();
1431 else
1433 rModulWindow.GetBreakPoints().AdjustBreakPoints( static_cast<sal_uInt16>(nPara)+1, bInserted );
1435 tools::Long nLineHeight = GetTextHeight();
1436 Size aSz = rModulWindow.GetBreakPointWindow().GetOutDev()->GetOutputSize();
1437 tools::Rectangle aInvRect( Point( 0, 0 ), aSz );
1438 tools::Long nY = nPara*nLineHeight - rModulWindow.GetBreakPointWindow().GetCurYOffset();
1439 aInvRect.SetTop( nY );
1440 rModulWindow.GetBreakPointWindow().Invalidate( aInvRect );
1442 Size aLnSz(rModulWindow.GetLineNumberWindow().GetWidth(),
1443 GetOutputSizePixel().Height() - 2 * DWBORDER);
1444 rModulWindow.GetLineNumberWindow().SetPosSizePixel(Point(DWBORDER + 19, DWBORDER), aLnSz);
1445 rModulWindow.GetLineNumberWindow().Invalidate();
1449 void EditorWindow::CreateProgress( const OUString& rText, sal_uInt32 nRange )
1451 DBG_ASSERT( !pProgress, "ProgressInfo exists already" );
1452 pProgress.reset(new ProgressInfo(
1453 GetShell()->GetViewFrame().GetObjectShell(),
1454 rText,
1455 nRange
1459 void EditorWindow::DestroyProgress()
1461 pProgress.reset();
1464 void EditorWindow::ForceSyntaxTimeout()
1466 aSyntaxIdle.Stop();
1467 aSyntaxIdle.Invoke();
1470 FactoryFunction EditorWindow::GetUITestFactory() const
1472 return EditorWindowUIObject::create;
1476 // BreakPointWindow
1478 BreakPointWindow::BreakPointWindow (vcl::Window* pParent, ModulWindow* pModulWindow)
1479 : Window(pParent, WB_BORDER)
1480 , rModulWindow(*pModulWindow)
1481 , nCurYOffset(0) // memorize nCurYOffset and not take it from EditEngine
1482 , nMarkerPos(NoMarker)
1483 , bErrorMarker(false)
1485 setBackgroundColor(GetSettings().GetStyleSettings().GetFieldColor());
1486 SetHelpId(HID_BASICIDE_BREAKPOINTWINDOW);
1489 void BreakPointWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1491 if (SyncYOffset())
1492 return;
1494 Size const aOutSz = rRenderContext.GetOutputSize();
1495 tools::Long const nLineHeight = rRenderContext.GetTextHeight();
1497 Image const aBrk[2] =
1499 GetImage(RID_BMP_BRKDISABLED),
1500 GetImage(RID_BMP_BRKENABLED)
1503 Size const aBmpSz = rRenderContext.PixelToLogic(aBrk[1].GetSizePixel());
1504 Point const aBmpOff((aOutSz.Width() - aBmpSz.Width()) / 2,
1505 (nLineHeight - aBmpSz.Height()) / 2);
1507 for (size_t i = 0, n = GetBreakPoints().size(); i < n; ++i)
1509 BreakPoint& rBrk = GetBreakPoints().at(i);
1510 sal_uInt16 const nLine = rBrk.nLine - 1;
1511 size_t const nY = nLine*nLineHeight - nCurYOffset;
1512 rRenderContext.DrawImage(Point(0, nY) + aBmpOff, aBrk[rBrk.bEnabled]);
1515 ShowMarker(rRenderContext);
1518 void BreakPointWindow::ShowMarker(vcl::RenderContext& rRenderContext)
1520 if (nMarkerPos == NoMarker)
1521 return;
1523 Size const aOutSz = GetOutDev()->GetOutputSize();
1524 tools::Long const nLineHeight = GetTextHeight();
1526 Image aMarker = GetImage(bErrorMarker ? RID_BMP_ERRORMARKER : RID_BMP_STEPMARKER);
1528 Size aMarkerSz(aMarker.GetSizePixel());
1529 aMarkerSz = rRenderContext.PixelToLogic(aMarkerSz);
1530 Point aMarkerOff(0, 0);
1531 aMarkerOff.setX( (aOutSz.Width() - aMarkerSz.Width()) / 2 );
1532 aMarkerOff.setY( (nLineHeight - aMarkerSz.Height()) / 2 );
1534 tools::Long nY = nMarkerPos * nLineHeight - nCurYOffset;
1535 Point aPos(0, nY);
1536 aPos += aMarkerOff;
1538 rRenderContext.DrawImage(aPos, aMarker);
1541 void BreakPointWindow::DoScroll( tools::Long nVertScroll )
1543 nCurYOffset -= nVertScroll;
1544 Window::Scroll( 0, nVertScroll );
1547 void BreakPointWindow::SetMarkerPos( sal_uInt16 nLine, bool bError )
1549 if ( SyncYOffset() )
1550 PaintImmediately();
1552 nMarkerPos = nLine;
1553 bErrorMarker = bError;
1554 Invalidate();
1557 void BreakPointWindow::SetNoMarker ()
1559 SetMarkerPos(NoMarker);
1562 BreakPoint* BreakPointWindow::FindBreakPoint( const Point& rMousePos )
1564 size_t nLineHeight = GetTextHeight();
1565 nLineHeight = nLineHeight > 0 ? nLineHeight : 1;
1566 size_t nYPos = rMousePos.Y() + nCurYOffset;
1568 for ( size_t i = 0, n = GetBreakPoints().size(); i < n ; ++i )
1570 BreakPoint& rBrk = GetBreakPoints().at( i );
1571 sal_uInt16 nLine = rBrk.nLine-1;
1572 size_t nY = nLine*nLineHeight;
1573 if ( ( nYPos > nY ) && ( nYPos < ( nY + nLineHeight ) ) )
1574 return &rBrk;
1576 return nullptr;
1579 void BreakPointWindow::MouseButtonDown( const MouseEvent& rMEvt )
1581 if ( rMEvt.GetClicks() == 2 )
1583 Point aMousePos( PixelToLogic( rMEvt.GetPosPixel() ) );
1584 tools::Long nLineHeight = GetTextHeight();
1585 if(nLineHeight)
1587 tools::Long nYPos = aMousePos.Y() + nCurYOffset;
1588 tools::Long nLine = nYPos / nLineHeight + 1;
1589 rModulWindow.ToggleBreakPoint( static_cast<sal_uInt16>(nLine) );
1590 Invalidate();
1595 void BreakPointWindow::Command( const CommandEvent& rCEvt )
1597 if ( rCEvt.GetCommand() != CommandEventId::ContextMenu )
1598 return;
1600 Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) );
1601 tools::Rectangle aRect(aPos, Size(1, 1));
1602 weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
1604 std::unique_ptr<weld::Builder> xUIBuilder(Application::CreateBuilder(pPopupParent, u"modules/BasicIDE/ui/breakpointmenus.ui"_ustr));
1606 Point aEventPos( PixelToLogic( aPos ) );
1607 BreakPoint* pBrk = rCEvt.IsMouseEvent() ? FindBreakPoint( aEventPos ) : nullptr;
1608 if ( pBrk )
1610 // test if break point is enabled...
1611 std::unique_ptr<weld::Menu> xBrkPropMenu = xUIBuilder->weld_menu(u"breakmenu"_ustr);
1612 xBrkPropMenu->set_active(u"active"_ustr, pBrk->bEnabled);
1613 OUString sCommand = xBrkPropMenu->popup_at_rect(pPopupParent, aRect);
1614 if (sCommand == "active")
1616 pBrk->bEnabled = !pBrk->bEnabled;
1617 rModulWindow.UpdateBreakPoint( *pBrk );
1618 Invalidate();
1620 else if (sCommand == "properties")
1622 BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1623 aBrkDlg.SetCurrentBreakPoint( *pBrk );
1624 aBrkDlg.run();
1625 Invalidate();
1628 else
1630 std::unique_ptr<weld::Menu> xBrkListMenu = xUIBuilder->weld_menu(u"breaklistmenu"_ustr);
1631 OUString sCommand = xBrkListMenu->popup_at_rect(pPopupParent, aRect);
1632 if (sCommand == "manage")
1634 BreakPointDialog aBrkDlg(pPopupParent, GetBreakPoints());
1635 aBrkDlg.run();
1636 Invalidate();
1641 bool BreakPointWindow::SyncYOffset()
1643 TextView* pView = rModulWindow.GetEditView();
1644 if ( pView )
1646 tools::Long nViewYOffset = pView->GetStartDocPos().Y();
1647 if ( nCurYOffset != nViewYOffset )
1649 nCurYOffset = nViewYOffset;
1650 Invalidate();
1651 return true;
1654 return false;
1657 // virtual
1658 void BreakPointWindow::DataChanged(DataChangedEvent const & rDCEvt)
1660 Window::DataChanged(rDCEvt);
1661 if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
1662 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
1664 Color aColor(GetSettings().GetStyleSettings().GetFieldColor());
1665 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
1666 if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFieldColor())
1668 setBackgroundColor(aColor);
1669 Invalidate();
1674 void BreakPointWindow::setBackgroundColor(Color aColor)
1676 SetBackground(Wallpaper(aColor));
1679 namespace {
1681 struct WatchItem
1683 OUString maName;
1684 OUString maDisplayName;
1685 SbxObjectRef mpObject;
1686 std::vector<OUString> maMemberList;
1688 SbxDimArrayRef mpArray;
1689 int nDimLevel; // 0 = Root
1690 int nDimCount;
1691 std::vector<sal_Int32> vIndices;
1693 WatchItem* mpArrayParentItem;
1695 explicit WatchItem (OUString aName):
1696 maName(std::move(aName)),
1697 nDimLevel(0),
1698 nDimCount(0),
1699 mpArrayParentItem(nullptr)
1702 void clearWatchItem ()
1704 maMemberList.clear();
1707 WatchItem* GetRootItem();
1708 SbxDimArray* GetRootArray();
1713 WatchWindow::WatchWindow(Layout* pParent)
1714 : DockingWindow(pParent, u"modules/BasicIDE/ui/dockingwatch.ui"_ustr, u"DockingWatch"_ustr)
1715 , m_nUpdateWatchesId(nullptr)
1717 m_xTitleArea = m_xBuilder->weld_container(u"titlearea"_ustr);
1719 nVirtToolBoxHeight = m_xTitleArea->get_preferred_size().Height();
1721 m_xTitle = m_xBuilder->weld_label(u"title"_ustr);
1722 m_xTitle->set_label(IDEResId(RID_STR_REMOVEWATCH));
1724 m_xEdit = m_xBuilder->weld_entry(u"edit"_ustr);
1725 m_xRemoveWatchButton = m_xBuilder->weld_button(u"remove"_ustr);
1726 m_xTreeListBox = m_xBuilder->weld_tree_view(u"treeview"_ustr);
1728 m_xEdit->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1729 m_xEdit->set_help_id(HID_BASICIDE_WATCHWINDOW_EDIT);
1730 m_xEdit->set_size_request(LogicToPixel(Size(80, 0), MapMode(MapUnit::MapAppFont)).Width(), -1);
1731 m_xEdit->connect_activate(LINK( this, WatchWindow, ActivateHdl));
1732 m_xEdit->connect_key_press(LINK( this, WatchWindow, KeyInputHdl));
1733 m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_WATCHNAME));
1735 m_xRemoveWatchButton->set_sensitive(false);
1736 m_xRemoveWatchButton->connect_clicked(LINK( this, WatchWindow, ButtonHdl));
1737 m_xRemoveWatchButton->set_help_id(HID_BASICIDE_REMOVEWATCH);
1738 m_xRemoveWatchButton->set_tooltip_text(IDEResId(RID_STR_REMOVEWATCHTIP));
1740 m_xTreeListBox->set_help_id(HID_BASICIDE_WATCHWINDOW_LIST);
1741 m_xTreeListBox->connect_editing(LINK(this, WatchWindow, EditingEntryHdl),
1742 LINK(this, WatchWindow, EditedEntryHdl));
1743 m_xTreeListBox->connect_selection_changed(LINK(this, WatchWindow, TreeListHdl));
1744 m_xTreeListBox->connect_expanding(LINK(this, WatchWindow, RequestingChildrenHdl));
1746 // VarTabWidth, ValueTabWidth, TypeTabWidth
1747 std::vector<int> aWidths { 220, 100, 1250 };
1748 std::vector<bool> aEditables { false, true, false };
1749 m_xTreeListBox->set_column_fixed_widths(aWidths);
1750 m_xTreeListBox->set_column_editables(aEditables);
1752 SetText(IDEResId(RID_STR_WATCHNAME));
1754 SetHelpId( HID_BASICIDE_WATCHWINDOW );
1756 // make watch window keyboard accessible
1757 GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1760 WatchWindow::~WatchWindow()
1762 disposeOnce();
1765 void WatchWindow::dispose()
1767 if (m_nUpdateWatchesId)
1769 Application::RemoveUserEvent(m_nUpdateWatchesId);
1770 m_nUpdateWatchesId = nullptr;
1773 // Destroy user data
1774 m_xTreeListBox->all_foreach([this](weld::TreeIter& rEntry){
1775 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
1776 delete pItem;
1777 return false;
1780 m_xTitle.reset();
1781 m_xEdit.reset();
1782 m_xRemoveWatchButton.reset();
1783 m_xTitleArea.reset();
1784 m_xTreeListBox.reset();
1785 GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1786 DockingWindow::dispose();
1789 void WatchWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1791 lcl_DrawIDEWindowFrame(this, rRenderContext);
1794 void WatchWindow::Resize()
1796 Size aSz = GetOutputSizePixel();
1797 Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1799 if ( aBoxSz.Width() < 4 )
1800 aBoxSz.setWidth( 0 );
1801 if ( aBoxSz.Height() < 4 )
1802 aBoxSz.setHeight( 0 );
1804 m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1806 Invalidate();
1809 WatchItem* WatchItem::GetRootItem()
1811 WatchItem* pItem = mpArrayParentItem;
1812 while( pItem )
1814 if( pItem->mpArray.is() )
1815 break;
1816 pItem = pItem->mpArrayParentItem;
1818 return pItem;
1821 SbxDimArray* WatchItem::GetRootArray()
1823 WatchItem* pRootItem = GetRootItem();
1824 SbxDimArray* pRet = nullptr;
1825 if( pRootItem )
1826 pRet = pRootItem->mpArray.get();
1827 return pRet;
1830 void WatchWindow::AddWatch( const OUString& rVName )
1832 OUString aVar, aIndex;
1833 lcl_SeparateNameAndIndex( rVName, aVar, aIndex );
1834 WatchItem* pWatchItem = new WatchItem(aVar);
1836 OUString sId(weld::toId(pWatchItem));
1837 std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
1838 m_xTreeListBox->insert(nullptr, -1, &aVar, &sId, nullptr, nullptr, false, xRet.get());
1839 m_xTreeListBox->set_text(*xRet, u""_ustr, 1);
1840 m_xTreeListBox->set_text(*xRet, u""_ustr, 2);
1842 m_xTreeListBox->set_cursor(*xRet);
1843 m_xTreeListBox->select(*xRet);
1844 m_xTreeListBox->scroll_to_row(*xRet);
1845 m_xRemoveWatchButton->set_sensitive(true);
1847 UpdateWatches(false);
1850 void WatchWindow::RemoveSelectedWatch()
1852 std::unique_ptr<weld::TreeIter> xEntry = m_xTreeListBox->make_iterator();
1853 bool bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1854 if (bEntry)
1856 m_xTreeListBox->remove(*xEntry);
1857 bEntry = m_xTreeListBox->get_cursor(xEntry.get());
1858 if (bEntry)
1859 m_xEdit->set_text(weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xEntry))->maName);
1860 else
1861 m_xEdit->set_text(OUString());
1862 if ( !m_xTreeListBox->n_children() )
1863 m_xRemoveWatchButton->set_sensitive(false);
1867 IMPL_STATIC_LINK_NOARG(WatchWindow, ButtonHdl, weld::Button&, void)
1869 if (SfxDispatcher* pDispatcher = GetDispatcher())
1870 pDispatcher->Execute(SID_BASICIDE_REMOVEWATCH);
1873 IMPL_LINK_NOARG(WatchWindow, TreeListHdl, weld::TreeView&, void)
1875 std::unique_ptr<weld::TreeIter> xCurEntry = m_xTreeListBox->make_iterator();
1876 bool bCurEntry = m_xTreeListBox->get_cursor(xCurEntry.get());
1877 if (!bCurEntry)
1878 return;
1879 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xCurEntry));
1880 if (!pItem)
1881 return;
1882 m_xEdit->set_text(pItem->maName);
1885 IMPL_LINK_NOARG(WatchWindow, ActivateHdl, weld::Entry&, bool)
1887 OUString aCurText(m_xEdit->get_text());
1888 if (!aCurText.isEmpty())
1890 AddWatch(aCurText);
1891 m_xEdit->select_region(0, -1);
1893 return true;
1896 IMPL_LINK(WatchWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
1898 bool bHandled = false;
1900 sal_uInt16 nKeyCode = rKEvt.GetKeyCode().GetCode();
1901 if (nKeyCode == KEY_ESCAPE)
1903 m_xEdit->set_text(OUString());
1904 bHandled = true;
1907 return bHandled;
1910 // StackWindow
1911 StackWindow::StackWindow(Layout* pParent)
1912 : DockingWindow(pParent, u"modules/BasicIDE/ui/dockingstack.ui"_ustr, u"DockingStack"_ustr)
1914 m_xTitle = m_xBuilder->weld_label(u"title"_ustr);
1915 m_xTitle->set_label(IDEResId(RID_STR_STACK));
1917 m_xTitle->set_size_request(-1, nVirtToolBoxHeight); // so the two title areas are the same height
1919 m_xTreeListBox = m_xBuilder->weld_tree_view(u"stack"_ustr);
1921 m_xTreeListBox->set_help_id(HID_BASICIDE_STACKWINDOW_LIST);
1922 m_xTreeListBox->set_accessible_name(IDEResId(RID_STR_STACKNAME));
1923 m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
1924 m_xTreeListBox->append_text(OUString());
1926 SetText(IDEResId(RID_STR_STACKNAME));
1928 SetHelpId( HID_BASICIDE_STACKWINDOW );
1930 // make stack window keyboard accessible
1931 GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
1934 StackWindow::~StackWindow()
1936 disposeOnce();
1939 void StackWindow::dispose()
1941 GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
1942 m_xTitle.reset();
1943 m_xTreeListBox.reset();
1944 DockingWindow::dispose();
1947 void StackWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
1949 lcl_DrawIDEWindowFrame(this, rRenderContext);
1952 void StackWindow::Resize()
1954 Size aSz = GetOutputSizePixel();
1955 Size aBoxSz(aSz.Width() - 2*DWBORDER, aSz.Height() - 2*DWBORDER);
1957 if ( aBoxSz.Width() < 4 )
1958 aBoxSz.setWidth( 0 );
1959 if ( aBoxSz.Height() < 4 )
1960 aBoxSz.setHeight( 0 );
1962 m_xBox->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBoxSz);
1964 Invalidate();
1967 void StackWindow::UpdateCalls()
1969 m_xTreeListBox->freeze();
1970 m_xTreeListBox->clear();
1972 if (StarBASIC::IsRunning())
1974 ErrCode eOld = SbxBase::GetError();
1975 m_xTreeListBox->set_selection_mode(SelectionMode::Single);
1977 sal_Int32 nScope = 0;
1978 SbMethod* pMethod = StarBASIC::GetActiveMethod( nScope );
1979 while ( pMethod )
1981 OUStringBuffer aEntry( OUString::number(nScope ));
1982 if ( aEntry.getLength() < 2 )
1983 aEntry.insert(0, " ");
1984 aEntry.append(": " + pMethod->GetName());
1985 SbxArray* pParams = pMethod->GetParameters();
1986 SbxInfo* pInfo = pMethod->GetInfo();
1987 if ( pParams )
1989 aEntry.append("(");
1990 // 0 is the sub's name...
1991 for (sal_uInt32 nParam = 1; nParam < pParams->Count(); nParam++)
1993 SbxVariable* pVar = pParams->Get(nParam);
1994 assert(pVar && "Parameter?!");
1995 if ( !pVar->GetName().isEmpty() )
1997 aEntry.append(pVar->GetName());
1999 else if ( pInfo )
2001 assert(nParam <= std::numeric_limits<sal_uInt16>::max());
2002 const SbxParamInfo* pParam = pInfo->GetParam( sal::static_int_cast<sal_uInt16>(nParam) );
2003 if ( pParam )
2005 aEntry.append(pParam->aName);
2008 aEntry.append("=");
2009 SbxDataType eType = pVar->GetType();
2010 if( eType & SbxARRAY )
2012 aEntry.append("...");
2014 else if( eType != SbxOBJECT )
2016 aEntry.append(pVar->GetOUString());
2018 if (nParam < (pParams->Count() - 1))
2020 aEntry.append(", ");
2023 aEntry.append(")");
2025 m_xTreeListBox->append_text(aEntry.makeStringAndClear());
2026 nScope++;
2027 pMethod = StarBASIC::GetActiveMethod( nScope );
2030 SbxBase::ResetError();
2031 if( eOld != ERRCODE_NONE )
2032 SbxBase::SetError( eOld );
2034 else
2036 m_xTreeListBox->set_selection_mode(SelectionMode::NONE);
2037 m_xTreeListBox->append_text(OUString());
2040 m_xTreeListBox->thaw();
2043 ComplexEditorWindow::ComplexEditorWindow( ModulWindow* pParent ) :
2044 Window( pParent, WB_3DLOOK | WB_CLIPCHILDREN ),
2045 aBrkWindow(VclPtr<BreakPointWindow>::Create(this, pParent)),
2046 aLineNumberWindow(VclPtr<LineNumberWindow>::Create(this, pParent)),
2047 aEdtWindow(VclPtr<EditorWindow>::Create(this, pParent)),
2048 aEWVScrollBar(VclPtr<ScrollAdaptor>::Create(this, false)),
2049 aEWHScrollBar(VclPtr<ScrollAdaptor>::Create(this, true))
2051 // tdf#153853 The line numbering and breakpoint windows should appear on
2052 // the left, even on RTL locales
2053 EnableRTL(false);
2055 aEdtWindow->Show();
2056 aBrkWindow->Show();
2058 aEWVScrollBar->SetLineSize(nScrollLine);
2059 aEWVScrollBar->SetPageSize(nScrollPage);
2060 aEWVScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2061 aEWVScrollBar->Show();
2063 aEWHScrollBar->SetLineSize(nScrollLine);
2064 aEWHScrollBar->SetPageSize(nScrollPage);
2065 aEWHScrollBar->SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
2066 aEWHScrollBar->Show();
2069 ComplexEditorWindow::~ComplexEditorWindow()
2071 disposeOnce();
2074 void ComplexEditorWindow::dispose()
2076 aBrkWindow.disposeAndClear();
2077 aLineNumberWindow.disposeAndClear();
2078 aEdtWindow.disposeAndClear();
2079 aEWVScrollBar.disposeAndClear();
2080 aEWHScrollBar.disposeAndClear();
2081 vcl::Window::dispose();
2084 void ComplexEditorWindow::Resize()
2086 Size aOutSz = GetOutputSizePixel();
2087 Size aSz(aOutSz);
2088 aSz.AdjustWidth( -(2*DWBORDER) );
2089 aSz.AdjustHeight( -(2*DWBORDER) );
2090 tools::Long nBrkWidth = 20;
2091 tools::Long nSBWidth = aEWVScrollBar->GetSizePixel().Width();
2092 tools::Long nSBHeight = aEWHScrollBar->GetSizePixel().Height();
2094 Size aBrkSz(nBrkWidth, aSz.Height() - nSBHeight);
2096 if (aLineNumberWindow->IsVisible())
2098 Size aLnSz(aLineNumberWindow->GetWidth(), aSz.Height() - nSBHeight);
2099 Size aEWSz(aSz.Width() - nBrkWidth - aLineNumberWindow->GetWidth() - nSBWidth, aSz.Height() - nSBHeight);
2100 aBrkWindow->SetPosSizePixel(Point(DWBORDER, DWBORDER), aBrkSz);
2101 aLineNumberWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aLnSz);
2102 aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth + aLnSz.Width(), DWBORDER), aEWSz);
2104 else
2106 Size aEWSz(aSz.Width() - nBrkWidth - nSBWidth, aSz.Height() - nSBHeight);
2107 aBrkWindow->SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz );
2108 aEdtWindow->SetPosSizePixel(Point(DWBORDER + nBrkWidth, DWBORDER), aEWSz);
2111 aEWVScrollBar->SetPosSizePixel(Point(aOutSz.Width() - DWBORDER - nSBWidth, DWBORDER),
2112 Size(nSBWidth, aSz.Height() - nSBHeight));
2113 aEWHScrollBar->SetPosSizePixel(Point(DWBORDER, aOutSz.Height() - DWBORDER - nSBHeight),
2114 Size(aSz.Width() - nSBWidth, nSBHeight));
2117 IMPL_LINK_NOARG(ComplexEditorWindow, ScrollHdl, weld::Scrollbar&, void)
2119 if (aEdtWindow->GetEditView())
2121 tools::Long nXDiff = aEdtWindow->GetEditView()->GetStartDocPos().X() - aEWHScrollBar->GetThumbPos();
2122 tools::Long nYDiff = aEdtWindow->GetEditView()->GetStartDocPos().Y() - aEWVScrollBar->GetThumbPos();
2123 aEdtWindow->GetEditView()->Scroll(nXDiff, nYDiff);
2124 aBrkWindow->DoScroll( nYDiff );
2125 aLineNumberWindow->DoScroll( nYDiff );
2126 aEdtWindow->GetEditView()->ShowCursor(false);
2127 aEWVScrollBar->SetThumbPos( aEdtWindow->GetEditView()->GetStartDocPos().Y() );
2131 void ComplexEditorWindow::DataChanged(DataChangedEvent const & rDCEvt)
2133 Window::DataChanged(rDCEvt);
2134 if (rDCEvt.GetType() == DataChangedEventType::SETTINGS
2135 && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
2137 Color aColor(GetSettings().GetStyleSettings().GetFaceColor());
2138 const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
2139 if (!pOldSettings || aColor != pOldSettings->GetStyleSettings().GetFaceColor())
2141 SetBackground(Wallpaper(aColor));
2142 Invalidate();
2147 void ComplexEditorWindow::SetLineNumberDisplay(bool b)
2149 aLineNumberWindow->Show(b);
2150 Resize();
2153 uno::Reference< awt::XVclWindowPeer >
2154 EditorWindow::GetComponentInterface(bool bCreate)
2156 uno::Reference< awt::XVclWindowPeer > xPeer(
2157 Window::GetComponentInterface(false));
2158 if (!xPeer.is() && bCreate)
2160 // Make sure edit engine and view are available:
2161 if (!pEditEngine)
2162 CreateEditEngine();
2164 xPeer = createTextWindowPeer(*GetEditView());
2165 SetComponentInterface(xPeer);
2167 return xPeer;
2170 static sal_uInt32 getCorrectedPropCount(SbxArray* p)
2172 sal_uInt32 nPropCount = p->Count();
2173 if (nPropCount >= 3 && p->Get(nPropCount - 1)->GetName().equalsIgnoreAsciiCase("Dbg_Methods")
2174 && p->Get(nPropCount - 2)->GetName().equalsIgnoreAsciiCase("Dbg_Properties")
2175 && p->Get(nPropCount - 3)->GetName().equalsIgnoreAsciiCase("Dbg_SupportedInterfaces"))
2177 nPropCount -= 3;
2179 return nPropCount;
2182 IMPL_LINK(WatchWindow, RequestingChildrenHdl, const weld::TreeIter&, rParent, bool)
2184 if( !StarBASIC::IsRunning() )
2185 return true;
2187 if (m_xTreeListBox->iter_has_child(rParent))
2188 return true;
2190 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rParent));
2191 std::unique_ptr<weld::TreeIter> xRet = m_xTreeListBox->make_iterator();
2193 SbxDimArray* pArray = pItem->mpArray.get();
2194 SbxDimArray* pRootArray = pItem->GetRootArray();
2195 bool bArrayIsRootArray = false;
2196 if( !pArray && pRootArray )
2198 pArray = pRootArray;
2199 bArrayIsRootArray = true;
2202 SbxObject* pObj = pItem->mpObject.get();
2203 if( pObj )
2205 createAllObjectProperties( pObj );
2206 SbxArray* pProps = pObj->GetProperties();
2207 const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2208 pItem->maMemberList.reserve(nPropCount);
2210 for( sal_uInt32 i = 0 ; i < nPropCount ; ++i )
2212 SbxVariable* pVar = pProps->Get(i);
2214 pItem->maMemberList.push_back(pVar->GetName());
2215 OUString const& rName = pItem->maMemberList.back();
2217 WatchItem* pWatchItem = new WatchItem(rName);
2218 OUString sId(weld::toId(pWatchItem));
2220 m_xTreeListBox->insert(&rParent, -1, &rName, &sId, nullptr, nullptr, false, xRet.get());
2221 m_xTreeListBox->set_text(*xRet, u""_ustr, 1);
2222 m_xTreeListBox->set_text(*xRet, u""_ustr, 2);
2225 if (nPropCount > 0 && !m_nUpdateWatchesId)
2227 m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2230 else if( pArray )
2232 sal_uInt16 nElementCount = 0;
2234 // Loop through indices of current level
2235 int nParentLevel = bArrayIsRootArray ? pItem->nDimLevel : 0;
2236 int nThisLevel = nParentLevel + 1;
2237 sal_Int32 nMin, nMax;
2238 if (pArray->GetDim(nThisLevel, nMin, nMax))
2240 for (sal_Int32 i = nMin; i <= nMax; i++)
2242 WatchItem* pChildItem = new WatchItem(pItem->maName);
2244 // Copy data and create name
2246 OUStringBuffer aIndexStr = "(";
2247 pChildItem->mpArrayParentItem = pItem;
2248 pChildItem->nDimLevel = nThisLevel;
2249 pChildItem->nDimCount = pItem->nDimCount;
2250 pChildItem->vIndices.resize(pChildItem->nDimCount);
2251 sal_Int32 j;
2252 for (j = 0; j < nParentLevel; j++)
2254 sal_Int32 n = pChildItem->vIndices[j] = pItem->vIndices[j];
2255 aIndexStr.append( OUString::number(n) + "," );
2257 pChildItem->vIndices[nParentLevel] = i;
2258 aIndexStr.append( OUString::number(i) + ")" );
2260 OUString aDisplayName;
2261 WatchItem* pArrayRootItem = pChildItem->GetRootItem();
2262 if (pArrayRootItem && pArrayRootItem->mpArrayParentItem)
2263 aDisplayName = pItem->maDisplayName;
2264 else
2265 aDisplayName = pItem->maName;
2266 aDisplayName += aIndexStr;
2267 pChildItem->maDisplayName = aDisplayName;
2269 OUString sId(weld::toId(pChildItem));
2271 m_xTreeListBox->insert(&rParent, -1, &aDisplayName, &sId, nullptr, nullptr, false,
2272 xRet.get());
2273 m_xTreeListBox->set_text(*xRet, u""_ustr, 1);
2274 m_xTreeListBox->set_text(*xRet, u""_ustr, 2);
2276 nElementCount++;
2279 if (nElementCount > 0 && !m_nUpdateWatchesId)
2281 m_nUpdateWatchesId = Application::PostUserEvent(LINK(this, WatchWindow, ExecuteUpdateWatches));
2285 return true;
2288 IMPL_LINK_NOARG(WatchWindow, ExecuteUpdateWatches, void*, void)
2290 m_nUpdateWatchesId = nullptr;
2291 UpdateWatches();
2294 SbxBase* WatchWindow::ImplGetSBXForEntry(const weld::TreeIter& rEntry, bool& rbArrayElement)
2296 SbxBase* pSBX = nullptr;
2297 rbArrayElement = false;
2299 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2300 OUString aVName( pItem->maName );
2302 std::unique_ptr<weld::TreeIter> xParentEntry = m_xTreeListBox->make_iterator(&rEntry);
2303 bool bParentEntry = m_xTreeListBox->iter_parent(*xParentEntry);
2304 WatchItem* pParentItem = bParentEntry ? weld::fromId<WatchItem*>(m_xTreeListBox->get_id(*xParentEntry)) : nullptr;
2305 if( pParentItem )
2307 SbxObject* pObj = pParentItem->mpObject.get();
2308 SbxDimArray* pArray;
2309 if( pObj )
2311 pSBX = pObj->Find( aVName, SbxClassType::DontCare );
2312 if (SbxVariable const* pVar = IsSbxVariable(pSBX))
2314 // Force getting value
2315 SbxValues aRes;
2316 aRes.eType = SbxVOID;
2317 if (!isVeryLargeUnoProperty(pVar))
2318 pVar->Get( aRes );
2319 else
2321 aRes.eType = SbxSTRING;
2322 aRes.pOUString = new OUString("<" + IDEResId(RID_VARIABLE_TOO_LARGE_TO_DISPLAY) + ">");
2326 // Array?
2327 else if( (pArray = pItem->GetRootArray()) != nullptr )
2329 rbArrayElement = true;
2330 if( pParentItem->nDimLevel + 1 == pParentItem->nDimCount )
2331 pSBX = pArray->Get(pItem->vIndices.empty() ? nullptr : &*pItem->vIndices.begin());
2334 else
2336 pSBX = StarBASIC::FindSBXInCurrentScope( aVName );
2338 return pSBX;
2341 IMPL_LINK(WatchWindow, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
2343 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rIter));
2345 bool bEdit = false;
2346 if (StarBASIC::IsRunning() && StarBASIC::GetActiveMethod() && !SbxBase::IsError())
2348 // No out of scope entries
2349 bool bArrayElement;
2350 SbxBase* pSbx = ImplGetSBXForEntry(rIter, bArrayElement);
2351 if (IsSbxVariable(pSbx) || bArrayElement)
2353 // Accept no objects and only end nodes of arrays for editing
2354 if( !pItem->mpObject.is() && ( !pItem->mpArray.is() || pItem->nDimLevel == pItem->nDimCount ) )
2356 aEditingRes = m_xTreeListBox->get_text(rIter, 1);
2357 aEditingRes = comphelper::string::strip(aEditingRes, ' ');
2358 bEdit = true;
2363 return bEdit;
2366 IMPL_LINK(WatchWindow, EditedEntryHdl, const IterString&, rIterString, bool)
2368 const weld::TreeIter& rIter = rIterString.first;
2369 OUString aResult = comphelper::string::strip(rIterString.second, ' ');
2371 sal_uInt16 nResultLen = aResult.getLength();
2372 sal_Unicode cFirst = aResult[0];
2373 sal_Unicode cLast = aResult[ nResultLen - 1 ];
2374 if( cFirst == '\"' && cLast == '\"' )
2375 aResult = aResult.copy( 1, nResultLen - 2 );
2377 if (aResult == aEditingRes)
2378 return false;
2380 bool bArrayElement;
2381 SbxBase* pSBX = ImplGetSBXForEntry(rIter, bArrayElement);
2383 if (SbxVariable* pVar = IsSbxVariable(pSBX))
2385 SbxDataType eType = pVar->GetType();
2386 if ( static_cast<sal_uInt8>(eType) != sal_uInt8(SbxOBJECT)
2387 && ( eType & SbxARRAY ) == 0 )
2389 // If the type is variable, the conversion of the SBX does not matter,
2390 // else the string is converted.
2391 pVar->PutStringExt( aResult );
2395 if ( SbxBase::IsError() )
2397 SbxBase::ResetError();
2400 UpdateWatches();
2402 // The text should never be taken/copied 1:1,
2403 // as the UpdateWatches will be lost
2404 return false;
2407 namespace
2410 void implCollapseModifiedObjectEntry(const weld::TreeIter& rParent, weld::TreeView& rTree)
2412 rTree.collapse_row(rParent);
2414 std::unique_ptr<weld::TreeIter> xDeleteEntry = rTree.make_iterator(&rParent);
2416 while (rTree.iter_children(*xDeleteEntry))
2418 implCollapseModifiedObjectEntry(*xDeleteEntry, rTree);
2420 WatchItem* pItem = weld::fromId<WatchItem*>(rTree.get_id(*xDeleteEntry));
2421 delete pItem;
2422 rTree.remove(*xDeleteEntry);
2423 rTree.copy_iterator(rParent, *xDeleteEntry);
2427 OUString implCreateTypeStringForDimArray( WatchItem* pItem, SbxDataType eType )
2429 OUString aRetStr = getBasicTypeName( eType );
2431 SbxDimArray* pArray = pItem->mpArray.get();
2432 if( !pArray )
2433 pArray = pItem->GetRootArray();
2434 if( pArray )
2436 int nDimLevel = pItem->nDimLevel;
2437 int nDims = pItem->nDimCount;
2438 if( nDimLevel < nDims )
2440 aRetStr += "(";
2441 for( int i = nDimLevel ; i < nDims ; i++ )
2443 sal_Int32 nMin, nMax;
2444 pArray->GetDim(sal::static_int_cast<sal_Int32>(i + 1), nMin, nMax);
2445 aRetStr += OUString::number(nMin) + " to " + OUString::number(nMax);
2446 if( i < nDims - 1 )
2447 aRetStr += ", ";
2449 aRetStr += ")";
2452 return aRetStr;
2455 } // namespace
2457 void WatchWindow::implEnableChildren(const weld::TreeIter& rEntry, bool bEnable)
2459 if (bEnable)
2461 if (!m_xTreeListBox->get_row_expanded(rEntry))
2462 m_xTreeListBox->set_children_on_demand(rEntry, true);
2464 else
2466 assert(!m_xTreeListBox->get_row_expanded(rEntry));
2467 m_xTreeListBox->set_children_on_demand(rEntry, false);
2471 void WatchWindow::UpdateWatches(bool bBasicStopped)
2473 SbMethod* pCurMethod = StarBASIC::GetActiveMethod();
2475 ErrCode eOld = SbxBase::GetError();
2476 setBasicWatchMode( true );
2478 m_xTreeListBox->all_foreach([this, pCurMethod, bBasicStopped](weld::TreeIter& rEntry){
2479 WatchItem* pItem = weld::fromId<WatchItem*>(m_xTreeListBox->get_id(rEntry));
2480 DBG_ASSERT( !pItem->maName.isEmpty(), "Var? - Must not be empty!" );
2481 OUString aWatchStr;
2482 OUString aTypeStr;
2483 if ( pCurMethod )
2485 bool bCollapse = false;
2486 TriState eEnableChildren = TRISTATE_INDET;
2488 bool bArrayElement;
2489 SbxBase* pSBX = ImplGetSBXForEntry(rEntry, bArrayElement);
2491 // Array? If no end node create type string
2492 if( bArrayElement && pItem->nDimLevel < pItem->nDimCount )
2494 SbxDimArray* pRootArray = pItem->GetRootArray();
2495 SbxDataType eType = pRootArray->GetType();
2496 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2497 eEnableChildren = TRISTATE_TRUE;
2500 if (SbxVariable* pVar = dynamic_cast<SbxVariable*>(pSBX))
2502 // extra treatment of arrays
2503 SbxDataType eType = pVar->GetType();
2504 if (isVeryLargeUnoProperty(pVar))
2506 aWatchStr += "<" + IDEResId(RID_VARIABLE_TOO_LARGE_TO_DISPLAY) + ">";
2508 else if ( eType & SbxARRAY )
2510 // consider multidimensional arrays!
2511 if (SbxDimArray* pNewArray = dynamic_cast<SbxDimArray*>(pVar->GetObject()))
2513 SbxDimArray* pOldArray = pItem->mpArray.get();
2515 bool bArrayChanged = false;
2516 if (pOldArray != nullptr)
2518 // Compare Array dimensions to see if array has changed
2519 // Can be a copy, so comparing pointers does not work
2520 sal_Int32 nOldDims = pOldArray->GetDims();
2521 sal_Int32 nNewDims = pNewArray->GetDims();
2522 if( nOldDims != nNewDims )
2524 bArrayChanged = true;
2526 else
2528 for( sal_Int32 i = 0 ; i < nOldDims ; i++ )
2530 sal_Int32 nOldMin, nOldMax;
2531 sal_Int32 nNewMin, nNewMax;
2533 pOldArray->GetDim(i + 1, nOldMin, nOldMax);
2534 pNewArray->GetDim(i + 1, nNewMin, nNewMax);
2535 if( nOldMin != nNewMin || nOldMax != nNewMax )
2537 bArrayChanged = true;
2538 break;
2543 else
2545 bArrayChanged = true;
2547 eEnableChildren = TRISTATE_TRUE;
2548 // #i37227 Clear always and replace array
2549 if( pNewArray != pOldArray )
2551 pItem->clearWatchItem();
2553 pItem->mpArray = pNewArray;
2554 sal_Int32 nDims = pNewArray->GetDims();
2555 pItem->nDimLevel = 0;
2556 pItem->nDimCount = nDims;
2558 if( bArrayChanged && pOldArray != nullptr )
2560 bCollapse = true;
2562 aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
2564 else
2566 aWatchStr += "<?>";
2569 else if ( static_cast<sal_uInt8>(eType) == sal_uInt8(SbxOBJECT) )
2571 if (SbxObject* pObj = dynamic_cast<SbxObject*>(pVar->GetObject()))
2573 if ( pItem->mpObject.is() && !pItem->maMemberList.empty() )
2575 createAllObjectProperties(pObj);
2576 SbxArray* pProps = pObj->GetProperties();
2577 const sal_uInt32 nPropCount = getCorrectedPropCount(pProps);
2578 // Check if member list has changed
2579 bCollapse = pItem->maMemberList.size() != nPropCount;
2580 for( sal_uInt32 i = 0 ; !bCollapse && i < nPropCount ; i++ )
2582 SbxVariable* pVar_ = pProps->Get(i);
2583 if( pItem->maMemberList[i] != pVar_->GetName() )
2584 bCollapse = true;
2588 pItem->mpObject = pObj;
2589 eEnableChildren = TRISTATE_TRUE;
2590 aTypeStr = getBasicObjectTypeName( pObj );
2592 else
2594 aWatchStr = "Null";
2595 if( pItem->mpObject.is() )
2597 bCollapse = true;
2598 eEnableChildren = TRISTATE_FALSE;
2602 else
2604 if( pItem->mpObject.is() )
2606 bCollapse = true;
2607 eEnableChildren = TRISTATE_FALSE;
2610 bool bString = (static_cast<sal_uInt8>(eType) == sal_uInt8(SbxSTRING));
2611 OUString aStrStr( u"\""_ustr );
2612 if( bString )
2614 aWatchStr += aStrStr;
2616 // tdf#57308 - avoid a second call to retrieve the data
2617 const SbxFlagBits nFlags = pVar->GetFlags();
2618 pVar->SetFlag(SbxFlagBits::NoBroadcast);
2619 aWatchStr += pVar->GetOUString();
2620 pVar->SetFlags(nFlags);
2621 if( bString )
2623 aWatchStr += aStrStr;
2626 if( aTypeStr.isEmpty() )
2628 if( !pVar->IsFixed() )
2630 aTypeStr = "Variant/";
2632 aTypeStr += getBasicTypeName( pVar->GetType() );
2635 else if( !bArrayElement )
2637 aWatchStr += "<Out of Scope>";
2640 if( bCollapse )
2642 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2643 pItem->clearWatchItem();
2646 if (eEnableChildren != TRISTATE_INDET)
2647 implEnableChildren(rEntry, eEnableChildren == TRISTATE_TRUE);
2649 else if( bBasicStopped )
2651 if( pItem->mpObject.is() || pItem->mpArray.is() )
2653 implCollapseModifiedObjectEntry(rEntry, *m_xTreeListBox);
2654 pItem->mpObject.clear();
2655 pItem->mpArray.clear();
2657 pItem->clearWatchItem();
2660 m_xTreeListBox->set_text(rEntry, aWatchStr, 1);
2661 m_xTreeListBox->set_text(rEntry, aTypeStr, 2);
2663 return false;
2666 SbxBase::ResetError();
2667 if( eOld != ERRCODE_NONE )
2668 SbxBase::SetError( eOld );
2669 setBasicWatchMode( false );
2672 IMPL_LINK_NOARG(CodeCompleteWindow, ImplDoubleClickHdl, weld::TreeView&, bool)
2674 InsertSelectedEntry();
2675 return true;
2678 IMPL_LINK_NOARG(CodeCompleteWindow, ImplSelectHdl, weld::TreeView&, void)
2680 //give back the focus to the parent
2681 pParent->GrabFocus();
2684 TextView* CodeCompleteWindow::GetParentEditView()
2686 return pParent->GetEditView();
2689 void CodeCompleteWindow::InsertSelectedEntry()
2691 OUString sSelectedEntry = m_xListBox->get_selected_text();
2693 if( !aFuncBuffer.isEmpty() )
2695 // if the user typed in something: remove, and insert
2696 GetParentEditView()->SetSelection(pParent->GetLastHighlightPortionTextSelection());
2697 GetParentEditView()->DeleteSelected();
2699 if (!sSelectedEntry.isEmpty())
2701 // if the user selected something
2702 GetParentEditView()->InsertText(sSelectedEntry);
2705 else
2707 if (!sSelectedEntry.isEmpty())
2709 // if the user selected something
2710 GetParentEditView()->InsertText(sSelectedEntry);
2713 HideAndRestoreFocus();
2716 void CodeCompleteWindow::SetMatchingEntries()
2718 for (sal_Int32 i = 0, nEntryCount = m_xListBox->n_children(); i< nEntryCount; ++i)
2720 OUString sEntry = m_xListBox->get_text(i);
2721 if (sEntry.startsWithIgnoreAsciiCase(aFuncBuffer))
2723 m_xListBox->select(i);
2724 break;
2729 IMPL_LINK(CodeCompleteWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool)
2731 return HandleKeyInput(rKEvt);
2734 bool CodeCompleteWindow::HandleKeyInput( const KeyEvent& rKeyEvt )
2736 bool bHandled = true;
2738 sal_Unicode aChar = rKeyEvt.GetKeyCode().GetCode();
2739 if( (( aChar >= KEY_A ) && ( aChar <= KEY_Z ))
2740 || ((aChar >= KEY_0) && (aChar <= KEY_9)) )
2742 aFuncBuffer.append(rKeyEvt.GetCharCode());
2743 SetMatchingEntries();
2745 else
2747 switch( aChar )
2749 case KEY_POINT:
2750 break;
2751 case KEY_ESCAPE: // hide, do nothing
2752 case KEY_SPACE:
2753 HideAndRestoreFocus();
2754 break;
2755 case KEY_RIGHT:
2757 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2758 if( aTextSelection.GetEnd().GetPara() != GetTextSelection().GetEnd().GetPara()-1 )
2760 HideAndRestoreFocus();
2762 break;
2764 case KEY_LEFT:
2766 TextSelection aTextSelection( GetParentEditView()->GetSelection() );
2767 if( aTextSelection.GetStart().GetIndex()-1 < GetTextSelection().GetStart().GetIndex() )
2768 {//leave the cursor where it is
2769 HideAndRestoreFocus();
2771 break;
2773 case KEY_TAB:
2775 TextSelection aTextSelection = pParent->GetLastHighlightPortionTextSelection();
2776 OUString sTypedText = pParent->GetEditEngine()->GetText(aTextSelection);
2777 if( !aFuncBuffer.isEmpty() )
2779 sal_Int32 nInd = m_xListBox->get_selected_index();
2780 if (nInd != -1)
2782 int nEntryCount = m_xListBox->n_children();
2783 //if there is something selected
2784 bool bFound = false;
2785 for (sal_Int32 i = nInd; i != nEntryCount; ++i)
2787 OUString sEntry = m_xListBox->get_text(i);
2788 if( sEntry.startsWithIgnoreAsciiCase( aFuncBuffer )
2789 && (std::u16string_view(aFuncBuffer) != sTypedText) && (i != nInd) )
2791 m_xListBox->select(i);
2792 bFound = true;
2793 break;
2796 if( !bFound )
2797 SetMatchingEntries();
2799 GetParentEditView()->SetSelection( aTextSelection );
2800 GetParentEditView()->DeleteSelected();
2801 GetParentEditView()->InsertText(m_xListBox->get_selected_text());
2804 break;
2806 case KEY_BACKSPACE: case KEY_DELETE:
2807 if( !aFuncBuffer.isEmpty() )
2809 //if there was something inserted by tab: add it to aFuncBuffer
2810 TextSelection aSel( GetParentEditView()->GetSelection() );
2811 TextPaM aEnd( GetParentEditView()->CursorEndOfLine(GetTextSelection().GetEnd()) );
2812 GetParentEditView()->SetSelection(TextSelection(GetTextSelection().GetStart(), aEnd ) );
2813 OUString aTabInsertedStr( GetParentEditView()->GetSelected() );
2814 GetParentEditView()->SetSelection( aSel );
2816 if( !aTabInsertedStr.isEmpty() && aTabInsertedStr != std::u16string_view(aFuncBuffer) )
2818 aFuncBuffer = aTabInsertedStr;
2820 aFuncBuffer.remove(aFuncBuffer.getLength()-1, 1);
2821 SetMatchingEntries();
2823 else
2825 ClearAndHide();
2826 bHandled = false;
2828 break;
2829 case KEY_RETURN:
2830 InsertSelectedEntry();
2831 break;
2832 case KEY_UP:
2834 int nInd = m_xListBox->get_selected_index();
2835 if (nInd)
2836 m_xListBox->select(nInd - 1);
2837 break;
2839 case KEY_DOWN:
2841 int nInd = m_xListBox->get_selected_index();
2842 if (nInd + 1 < m_xListBox->n_children())
2843 m_xListBox->select(nInd + 1);
2844 break;
2846 default:
2847 bHandled = false;
2848 break;
2852 return bHandled;
2855 void CodeCompleteWindow::HideAndRestoreFocus()
2857 Hide();
2858 pParent->GrabFocus();
2861 CodeCompleteWindow::CodeCompleteWindow(EditorWindow* pPar)
2862 : InterimItemWindow(pPar, u"modules/BasicIDE/ui/codecomplete.ui"_ustr, u"CodeComplete"_ustr)
2863 , pParent(pPar)
2864 , m_xListBox(m_xBuilder->weld_tree_view(u"treeview"_ustr))
2866 m_xListBox->connect_row_activated(LINK(this, CodeCompleteWindow, ImplDoubleClickHdl));
2867 m_xListBox->connect_selection_changed(LINK(this, CodeCompleteWindow, ImplSelectHdl));
2868 m_xListBox->connect_key_press(LINK(this, CodeCompleteWindow, KeyInputHdl));
2869 m_xListBox->make_sorted();
2870 m_xListBox->set_direction(false);
2872 m_xListBox->set_size_request(150, 150); // default, this will adopt the line length
2873 SetSizePixel(m_xContainer->get_preferred_size());
2876 CodeCompleteWindow::~CodeCompleteWindow()
2878 disposeOnce();
2881 void CodeCompleteWindow::dispose()
2883 m_xListBox.reset();
2884 pParent.clear();
2885 InterimItemWindow::dispose();
2888 void CodeCompleteWindow::InsertEntry( const OUString& aStr )
2890 m_xListBox->append_text(aStr);
2893 void CodeCompleteWindow::ClearListBox()
2895 m_xListBox->clear();
2896 aFuncBuffer.setLength(0);
2899 void CodeCompleteWindow::SetTextSelection( const TextSelection& aSel )
2901 m_aTextSelection = aSel;
2904 void CodeCompleteWindow::ResizeAndPositionListBox()
2906 if (m_xListBox->n_children() < 1)
2907 return;
2909 // if there is at least one element inside
2910 // calculate basic position: under the current line
2911 tools::Rectangle aRect = static_cast<TextEngine*>(pParent->GetEditEngine())->PaMtoEditCursor( pParent->GetEditView()->GetSelection().GetEnd() );
2912 tools::Long nViewYOffset = pParent->GetEditView()->GetStartDocPos().Y();
2913 Point aPos = aRect.BottomRight();// this variable will be used later (if needed)
2914 aPos.setY( (aPos.Y() - nViewYOffset) + nBasePad );
2916 // get line count
2917 const sal_uInt16 nLines = static_cast<sal_uInt16>(std::min(6, m_xListBox->n_children()));
2919 m_xListBox->set_size_request(-1, m_xListBox->get_height_rows(nLines));
2921 Size aSize = m_xContainer->get_preferred_size();
2922 //set the size
2923 SetSizePixel( aSize );
2925 //calculate position
2926 const tools::Rectangle aVisArea( pParent->GetEditView()->GetStartDocPos(), pParent->GetOutputSizePixel() ); //the visible area
2927 const Point aBottomPoint = aVisArea.BottomRight();
2929 if( aVisArea.TopRight().getY() + aPos.getY() + aSize.getHeight() > aBottomPoint.getY() )
2930 {//clipped at the bottom: move it up
2931 const tools::Long nParentFontHeight = pParent->GetEditEngine()->GetFont().GetFontHeight(); //parent's font (in the IDE): needed for height
2932 aPos.AdjustY( -(aSize.getHeight() + nParentFontHeight + nCursorPad) );
2935 if( aVisArea.TopLeft().getX() + aPos.getX() + aSize.getWidth() > aBottomPoint.getX() )
2936 {//clipped at the right side, move it a bit left
2937 aPos.AdjustX( -(aSize.getWidth() + aVisArea.TopLeft().getX()) );
2939 //set the position
2940 SetPosPixel( aPos );
2943 void CodeCompleteWindow::SelectFirstEntry()
2945 if (m_xListBox->n_children() > 0)
2946 m_xListBox->select(0);
2949 void CodeCompleteWindow::ClearAndHide()
2951 ClearListBox();
2952 HideAndRestoreFocus();
2955 UnoTypeCodeCompletetor::UnoTypeCodeCompletetor( const std::vector< OUString >& aVect, const OUString& sVarType )
2956 : bCanComplete( true )
2958 if( aVect.empty() || sVarType.isEmpty() )
2960 bCanComplete = false;//invalid parameters, nothing to code complete
2961 return;
2966 // Get the base class for reflection:
2967 xClass = css::reflection::theCoreReflection::get(
2968 comphelper::getProcessComponentContext())->forName(sVarType);
2970 catch( const Exception& )
2972 bCanComplete = false;
2973 return;
2976 //start from aVect[1]: aVect[0] is the variable name
2977 bCanComplete = std::none_of(aVect.begin() + 1, aVect.end(), [this](const OUString& rMethName) {
2978 return (!CodeCompleteOptions::IsExtendedTypeDeclaration() || !CheckMethod(rMethName)) && !CheckField(rMethName); });
2981 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassMethods() const
2983 std::vector< OUString > aRetVect;
2984 if( bCanComplete && ( xClass != nullptr ) )
2986 const Sequence< Reference< reflection::XIdlMethod > > aMethods = xClass->getMethods();
2987 for(Reference< reflection::XIdlMethod > const & rMethod : aMethods)
2989 aRetVect.push_back( rMethod->getName() );
2992 return aRetVect;//this is empty when cannot code complete
2995 std::vector< OUString > UnoTypeCodeCompletetor::GetXIdlClassFields() const
2997 std::vector< OUString > aRetVect;
2998 if( bCanComplete && ( xClass != nullptr ) )
3000 const Sequence< Reference< reflection::XIdlField > > aFields = xClass->getFields();
3001 for(Reference< reflection::XIdlField > const & rxField : aFields)
3003 aRetVect.push_back( rxField->getName() );
3006 return aRetVect;//this is empty when cannot code complete
3010 bool UnoTypeCodeCompletetor::CheckField( const OUString& sFieldName )
3011 {// modifies xClass!!!
3013 if ( xClass == nullptr )
3014 return false;
3016 Reference< reflection::XIdlField> xField = xClass->getField( sFieldName );
3017 if( xField != nullptr )
3019 xClass = xField->getType();
3020 if( xClass != nullptr )
3022 return true;
3025 return false;
3028 bool UnoTypeCodeCompletetor::CheckMethod( const OUString& sMethName )
3029 {// modifies xClass!!!
3032 if ( xClass == nullptr )
3033 return false;
3035 Reference< reflection::XIdlMethod> xMethod = xClass->getMethod( sMethName );
3036 if( xMethod != nullptr ) //method OK, check return type
3038 xClass = xMethod->getReturnType();
3039 if( xClass != nullptr )
3041 return true;
3044 return false;
3047 } // namespace basctl
3049 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */