Bump version to 24.04.3.4
[LibreOffice.git] / basctl / source / basicide / basobj2.cxx
blob708b1ce035d21df680aff0e753d8927e07a4a452
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 <basidesh.hxx>
21 #include <iderdll.hxx>
22 #include "iderdll2.hxx"
23 #include "macrodlg.hxx"
24 #include "moduldlg.hxx"
25 #include <iderid.hxx>
26 #include <strings.hrc>
27 #include "baside2.hxx"
29 #include <com/sun/star/document/XScriptInvocationContext.hpp>
31 #include <basic/sbmeth.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <comphelper/string.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <framework/documentundoguard.hxx>
36 #include <sal/log.hxx>
37 #include <comphelper/diagnose_ex.hxx>
38 #include <unotools/moduleoptions.hxx>
39 #include <vcl/settings.hxx>
40 #include <vcl/svapp.hxx>
41 #include <vcl/weld.hxx>
43 #include <memory>
44 #include <vector>
45 #include <algorithm>
46 #include <basic/basmgr.hxx>
47 namespace basctl
50 using namespace ::com::sun::star;
51 using namespace ::com::sun::star::uno;
52 using namespace ::com::sun::star::container;
54 extern "C" {
55 SAL_DLLPUBLIC_EXPORT rtl_uString* basicide_choose_macro(void* pParent, void* pOnlyInDocument_AsXModel, void* pDocFrame_AsXFrame, sal_Bool bChooseOnly )
57 Reference< frame::XModel > aDocument( static_cast< frame::XModel* >( pOnlyInDocument_AsXModel ) );
58 Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) );
59 OUString aScriptURL = basctl::ChooseMacro(static_cast<weld::Window*>(pParent), aDocument, aDocFrame, bChooseOnly);
60 rtl_uString* pScriptURL = aScriptURL.pData;
61 rtl_uString_acquire( pScriptURL );
63 return pScriptURL;
65 SAL_DLLPUBLIC_EXPORT void basicide_macro_organizer(void *pParent, void* pDocFrame_AsXFrame, sal_Int16 nTabId)
67 SAL_INFO("basctl.basicide","in basicide_macro_organizer");
68 Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) );
69 basctl::Organize(static_cast<weld::Window*>(pParent), aDocFrame, nTabId);
73 void Organize(weld::Window* pParent, const css::uno::Reference<css::frame::XFrame>& xDocFrame, sal_Int16 tabId)
75 EnsureIde();
77 auto xDlg(std::make_shared<OrganizeDialog>(pParent, xDocFrame, tabId));
78 weld::DialogController::runAsync(xDlg, [](int) {});
81 bool IsValidSbxName( std::u16string_view rName )
83 for ( size_t nChar = 0; nChar < rName.size(); nChar++ )
85 sal_Unicode c = rName[nChar];
86 bool bValid = (
87 ( c >= 'A' && c <= 'Z' ) ||
88 ( c >= 'a' && c <= 'z' ) ||
89 ( c >= '0' && c <= '9' && nChar ) ||
90 ( c == '_' )
92 if ( !bValid )
93 return false;
95 return true;
98 Sequence< OUString > GetMergedLibraryNames( const Reference< script::XLibraryContainer >& xModLibContainer, const Reference< script::XLibraryContainer >& xDlgLibContainer )
100 // create a list of module library names
101 std::vector<OUString> aLibList;
102 if ( xModLibContainer.is() )
104 const Sequence< OUString > aModLibNames = xModLibContainer->getElementNames();
105 aLibList.insert( aLibList.end(), aModLibNames.begin(), aModLibNames.end() );
108 // create a list of dialog library names
109 if ( xDlgLibContainer.is() )
111 const Sequence< OUString > aDlgLibNames = xDlgLibContainer->getElementNames();
112 aLibList.insert( aLibList.end(), aDlgLibNames.begin(), aDlgLibNames.end() );
115 // sort list
116 auto const sort = comphelper::string::NaturalStringSorter(
117 comphelper::getProcessComponentContext(),
118 Application::GetSettings().GetUILanguageTag().getLocale());
119 std::sort(aLibList.begin(), aLibList.end(),
120 [&sort](const OUString& rLHS, const OUString& rRHS) {
121 return sort.compare(rLHS, rRHS) < 0;
123 // remove duplicates
124 std::vector<OUString>::iterator aIterEnd = std::unique( aLibList.begin(), aLibList.end() );
125 aLibList.erase( aIterEnd, aLibList.end() );
127 return comphelper::containerToSequence(aLibList);
130 bool RenameModule (
131 weld::Widget* pErrorParent,
132 const ScriptDocument& rDocument,
133 const OUString& rLibName,
134 const OUString& rOldName,
135 const OUString& rNewName
138 if ( !rDocument.hasModule( rLibName, rOldName ) )
140 SAL_WARN( "basctl.basicide","basctl::RenameModule: old module name is invalid!" );
141 return false;
144 if ( rDocument.hasModule( rLibName, rNewName ) )
146 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent,
147 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2)));
148 xError->run();
149 return false;
152 // #i74440
153 if ( rNewName.isEmpty() )
155 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent,
156 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME)));
157 xError->run();
158 return false;
161 if ( !rDocument.renameModule( rLibName, rOldName, rNewName ) )
162 return false;
164 Shell* pShell = GetShell();
165 if (!pShell)
166 return true;
167 VclPtr<ModulWindow> pWin = pShell->FindBasWin(rDocument, rLibName, rNewName, false, true);
168 if (!pWin)
169 return true;
171 // set new name in window
172 pWin->SetName( rNewName );
174 // set new module in module window
175 pWin->SetSbModule( pWin->GetBasic()->FindModule( rNewName ) );
177 // update tabwriter
178 sal_uInt16 nId = pShell->GetWindowId( pWin );
179 SAL_WARN_IF( nId == 0 , "basctl.basicide", "No entry in Tabbar!");
180 if ( nId )
182 TabBar& rTabBar = pShell->GetTabBar();
183 rTabBar.SetPageText(nId, rNewName);
184 rTabBar.Sort();
185 rTabBar.MakeVisible(rTabBar.GetCurPageId());
187 return true;
190 namespace
192 struct MacroExecutionData
194 ScriptDocument aDocument;
195 SbMethodRef xMethod;
197 MacroExecutionData()
198 :aDocument( ScriptDocument::NoDocument )
203 class MacroExecution
205 public:
206 DECL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, void );
209 IMPL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, p, void )
211 MacroExecutionData* i_pData = static_cast<MacroExecutionData*>(p);
212 ENSURE_OR_RETURN_VOID( i_pData, "wrong MacroExecutionData" );
213 // take ownership of the data
214 std::unique_ptr< MacroExecutionData > pData( i_pData );
216 SAL_WARN_IF( (pData->xMethod->GetParent()->GetFlags() & SbxFlagBits::ExtSearch) == SbxFlagBits::NONE, "basctl.basicide","No EXTSEARCH!" );
218 // in case this is a document-local macro, try to protect the document's Undo Manager from
219 // flawed scripts
220 std::optional< ::framework::DocumentUndoGuard > pUndoGuard;
221 if ( pData->aDocument.isDocument() )
222 pUndoGuard.emplace( pData->aDocument.getDocument() );
224 RunMethod( pData->xMethod.get() );
228 OUString ChooseMacro(weld::Window* pParent,
229 const uno::Reference< frame::XModel >& rxLimitToDocument,
230 const uno::Reference< frame::XFrame >& xDocFrame,
231 bool bChooseOnly)
233 EnsureIde();
235 GetExtraData()->ChoosingMacro() = true;
237 OUString aScriptURL;
238 SbMethod* pMethod = nullptr;
240 MacroChooser aChooser(pParent, xDocFrame);
241 if ( bChooseOnly || !SvtModuleOptions::IsBasicIDE() )
242 aChooser.SetMode(MacroChooser::ChooseOnly);
244 if ( !bChooseOnly && rxLimitToDocument.is() )
246 // Hack!
247 aChooser.SetMode(MacroChooser::Recording);
250 short nRetValue = aChooser.run();
252 GetExtraData()->ChoosingMacro() = false;
254 switch ( nRetValue )
256 case Macro_OkRun:
258 bool bError = false;
260 pMethod = aChooser.GetMacro();
261 if ( !pMethod && aChooser.GetMode() == MacroChooser::Recording )
262 pMethod = aChooser.CreateMacro();
264 if ( !pMethod )
265 break;
267 SbModule* pModule = pMethod->GetModule();
268 if ( !pModule )
270 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Module found!" );
271 break;
274 StarBASIC* pBasic = dynamic_cast<StarBASIC*>(pModule->GetParent());
275 if ( !pBasic )
277 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Basic found!" );
278 break;
281 BasicManager* pBasMgr = FindBasicManager( pBasic );
282 if ( !pBasMgr )
284 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No BasicManager found!" );
285 break;
288 // name
289 OUString aName = pBasic->GetName() + "." + pModule->GetName() + "." + pMethod->GetName();
291 // location
292 OUString aLocation;
293 ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) );
294 if ( aDocument.isDocument() )
296 // document basic
297 aLocation = "document" ;
299 if ( rxLimitToDocument.is() )
301 uno::Reference< frame::XModel > xLimitToDocument( rxLimitToDocument );
303 uno::Reference< document::XEmbeddedScripts > xScripts( rxLimitToDocument, UNO_QUERY );
304 if ( !xScripts.is() )
305 { // the document itself does not support embedding scripts
306 uno::Reference< document::XScriptInvocationContext > xContext( rxLimitToDocument, UNO_QUERY );
307 if ( xContext.is() )
308 xScripts = xContext->getScriptContainer();
309 if ( xScripts.is() )
310 { // but it is able to refer to a document which actually does support this
311 xLimitToDocument.set( xScripts, UNO_QUERY );
312 if ( !xLimitToDocument.is() )
314 SAL_WARN_IF(!xLimitToDocument.is(), "basctl.basicide", "basctl::ChooseMacro: a script container which is no document!?" );
315 xLimitToDocument = rxLimitToDocument;
320 if ( xLimitToDocument != aDocument.getDocument() )
322 // error
323 bError = true;
324 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(nullptr,
325 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_ERRORCHOOSEMACRO)));
326 xError->run();
330 else
332 // application basic
333 aLocation = "application" ;
336 // script URL
337 if ( !bError )
339 aScriptURL = "vnd.sun.star.script:" + aName + "?language=Basic&location=" + aLocation;
342 if ( !rxLimitToDocument.is() )
344 MacroExecutionData* pExecData = new MacroExecutionData;
345 pExecData->aDocument = aDocument;
346 pExecData->xMethod = pMethod; // keep alive until the event has been processed
347 Application::PostUserEvent( LINK( nullptr, MacroExecution, ExecuteMacroEvent ), pExecData );
350 break;
353 return aScriptURL;
356 Sequence< OUString > GetMethodNames( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName )
358 Sequence< OUString > aSeqMethods;
360 // get module
361 OUString aOUSource;
362 if ( rDocument.getModule( rLibName, rModName, aOUSource ) )
364 BasicManager* pBasMgr = rDocument.getBasicManager();
365 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr;
366 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr;
368 SbModuleRef xModule;
369 // Only reparse modules if ScriptDocument source is out of sync
370 // with basic's Module
371 if ( !pMod || pMod->GetSource32() != aOUSource )
373 xModule = new SbModule( rModName );
374 xModule->SetSource32( aOUSource );
375 pMod = xModule.get();
378 sal_uInt32 nCount = pMod->GetMethods()->Count();
379 sal_uInt32 nRealCount = nCount;
380 for ( sal_uInt32 i = 0; i < nCount; i++ )
382 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get(i));
383 if( pMethod->IsHidden() )
384 --nRealCount;
386 aSeqMethods.realloc( nRealCount );
388 sal_uInt32 iTarget = 0;
389 for ( sal_uInt32 i = 0 ; i < nCount; ++i )
391 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get(i));
392 if( pMethod->IsHidden() )
393 continue;
394 SAL_WARN_IF( !pMethod, "basctl.basicide","Method not found! (NULL)" );
395 aSeqMethods.getArray()[ iTarget++ ] = pMethod->GetName();
399 return aSeqMethods;
402 bool HasMethod (
403 ScriptDocument const& rDocument,
404 OUString const& rLibName,
405 OUString const& rModName,
406 OUString const& rMethName
409 bool bHasMethod = false;
411 OUString aOUSource;
412 if ( rDocument.hasModule( rLibName, rModName ) && rDocument.getModule( rLibName, rModName, aOUSource ) )
414 // Check if we really need to scan the source ( again )
415 BasicManager* pBasMgr = rDocument.getBasicManager();
416 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr;
417 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr;
418 SbModuleRef xModule;
419 // Only reparse modules if ScriptDocument source is out of sync
420 // with basic's Module
421 if ( !pMod || pMod->GetSource32() != aOUSource )
423 xModule = new SbModule( rModName );
424 xModule->SetSource32( aOUSource );
425 pMod = xModule.get();
427 SbxArray* pMethods = pMod->GetMethods().get();
428 if ( pMethods )
430 SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Find( rMethName, SbxClassType::Method ));
431 if ( pMethod && !pMethod->IsHidden() )
432 bHasMethod = true;
436 return bHasMethod;
439 } // namespace basctl
441 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */