/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include "baside2.hxx" #include #include #include #include "brkdlg.hxx" #include #include #include "moduldlg.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace basctl { namespace { namespace Print { tools::Long const nLeftMargin = 1700; tools::Long const nRightMargin = 900; tools::Long const nTopMargin = 2000; tools::Long const nBottomMargin = 1000; tools::Long const nBorder = 300; } short const ValidWindow = 0x1234; // What (who) are OW and MTF? Compare to baside3.cxx where an // identically named variable, used in the same way, has the value // "*.*" on Windows, "*" otherwise. Is that what should be done here, // too? #if defined(OW) || defined(MTF) char const FilterMask_All[] = "*"; #else constexpr OUStringLiteral FilterMask_All = u"*.*"; #endif } // end anonymous namespace using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::ui::dialogs; using namespace utl; using namespace comphelper; namespace { void lcl_PrintHeader( Printer* pPrinter, sal_uInt16 nPages, sal_uInt16 nCurPage, const OUString& rTitle, bool bOutput ) { Size const aSz = pPrinter->GetOutputSize(); const Color aOldLineColor( pPrinter->GetLineColor() ); const Color aOldFillColor( pPrinter->GetFillColor() ); const vcl::Font aOldFont( pPrinter->GetFont() ); pPrinter->SetLineColor( COL_BLACK ); pPrinter->SetFillColor(); vcl::Font aFont( aOldFont ); aFont.SetWeight( WEIGHT_BOLD ); aFont.SetAlignment( ALIGN_BOTTOM ); pPrinter->SetFont( aFont ); tools::Long nFontHeight = pPrinter->GetTextHeight(); // 1st Border => line, 2+3 Border = free space tools::Long nYTop = Print::nTopMargin - 3*Print::nBorder - nFontHeight; tools::Long nXLeft = Print::nLeftMargin - Print::nBorder; tools::Long nXRight = aSz.Width() - Print::nRightMargin + Print::nBorder; if( bOutput ) pPrinter->DrawRect(tools::Rectangle( Point(nXLeft, nYTop), Size(nXRight - nXLeft, aSz.Height() - nYTop - Print::nBottomMargin + Print::nBorder) )); tools::Long nY = Print::nTopMargin - 2*Print::nBorder; Point aPos(Print::nLeftMargin, nY); if( bOutput ) pPrinter->DrawText( aPos, rTitle ); if ( nPages != 1 ) { aFont.SetWeight( WEIGHT_NORMAL ); pPrinter->SetFont( aFont ); aPos.AdjustX(pPrinter->GetTextWidth( rTitle ) ); if( bOutput ) { OUString aPageStr = " [" + IDEResId(RID_STR_PAGE) + " " + OUString::number( nCurPage ) + "]"; pPrinter->DrawText( aPos, aPageStr ); } } nY = Print::nTopMargin - Print::nBorder; if( bOutput ) pPrinter->DrawLine( Point( nXLeft, nY ), Point( nXRight, nY ) ); pPrinter->SetFont( aOldFont ); pPrinter->SetFillColor( aOldFillColor ); pPrinter->SetLineColor( aOldLineColor ); } void lcl_ConvertTabsToSpaces( OUString& rLine ) { if ( rLine.isEmpty() ) return; OUStringBuffer aResult( rLine ); sal_Int32 nPos = 0; sal_Int32 nMax = aResult.getLength(); while ( nPos < nMax ) { if ( aResult[nPos] == '\t' ) { // not 4 Blanks, but at 4 TabPos: OUStringBuffer aBlanker; string::padToLength(aBlanker, ( 4 - ( nPos % 4 ) ), ' '); aResult.remove( nPos, 1 ); aResult.insert( nPos, aBlanker ); nMax = aResult.getLength(); } ++nPos; } rLine = aResult.makeStringAndClear(); } } // namespace ModulWindow::ModulWindow (ModulWindowLayout* pParent, ScriptDocument const& rDocument, const OUString& aLibName, const OUString& aName, OUString aModule) : BaseWindow(pParent, rDocument, aLibName, aName) , m_rLayout(*pParent) , m_nValid(ValidWindow) , m_aXEditorWindow(VclPtr::Create(this)) , m_aModule(std::move(aModule)) { m_aXEditorWindow->Show(); SetBackground(); } SbModuleRef const & ModulWindow::XModule() { // ModuleWindows can now be created as a result of the // modules getting created via the api. This is a result of an // elementInserted event from the BasicLibrary container. // However the SbModule is also created from a different listener to // the same event ( in basmgr ) Therefore it is possible when we look // for m_xModule it may not yet be available, here we keep trying to access // the module until such time as it exists if ( !m_xModule.is() ) { BasicManager* pBasMgr = GetDocument().getBasicManager(); if ( pBasMgr ) { StarBASIC* pBasic = pBasMgr->GetLib( GetLibName() ); if ( pBasic ) { m_xBasic = pBasic; m_xModule = pBasic->FindModule( GetName() ); } } } return m_xModule; } ModulWindow::~ModulWindow() { disposeOnce(); } void ModulWindow::dispose() { m_nValid = 0; StarBASIC::Stop(); m_aXEditorWindow.disposeAndClear(); BaseWindow::dispose(); } void ModulWindow::GetFocus() { if (m_nValid != ValidWindow) return; m_aXEditorWindow->GetEdtWindow().GrabFocus(); // don't call basic calls because focus is somewhere else... } void ModulWindow::DoInit() { GetEditorWindow().InitScrollBars(); } void ModulWindow::Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle&) { } void ModulWindow::Resize() { m_aXEditorWindow->SetPosSizePixel( Point( 0, 0 ), GetOutputSizePixel() ); } void ModulWindow::CheckCompileBasic() { if ( !XModule().is() ) return; // never compile while running! bool const bRunning = StarBASIC::IsRunning(); bool const bModified = ( !m_xModule->IsCompiled() || ( GetEditEngine() && GetEditEngine()->IsModified() ) ); if ( bRunning || !bModified ) return; bool bDone = false; GetShell()->GetViewFrame().GetWindow().EnterWait(); AssertValidEditEngine(); GetEditorWindow().SetSourceInBasic(); bool bWasModified = GetBasic()->IsModified(); { // tdf#106529: only use strict compilation mode when compiling from the IDE css::uno::ContextLayer layer(comphelper::NewFlagContext("BasicStrict")); bDone = m_xModule->Compile(); } if ( !bWasModified ) GetBasic()->SetModified(false); if ( bDone ) { GetBreakPoints().SetBreakPointsInBasic( m_xModule.get() ); } GetShell()->GetViewFrame().GetWindow().LeaveWait(); m_aStatus.bError = !bDone; m_aStatus.bIsRunning = false; } void ModulWindow::BasicExecute() { // #116444# check security settings before macro execution ScriptDocument aDocument( GetDocument() ); bool bMacrosDisabled = officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get(); if (bMacrosDisabled || (aDocument.isDocument() && !aDocument.allowMacros())) { std::unique_ptr xBox( Application::CreateMessageDialog(GetFrameWeld(), VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_CANNOTRUNMACRO))); xBox->run(); return; } CheckCompileBasic(); if ( !XModule().is() || !m_xModule->IsCompiled() || m_aStatus.bError ) return; if ( GetBreakPoints().size() ) m_aStatus.nBasicFlags = m_aStatus.nBasicFlags | BasicDebugFlags::Break; if ( !m_aStatus.bIsRunning ) { DBG_ASSERT( m_xModule.is(), "No Module!" ); AddStatus( BASWIN_RUNNINGBASIC ); sal_uInt16 nStart, nEnd; TextSelection aSel = GetEditView()->GetSelection(); // Init cursor to top const sal_uInt32 nCurMethodStart = aSel.GetStart().GetPara() + 1; SbMethod* pMethod = nullptr; // first Macro, else blind "Main" (ExtSearch?) for (sal_uInt32 nMacro = 0; nMacro < m_xModule->GetMethods()->Count(); nMacro++) { SbMethod* pM = static_cast(m_xModule->GetMethods()->Get(nMacro)); assert(pM && "Method?"); pM->GetLineRange( nStart, nEnd ); if ( nCurMethodStart >= nStart && nCurMethodStart <= nEnd ) { // matched a method to the cursor position pMethod = pM; break; } } if ( !pMethod ) { // If not in a method then prompt the user ChooseMacro(GetFrameWeld(), uno::Reference()); return; } pMethod->SetDebugFlags(m_aStatus.nBasicFlags); BasicDLL::SetDebugMode(true); RunMethod(pMethod); BasicDLL::SetDebugMode(false); // if cancelled during Interactive=false BasicDLL::EnableBreak(true); ClearStatus( BASWIN_RUNNINGBASIC ); } else m_aStatus.bIsRunning = false; // cancel of Reschedule() } void ModulWindow::CompileBasic() { CheckCompileBasic(); XModule().is() && m_xModule->IsCompiled(); } void ModulWindow::BasicRun() { m_aStatus.nBasicFlags = BasicDebugFlags::NONE; BasicExecute(); } void ModulWindow::BasicStepOver() { m_aStatus.nBasicFlags = BasicDebugFlags::StepInto | BasicDebugFlags::StepOver; BasicExecute(); } void ModulWindow::BasicStepInto() { m_aStatus.nBasicFlags = BasicDebugFlags::StepInto; BasicExecute(); } void ModulWindow::BasicStepOut() { m_aStatus.nBasicFlags = BasicDebugFlags::StepOut; BasicExecute(); } void ModulWindow::BasicStop() { StarBASIC::Stop(); m_aStatus.bIsRunning = false; } void ModulWindow::LoadBasic() { sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, FileDialogFlags::NONE, this->GetFrameWeld()); aDlg.SetContext(sfx2::FileDialogHelper::BasicImportSource); Reference xFP = aDlg.GetFilePicker(); xFP->appendFilter( "BASIC" , "*.bas" ); xFP->appendFilter( IDEResId(RID_STR_FILTER_ALLFILES), FilterMask_All ); xFP->setCurrentFilter( "BASIC" ); if( aDlg.Execute() != ERRCODE_NONE ) return; Sequence< OUString > aPaths = xFP->getSelectedFiles(); SfxMedium aMedium( aPaths[0], StreamMode::READ | StreamMode::SHARE_DENYWRITE | StreamMode::NOCREATE ); SvStream* pStream = aMedium.GetInStream(); if ( pStream ) { AssertValidEditEngine(); sal_uInt32 nLines = CalcLineCount( *pStream ); // nLines*4: ReadText/Formatting/Highlighting/Formatting GetEditorWindow().CreateProgress( IDEResId(RID_STR_GENERATESOURCE), nLines*4 ); GetEditEngine()->SetUpdateMode( false ); // tdf#139196 - import macros using either default or utf-8 text encoding pStream->StartReadingUnicodeText(RTL_TEXTENCODING_UTF8); if (pStream->Tell() == 3) pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); GetEditView()->Read( *pStream ); GetEditEngine()->SetUpdateMode( true ); GetEditorWindow().PaintImmediately(); GetEditorWindow().ForceSyntaxTimeout(); GetEditorWindow().DestroyProgress(); ErrCode nError = aMedium.GetError(); if ( nError ) ErrorHandler::HandleError( nError ); } else { std::unique_ptr xBox(Application::CreateMessageDialog(GetFrameWeld(), VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_COULDNTREAD))); xBox->run(); } } void ModulWindow::SaveBasicSource() { sfx2::FileDialogHelper aDlg(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, this->GetFrameWeld()); aDlg.SetContext(sfx2::FileDialogHelper::BasicExportSource); const Reference& xFP = aDlg.GetFilePicker(); xFP.queryThrow()->setValue(ExtendedFilePickerElementIds::CHECKBOX_AUTOEXTENSION, 0, Any(true)); xFP->appendFilter( "BASIC", "*.bas" ); xFP->appendFilter( IDEResId(RID_STR_FILTER_ALLFILES), FilterMask_All ); xFP->setCurrentFilter( "BASIC" ); if( aDlg.Execute() != ERRCODE_NONE ) return; Sequence< OUString > aPaths = xFP->getSelectedFiles(); SfxMedium aMedium( aPaths[0], StreamMode::WRITE | StreamMode::SHARE_DENYWRITE | StreamMode::TRUNC ); SvStream* pStream = aMedium.GetOutStream(); if ( pStream ) { EnterWait(); AssertValidEditEngine(); // tdf#139196 - export macros using utf-8 including BOM pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8); pStream->WriteUChar(0xEF).WriteUChar(0xBB).WriteUChar(0xBF); GetEditEngine()->Write( *pStream ); aMedium.Commit(); LeaveWait(); ErrCode nError = aMedium.GetError(); if ( nError ) ErrorHandler::HandleError( nError ); } else { std::unique_ptr xErrorBox(Application::CreateMessageDialog(GetFrameWeld(), VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_COULDNTWRITE))); xErrorBox->run(); } } void ModulWindow::ImportDialog() { const ScriptDocument& rDocument = GetDocument(); OUString aLibName = GetLibName(); implImportDialog(GetFrameWeld(), rDocument, aLibName); } void ModulWindow::ToggleBreakPoint( sal_uInt16 nLine ) { DBG_ASSERT( XModule().is(), "No Module!" ); if ( !XModule().is() ) return; CheckCompileBasic(); if ( m_aStatus.bError ) { return; } BreakPoint* pBrk = GetBreakPoints().FindBreakPoint( nLine ); if ( pBrk ) // remove { m_xModule->ClearBP( nLine ); GetBreakPoints().remove( pBrk ); } else // create one { if ( m_xModule->SetBP( nLine )) { GetBreakPoints().InsertSorted( BreakPoint( nLine ) ); if ( StarBASIC::IsRunning() ) { for (sal_uInt32 nMethod = 0; nMethod < m_xModule->GetMethods()->Count(); nMethod++) { SbMethod* pMethod = static_cast(m_xModule->GetMethods()->Get(nMethod)); assert(pMethod && "Method not found! (NULL)"); pMethod->SetDebugFlags( pMethod->GetDebugFlags() | BasicDebugFlags::Break ); } } } } } void ModulWindow::UpdateBreakPoint( const BreakPoint& rBrk ) { DBG_ASSERT( XModule().is(), "No Module!" ); if ( XModule().is() ) { CheckCompileBasic(); if ( rBrk.bEnabled ) m_xModule->SetBP( rBrk.nLine ); else m_xModule->ClearBP( rBrk.nLine ); } } void ModulWindow::BasicToggleBreakPoint() { AssertValidEditEngine(); TextSelection aSel = GetEditView()->GetSelection(); aSel.GetStart().GetPara()++; // Basic lines start at 1! aSel.GetEnd().GetPara()++; for ( sal_uInt32 nLine = aSel.GetStart().GetPara(); nLine <= aSel.GetEnd().GetPara(); ++nLine ) { ToggleBreakPoint( nLine ); } m_aXEditorWindow->GetBrkWindow().Invalidate(); } void ModulWindow::BasicToggleBreakPointEnabled() { AssertValidEditEngine(); TextView* pView = GetEditView(); if ( !pView ) return; TextSelection aSel = pView->GetSelection(); BreakPointList& rList = GetBreakPoints(); for ( sal_uInt32 nLine = ++aSel.GetStart().GetPara(), nEnd = ++aSel.GetEnd().GetPara(); nLine <= nEnd; ++nLine ) { BreakPoint* pBrk = rList.FindBreakPoint( nLine ); if ( pBrk ) { pBrk->bEnabled = !pBrk->bEnabled; UpdateBreakPoint( *pBrk ); } } GetBreakPointWindow().Invalidate(); } void ModulWindow::ManageBreakPoints() { BreakPointWindow& rBrkWin = GetBreakPointWindow(); BreakPointDialog aBrkDlg(rBrkWin.GetFrameWeld(), GetBreakPoints()); aBrkDlg.run(); rBrkWin.Invalidate(); } void ModulWindow::BasicErrorHdl( StarBASIC const * pBasic ) { GetShell()->GetViewFrame().ToTop(); // Return value: BOOL // FALSE: cancel // TRUE: go on... sal_uInt16 nErrorLine = StarBASIC::GetLine() - 1; sal_uInt16 nErrCol1 = StarBASIC::GetCol1(); sal_uInt16 nErrCol2 = StarBASIC::GetCol2(); if ( nErrCol2 != 0xFFFF ) nErrCol2++; AssertValidEditEngine(); GetEditView()->SetSelection( TextSelection( TextPaM( nErrorLine, nErrCol1 ), TextPaM( nErrorLine, nErrCol2 ) ) ); // if other basic, the IDE should try to display the correct module bool const bMarkError = pBasic == GetBasic(); if ( bMarkError ) m_aXEditorWindow->GetBrkWindow().SetMarkerPos(nErrorLine, true); // #i47002# Reference< awt::XWindow > xWindow = VCLUnoHelper::GetInterface( this ); // tdf#118572 make a currently running dialog, regardless of what its modal // to, insensitive to user input until after this error dialog goes away. TopLevelWindowLocker aBusy; aBusy.incBusy(nullptr); ErrorHandler::HandleError(StarBASIC::GetErrorCode(), GetFrameWeld()); aBusy.decBusy(); // #i47002# VclPtr pWindow = VCLUnoHelper::GetWindow( xWindow ); if ( !pWindow ) return; if ( bMarkError ) m_aXEditorWindow->GetBrkWindow().SetNoMarker(); return; } BasicDebugFlags ModulWindow::BasicBreakHdl() { // Return value: sal_uInt16 => see SB-Debug-Flags sal_uInt16 nErrorLine = StarBASIC::GetLine(); BreakPoint* pBrk = GetBreakPoints().FindBreakPoint( nErrorLine ); if ( pBrk ) { pBrk->nHitCount++; if ( pBrk->nHitCount <= pBrk->nStopAfter && GetBasic()->IsBreak() ) return m_aStatus.nBasicFlags; // go on... } nErrorLine--; // EditEngine starts at 0, Basic at 1 AssertValidEditEngine(); GetEditView()->SetSelection( TextSelection( TextPaM( nErrorLine, 0 ), TextPaM( nErrorLine, 0 ) ) ); m_aXEditorWindow->GetBrkWindow().SetMarkerPos( nErrorLine ); m_rLayout.UpdateDebug(false); m_aStatus.bIsInReschedule = true; m_aStatus.bIsRunning = true; AddStatus( BASWIN_INRESCHEDULE ); InvalidateDebuggerSlots(); while( m_aStatus.bIsRunning && !Application::IsQuit()) Application::Yield(); m_aStatus.bIsInReschedule = false; m_aXEditorWindow->GetBrkWindow().SetNoMarker(); ClearStatus( BASWIN_INRESCHEDULE ); return m_aStatus.nBasicFlags; } void ModulWindow::BasicAddWatch() { AssertValidEditEngine(); bool bAdd = true; if ( !GetEditView()->HasSelection() ) { // tdf#57307 - expand selection to include connector punctuations TextSelection aSel; OUString aWord = GetEditEngine()->GetWord( GetEditView()->GetSelection().GetEnd(), &aSel.GetStart(), &aSel.GetEnd() ); if ( !aWord.isEmpty() ) GetEditView()->SetSelection( aSel ); else bAdd = false; } if ( bAdd ) { TextSelection aSel = GetEditView()->GetSelection(); if ( aSel.GetStart().GetPara() == aSel.GetEnd().GetPara() ) // single line selection m_rLayout.BasicAddWatch(GetEditView()->GetSelected()); } } void ModulWindow::EditMacro( const OUString& rMacroName ) { DBG_ASSERT( XModule().is(), "No Module!" ); if ( !XModule().is() ) return; CheckCompileBasic(); if ( m_aStatus.bError ) return; sal_uInt16 nStart, nEnd; SbMethod* pMethod = static_cast(m_xModule->Find( rMacroName, SbxClassType::Method )); if ( !pMethod ) return; pMethod->GetLineRange( nStart, nEnd ); if ( nStart ) { nStart--; nEnd--; } TextSelection aSel( TextPaM( nStart, 0 ), TextPaM( nStart, 0 ) ); AssertValidEditEngine(); TextView * pView = GetEditView(); // scroll if applicable so that first line is at the top tools::Long nVisHeight = GetOutputSizePixel().Height(); if ( pView->GetTextEngine()->GetTextHeight() > nVisHeight ) { tools::Long nMaxY = pView->GetTextEngine()->GetTextHeight() - nVisHeight; tools::Long nOldStartY = pView->GetStartDocPos().Y(); tools::Long nNewStartY = static_cast(nStart) * pView->GetTextEngine()->GetCharHeight(); nNewStartY = std::min( nNewStartY, nMaxY ); pView->Scroll( 0, -(nNewStartY-nOldStartY) ); pView->ShowCursor( false ); GetEditVScrollBar().SetThumbPos( pView->GetStartDocPos().Y() ); } pView->SetSelection( aSel ); pView->ShowCursor(); pView->GetWindow()->GrabFocus(); } void ModulWindow::StoreData() { // StoreData is called when the BasicManager is destroyed or // this window is closed. // => interrupts undesired! GetEditorWindow().SetSourceInBasic(); } bool ModulWindow::AllowUndo() { return GetEditorWindow().CanModify(); } void ModulWindow::UpdateData() { DBG_ASSERT( XModule().is(), "No Module!" ); // UpdateData is called when the source has changed from outside // => interrupts undesired! if ( !XModule().is() ) return; SetModule( m_xModule->GetSource32() ); if ( GetEditView() ) { TextSelection aSel = GetEditView()->GetSelection(); setTextEngineText(*GetEditEngine(), m_xModule->GetSource32()); GetEditView()->SetSelection( aSel ); GetEditEngine()->SetModified( false ); MarkDocumentModified( GetDocument() ); } } sal_Int32 ModulWindow::countPages( Printer* pPrinter ) { return FormatAndPrint( pPrinter, -1 ); } void ModulWindow::printPage( sal_Int32 nPage, Printer* pPrinter ) { FormatAndPrint( pPrinter, nPage ); } /* implementation note: this is totally inefficient for the XRenderable interface usage since the whole "document" will be format for every page. Should this ever become a problem we should - format only once for every new printer - keep an index list for each page which is the starting paragraph */ sal_Int32 ModulWindow::FormatAndPrint( Printer* pPrinter, sal_Int32 nPrintPage ) { AssertValidEditEngine(); MapMode eOldMapMode( pPrinter->GetMapMode() ); vcl::Font aOldFont( pPrinter->GetFont() ); vcl::Font aFont( GetEditEngine()->GetFont() ); aFont.SetAlignment( ALIGN_BOTTOM ); aFont.SetTransparent( true ); aFont.SetFontSize( Size( 0, 360 ) ); pPrinter->SetFont( aFont ); pPrinter->SetMapMode(MapMode(MapUnit::Map100thMM)); OUString aTitle( CreateQualifiedName() ); sal_Int32 nLineHeight = pPrinter->GetTextHeight(); if(nLineHeight == 0) { nLineHeight = 1; } Size aPaperSz = pPrinter->GetOutputSize(); aPaperSz.AdjustWidth( -(Print::nLeftMargin + Print::nRightMargin) ); aPaperSz.AdjustHeight( -(Print::nTopMargin + Print::nBottomMargin) ); // nLinepPage is not correct if there's a line break sal_Int32 nLinespPage = aPaperSz.Height()/nLineHeight; tools::Long nXTextWidth = pPrinter->approximate_digit_width(); sal_Int32 nCharspLine = aPaperSz.Width() / std::max(nXTextWidth, 1); const sal_uInt32 nParas = GetEditEngine()->GetParagraphCount(); sal_Int32 nPages = nParas/nLinespPage+1; sal_Int32 nCurPage = 1; lcl_PrintHeader( pPrinter, nPages, nCurPage, aTitle, nPrintPage == 0 ); Point aPos( Print::nLeftMargin, Print::nTopMargin ); for ( sal_uInt32 nPara = 0; nPara < nParas; ++nPara ) { OUString aLine( GetEditEngine()->GetText( nPara ) ); lcl_ConvertTabsToSpaces( aLine ); sal_Int32 nLines = aLine.getLength()/nCharspLine+1; for (sal_Int32 nLine = 0; nLine < nLines; ++nLine) { sal_Int32 nBeginIndex = nLine*nCharspLine; sal_Int32 nCopyCount = std::min(nCharspLine, aLine.getLength()-nBeginIndex); OUString aTmpLine = aLine.copy(nBeginIndex, nCopyCount); aPos.AdjustY(nLineHeight ); if ( aPos.Y() > ( aPaperSz.Height() + Print::nTopMargin ) ) { nCurPage++; lcl_PrintHeader( pPrinter, nPages, nCurPage, aTitle, nCurPage-1 == nPrintPage ); aPos = Point(Print::nLeftMargin, Print::nTopMargin + nLineHeight); } if( nCurPage-1 == nPrintPage ) pPrinter->DrawText( aPos, aTmpLine ); } aPos.AdjustY(10 ); // nParaSpace } pPrinter->SetFont( aOldFont ); pPrinter->SetMapMode( eOldMapMode ); return nCurPage; } void ModulWindow::ExecuteCommand (SfxRequest& rReq) { AssertValidEditEngine(); switch (rReq.GetSlot()) { case SID_DELETE: { if (!IsReadOnly()) { KeyEvent aFakeDelete(0, KEY_DELETE); (void)GetEditView()->KeyInput(aFakeDelete); } break; } case SID_SELECTALL: { TextSelection aSel( TextPaM( 0, 0 ), TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ) ); TextView * pView = GetEditView(); pView->SetSelection( aSel ); pView->GetWindow()->GrabFocus(); break; } case SID_BASICRUN: { BasicRun(); } break; case SID_BASICCOMPILE: { CompileBasic(); } break; case SID_BASICSTEPOVER: { BasicStepOver(); } break; case SID_BASICSTEPINTO: { BasicStepInto(); } break; case SID_BASICSTEPOUT: { BasicStepOut(); } break; case SID_BASICLOAD: { LoadBasic(); } break; case SID_BASICSAVEAS: { SaveBasicSource(); } break; case SID_IMPORT_DIALOG: { ImportDialog(); } break; case SID_BASICIDE_MATCHGROUP: { GetEditView()->MatchGroup(); } break; case SID_BASICIDE_TOGGLEBRKPNT: { BasicToggleBreakPoint(); } break; case SID_BASICIDE_MANAGEBRKPNTS: { ManageBreakPoints(); } break; case SID_BASICIDE_TOGGLEBRKPNTENABLED: { BasicToggleBreakPointEnabled(); } break; case SID_BASICIDE_ADDWATCH: { BasicAddWatch(); } break; case SID_BASICIDE_REMOVEWATCH: { m_rLayout.BasicRemoveWatch(); } break; case SID_CUT: { if ( !IsReadOnly() ) { GetEditView()->Cut(); if (SfxBindings* pBindings = GetBindingsPtr()) pBindings->Invalidate( SID_DOC_MODIFIED ); } } break; case SID_COPY: { GetEditView()->Copy(); } break; case SID_PASTE: { if ( !IsReadOnly() ) { GetEditView()->Paste(); if (SfxBindings* pBindings = GetBindingsPtr()) pBindings->Invalidate( SID_DOC_MODIFIED ); } } break; case SID_BASICIDE_BRKPNTSCHANGED: { GetBreakPointWindow().Invalidate(); } break; case SID_SHOWLINES: { const SfxBoolItem* pItem = rReq.GetArg(rReq.GetSlot()); bool bLineNumbers = pItem && pItem->GetValue(); m_aXEditorWindow->SetLineNumberDisplay(bLineNumbers); std::shared_ptr batch(comphelper::ConfigurationChanges::create()); officecfg::Office::BasicIDE::EditorSettings::LineNumbering::set(bLineNumbers, batch); batch->commit(); } break; case SID_BASICIDE_DELETECURRENT: { if (QueryDelModule(m_aName, GetFrameWeld())) { // tdf#134551 don't delete the window if last module is removed until this block // is complete VclPtr xKeepRef(this); if (m_aDocument.removeModule(m_aLibName, m_aName)) MarkDocumentModified(m_aDocument); } } break; case FID_SEARCH_OFF: GrabFocus(); break; case SID_GOTOLINE: { GotoLineDialog aGotoDlg(GetFrameWeld()); if (aGotoDlg.run() == RET_OK) { if (sal_Int32 const nLine = aGotoDlg.GetLineNumber()) { TextSelection const aSel(TextPaM(nLine - 1, 0), TextPaM(nLine - 1, 0)); GetEditView()->SetSelection(aSel); } } break; } } } void ModulWindow::ExecuteGlobal (SfxRequest& rReq) { switch (rReq.GetSlot()) { case SID_SIGNATURE: { DocumentSignature aSignature(m_aDocument); if (aSignature.supportsSignatures()) { aSignature.signScriptingContent(rReq.GetFrameWeld()); if (SfxBindings* pBindings = GetBindingsPtr()) pBindings->Invalidate(SID_SIGNATURE); } } break; } } void ModulWindow::GetState( SfxItemSet &rSet ) { SfxWhichIter aIter(rSet); for ( sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich() ) { switch ( nWh ) { case SID_CUT: { if ( !GetEditView() || !GetEditView()->HasSelection() ) rSet.DisableItem( nWh ); if ( IsReadOnly() ) rSet.DisableItem( nWh ); } break; case SID_COPY: { if ( !GetEditView() || !GetEditView()->HasSelection() ) rSet.DisableItem( nWh ); } break; case SID_PASTE: { if ( !IsPasteAllowed() ) rSet.DisableItem( nWh ); if ( IsReadOnly() ) rSet.DisableItem( nWh ); } break; case SID_BASICIDE_STAT_POS: { TextView* pView = GetEditView(); if ( pView ) { TextSelection aSel = pView->GetSelection(); OUString aPos = IDEResId( RID_STR_LINE ) + " " + OUString::number(aSel.GetEnd().GetPara()+1) + ", " + IDEResId( RID_STR_COLUMN ) + " " + OUString::number(aSel.GetEnd().GetIndex()+1); SfxStringItem aItem( SID_BASICIDE_STAT_POS, aPos ); rSet.Put( aItem ); } } break; case SID_BASICIDE_STAT_TITLE: { // search for current procedure name (Sub or Function) TextView* pView = GetEditView(); if ( pView ) { OUString sProcName; TextSelection aSel = pView->GetSelection(); sal_uInt32 i = aSel.GetStart().GetPara(); do { OUString aCurrLine = GetEditEngine()->GetText( i ); OUString sProcType; if (GetEditorWindow().GetProcedureName(aCurrLine, sProcType, sProcName)) break; } while (i--); OUString aTitle = CreateQualifiedName(); if (!sProcName.isEmpty()) aTitle += "." + sProcName; if (IsReadOnly()) aTitle += " (" + IDEResId(RID_STR_READONLY) + ")"; SfxStringItem aTitleItem( SID_BASICIDE_STAT_TITLE, aTitle ); rSet.Put( aTitleItem ); } } break; case SID_ATTR_INSERT: { TextView* pView = GetEditView(); if ( pView ) { SfxBoolItem aItem( SID_ATTR_INSERT, pView->IsInsertMode() ); rSet.Put( aItem ); } } break; case SID_SHOWLINES: { bool bLineNumbers = ::officecfg::Office::BasicIDE::EditorSettings::LineNumbering::get(); rSet.Put(SfxBoolItem(nWh, bLineNumbers)); break; } case SID_SELECTALL: { if ( !GetEditView() ) rSet.DisableItem( nWh ); } break; } } } void ModulWindow::DoScroll( Scrollable* pCurScrollBar ) { if ( ( pCurScrollBar == GetHScrollBar() ) && GetEditView() ) { // don't scroll with the value but rather use the Thumb-Pos for the VisArea: tools::Long nDiff = GetEditView()->GetStartDocPos().X() - pCurScrollBar->GetThumbPos(); GetEditView()->Scroll( nDiff, 0 ); GetEditView()->ShowCursor( false ); pCurScrollBar->SetThumbPos( GetEditView()->GetStartDocPos().X() ); } } bool ModulWindow::IsModified() { return GetEditEngine() && GetEditEngine()->IsModified(); } OUString ModulWindow::GetSbModuleName() { OUString aModuleName; if ( XModule().is() ) aModuleName = m_xModule->GetName(); return aModuleName; } OUString ModulWindow::GetTitle() { return GetSbModuleName(); } void ModulWindow::ShowCursor( bool bOn ) { if ( GetEditEngine() ) { TextView* pView = GetEditEngine()->GetActiveView(); if ( pView ) { if ( bOn ) pView->ShowCursor(); else pView->HideCursor(); } } } void ModulWindow::AssertValidEditEngine() { if ( !GetEditEngine() ) GetEditorWindow().CreateEditEngine(); } void ModulWindow::Activating () { bool bLineNumbers = ::officecfg::Office::BasicIDE::EditorSettings::LineNumbering::get(); m_aXEditorWindow->SetLineNumberDisplay(bLineNumbers); Show(); } void ModulWindow::Deactivating() { Hide(); } sal_uInt16 ModulWindow::StartSearchAndReplace( const SvxSearchItem& rSearchItem, bool bFromStart ) { if (IsSuspended()) return 0; // one could also relinquish syntaxhighlighting/formatting instead of the stupid replace-everything... AssertValidEditEngine(); TextView* pView = GetEditView(); TextSelection aSel; if ( bFromStart ) { aSel = pView->GetSelection(); if ( !rSearchItem.GetBackward() ) pView->SetSelection( TextSelection() ); else pView->SetSelection( TextSelection( TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ), TextPaM( TEXT_PARA_ALL, TEXT_INDEX_ALL ) ) ); } bool const bForward = !rSearchItem.GetBackward(); sal_uInt16 nFound = 0; if ( ( rSearchItem.GetCommand() == SvxSearchCmd::FIND ) || ( rSearchItem.GetCommand() == SvxSearchCmd::FIND_ALL ) ) { nFound = pView->Search( rSearchItem.GetSearchOptions() , bForward ) ? 1 : 0; } else if ( ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE ) || ( rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL ) ) { if ( !IsReadOnly() ) { bool const bAll = rSearchItem.GetCommand() == SvxSearchCmd::REPLACE_ALL; nFound = pView->Replace( rSearchItem.GetSearchOptions() , bAll , bForward ); } } if ( bFromStart && !nFound ) pView->SetSelection( aSel ); return nFound; } SfxUndoManager* ModulWindow::GetUndoManager() { if ( GetEditEngine() ) return &GetEditEngine()->GetUndoManager(); return nullptr; } SearchOptionFlags ModulWindow::GetSearchOptions() { SearchOptionFlags nOptions = SearchOptionFlags::SEARCH | SearchOptionFlags::WHOLE_WORDS | SearchOptionFlags::BACKWARDS | SearchOptionFlags::REG_EXP | SearchOptionFlags::EXACT | SearchOptionFlags::SELECTION | SearchOptionFlags::SIMILARITY; if ( !IsReadOnly() ) { nOptions |= SearchOptionFlags::REPLACE; nOptions |= SearchOptionFlags::REPLACE_ALL; } return nOptions; } void ModulWindow::BasicStarted() { if ( !XModule().is() ) return; m_aStatus.bIsRunning = true; BreakPointList& rList = GetBreakPoints(); if ( rList.size() ) { rList.ResetHitCount(); rList.SetBreakPointsInBasic( m_xModule.get() ); for (sal_uInt32 nMethod = 0; nMethod < m_xModule->GetMethods()->Count(); nMethod++) { SbMethod* pMethod = static_cast(m_xModule->GetMethods()->Get(nMethod)); assert(pMethod && "Method not found! (NULL)"); pMethod->SetDebugFlags( pMethod->GetDebugFlags() | BasicDebugFlags::Break ); } } } void ModulWindow::BasicStopped() { m_aStatus.bIsRunning = false; GetBreakPointWindow().SetNoMarker(); } EntryDescriptor ModulWindow::CreateEntryDescriptor() { ScriptDocument aDocument( GetDocument() ); OUString aLibName( GetLibName() ); LibraryLocation eLocation = aDocument.getLibraryLocation( aLibName ); OUString aModName( GetName() ); OUString aLibSubName; if( m_xBasic.is() && aDocument.isInVBAMode() && XModule().is() ) { switch( m_xModule->GetModuleType() ) { case script::ModuleType::DOCUMENT: { aLibSubName = IDEResId( RID_STR_DOCUMENT_OBJECTS ); uno::Reference< container::XNameContainer > xLib = aDocument.getOrCreateLibrary( E_SCRIPTS, aLibName ); if( xLib.is() ) { OUString sObjName; ModuleInfoHelper::getObjectName( xLib, aModName, sObjName ); if( !sObjName.isEmpty() ) { aModName += " (" + sObjName + ")"; } } break; } case script::ModuleType::FORM: aLibSubName = IDEResId( RID_STR_USERFORMS ); break; case script::ModuleType::NORMAL: aLibSubName = IDEResId( RID_STR_NORMAL_MODULES ); break; case script::ModuleType::CLASS: aLibSubName = IDEResId( RID_STR_CLASS_MODULES ); break; } } return EntryDescriptor( aDocument, eLocation, aLibName, aLibSubName, aModName, OBJ_TYPE_MODULE ); } void ModulWindow::SetReadOnly (bool b) { if ( GetEditView() ) GetEditView()->SetReadOnly( b ); } bool ModulWindow::IsReadOnly() { return GetEditView() && GetEditView()->IsReadOnly(); } bool ModulWindow::IsPasteAllowed() { bool bPaste = false; // get clipboard Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard(); if ( xClipboard.is() ) { Reference< datatransfer::XTransferable > xTransf = xClipboard->getContents(); if ( xTransf.is() ) { datatransfer::DataFlavor aFlavor; SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor ); if ( xTransf->isDataFlavorSupported( aFlavor ) ) bPaste = true; } } return bPaste; } void ModulWindow::OnNewDocument () { bool bLineNumbers = ::officecfg::Office::BasicIDE::EditorSettings::LineNumbering::get(); m_aXEditorWindow->SetLineNumberDisplay(bLineNumbers); } OUString ModulWindow::GetHid () const { return HID_BASICIDE_MODULWINDOW; } ItemType ModulWindow::GetType () const { return TYPE_MODULE; } bool ModulWindow::HasActiveEditor () const { return !IsSuspended(); } void ModulWindow::UpdateModule () { OUString const aModule = getTextEngineText(*GetEditEngine()); // update module in basic assert(m_xModule.is()); // update module in module window SetModule(aModule); // update module in library OSL_VERIFY(m_aDocument.updateModule(m_aLibName, m_aName, aModule)); GetEditEngine()->SetModified(false); MarkDocumentModified(m_aDocument); } ModulWindowLayout::ModulWindowLayout (vcl::Window* pParent, ObjectCatalog& rObjectCatalog_) : Layout(pParent), pChild(nullptr), aWatchWindow(VclPtr::Create(this)), aStackWindow(VclPtr::Create(this)), rObjectCatalog(rObjectCatalog_) { } ModulWindowLayout::~ModulWindowLayout() { disposeOnce(); } void ModulWindowLayout::dispose() { aWatchWindow.disposeAndClear(); aStackWindow.disposeAndClear(); pChild.clear(); Layout::dispose(); } void ModulWindowLayout::UpdateDebug (bool bBasicStopped) { aWatchWindow->UpdateWatches(bBasicStopped); aStackWindow->UpdateCalls(); } void ModulWindowLayout::Paint (vcl::RenderContext& rRenderContext, tools::Rectangle const&) { rRenderContext.DrawText(Point(), IDEResId(RID_STR_NOMODULE)); } void ModulWindowLayout::Activating (BaseWindow& rChild) { assert(dynamic_cast(&rChild)); pChild = &static_cast(rChild); aWatchWindow->Show(); aStackWindow->Show(); rObjectCatalog.Show(); rObjectCatalog.SetLayoutWindow(this); rObjectCatalog.UpdateEntries(); Layout::Activating(rChild); aSyntaxColors.SetActiveEditor(&pChild->GetEditorWindow()); } void ModulWindowLayout::Deactivating () { aSyntaxColors.SetActiveEditor(nullptr); Layout::Deactivating(); aWatchWindow->Hide(); aStackWindow->Hide(); rObjectCatalog.Hide(); pChild = nullptr; } void ModulWindowLayout::GetState (SfxItemSet &rSet, unsigned nWhich) { switch (nWhich) { case SID_SHOW_PROPERTYBROWSER: rSet.Put(SfxVisibilityItem(nWhich, false)); break; case SID_BASICIDE_CHOOSEMACRO: rSet.Put(SfxVisibilityItem(nWhich, true)); break; } } void ModulWindowLayout::BasicAddWatch (OUString const& rWatchStr) { aWatchWindow->AddWatch(rWatchStr); } void ModulWindowLayout::BasicRemoveWatch () { aWatchWindow->RemoveSelectedWatch(); } void ModulWindowLayout::ShowWatchWindow(bool bVisible) { aWatchWindow->Show(bVisible); ArrangeWindows(); } void ModulWindowLayout::ShowStackWindow(bool bVisible) { aStackWindow->Show(bVisible); ArrangeWindows(); } void ModulWindowLayout::OnFirstSize (tools::Long const nWidth, tools::Long const nHeight) { AddToLeft(&rObjectCatalog, Size(nWidth * 0.20, nHeight * 0.75)); AddToBottom(aWatchWindow.get(), Size(nWidth * 0.67, nHeight * 0.25)); AddToBottom(aStackWindow.get(), Size(nWidth * 0.33, nHeight * 0.25)); } ModulWindowLayout::SyntaxColors::SyntaxColors () : pEditor(nullptr) { aConfig.AddListener(this); NewConfig(true); } ModulWindowLayout::SyntaxColors::~SyntaxColors () { aConfig.RemoveListener(this); } // virtual void ModulWindowLayout::SyntaxColors::ConfigurationChanged (utl::ConfigurationBroadcaster*, ConfigurationHints) { NewConfig(false); } // when a new configuration has to be set void ModulWindowLayout::SyntaxColors::NewConfig (bool bFirst) { static struct { TokenType eTokenType; svtools::ColorConfigEntry eEntry; } const vIds[] = { { TokenType::Unknown, svtools::FONTCOLOR }, { TokenType::Identifier, svtools::BASICIDENTIFIER }, { TokenType::Whitespace, svtools::FONTCOLOR }, { TokenType::Number, svtools::BASICNUMBER }, { TokenType::String, svtools::BASICSTRING }, { TokenType::EOL, svtools::FONTCOLOR }, { TokenType::Comment, svtools::BASICCOMMENT }, { TokenType::Error, svtools::BASICERROR }, { TokenType::Operator, svtools::BASICOPERATOR }, { TokenType::Keywords, svtools::BASICKEYWORD }, }; Color aDocColor = aConfig.GetColorValue(svtools::BASICEDITOR).nColor; if (bFirst || aDocColor != m_aBackgroundColor) { m_aBackgroundColor = aDocColor; if (!bFirst && pEditor) { pEditor->SetBackground(Wallpaper(m_aBackgroundColor)); pEditor->Invalidate(); } } Color aFontColor = aConfig.GetColorValue(svtools::FONTCOLOR).nColor; if (bFirst || aFontColor != m_aFontColor) { m_aFontColor = aFontColor; if (!bFirst && pEditor) pEditor->ChangeFontColor(m_aFontColor); } bool bChanged = false; for (const auto& vId: vIds) { Color const aColor = aConfig.GetColorValue(vId.eEntry).nColor; Color& rMyColor = aColors[vId.eTokenType]; if (bFirst || aColor != rMyColor) { rMyColor = aColor; bChanged = true; } } if (bChanged && !bFirst && pEditor) pEditor->UpdateSyntaxHighlighting(); } } // namespace basctl /* vim:set shiftwidth=4 softtabstop=4 expandtab: */