Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / basctl / source / basicide / basobj2.cxx
blob1b64d85aac2890acc4472f5d469fea291c310485
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 <iderdll.hxx>
21 #include "iderdll2.hxx"
22 #include "macrodlg.hxx"
23 #include "moduldlg.hxx"
24 #include <strings.hrc>
25 #include "baside2.hxx"
27 #include <com/sun/star/document/XScriptInvocationContext.hpp>
29 #include <basic/sbmeth.hxx>
30 #include <framework/documentundoguard.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <unotools/moduleoptions.hxx>
33 #include <vcl/weld.hxx>
35 #include <memory>
36 #include <vector>
37 #include <algorithm>
38 #include <basic/basmgr.hxx>
39 namespace basctl
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::container;
46 extern "C" {
47 SAL_DLLPUBLIC_EXPORT rtl_uString* basicide_choose_macro( void* pOnlyInDocument_AsXModel, void* pDocFrame_AsXFrame, sal_Bool bChooseOnly )
49 Reference< frame::XModel > aDocument( static_cast< frame::XModel* >( pOnlyInDocument_AsXModel ) );
50 Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) );
51 OUString aScriptURL = basctl::ChooseMacro( aDocument, aDocFrame, bChooseOnly );
52 rtl_uString* pScriptURL = aScriptURL.pData;
53 rtl_uString_acquire( pScriptURL );
55 return pScriptURL;
57 SAL_DLLPUBLIC_EXPORT void basicide_macro_organizer( sal_Int16 nTabId )
59 SAL_INFO("basctl.basicide","in basicide_macro_organizer");
60 basctl::Organize( nTabId );
64 void Organize( sal_Int16 tabId )
66 EnsureIde();
68 EntryDescriptor aDesc;
69 if (Shell* pShell = GetShell())
70 if (BaseWindow* pCurWin = pShell->GetCurWindow())
71 aDesc = pCurWin->CreateEntryDescriptor();
73 vcl::Window* pParent = Application::GetDefDialogParent();
74 ScopedVclPtrInstance<OrganizeDialog>(pParent, tabId, aDesc)->Execute();
77 bool IsValidSbxName( const OUString& rName )
79 for ( sal_Int32 nChar = 0; nChar < rName.getLength(); nChar++ )
81 sal_Unicode c = rName[nChar];
82 bool bValid = (
83 ( c >= 'A' && c <= 'Z' ) ||
84 ( c >= 'a' && c <= 'z' ) ||
85 ( c >= '0' && c <= '9' && nChar ) ||
86 ( c == '_' )
88 if ( !bValid )
89 return false;
91 return true;
94 static bool StringCompareLessThan( const OUString& rStr1, const OUString& rStr2 )
96 return rStr1.compareToIgnoreAsciiCase( rStr2 ) < 0;
99 Sequence< OUString > GetMergedLibraryNames( const Reference< script::XLibraryContainer >& xModLibContainer, const Reference< script::XLibraryContainer >& xDlgLibContainer )
101 // create a sorted list of module library names
102 std::vector<OUString> aModLibList;
103 if ( xModLibContainer.is() )
105 Sequence< OUString > aModLibNames = xModLibContainer->getElementNames();
106 sal_Int32 nModLibCount = aModLibNames.getLength();
107 const OUString* pModLibNames = aModLibNames.getConstArray();
108 for ( sal_Int32 i = 0 ; i < nModLibCount ; i++ )
109 aModLibList.push_back( pModLibNames[ i ] );
110 std::sort( aModLibList.begin() , aModLibList.end() , StringCompareLessThan );
113 // create a sorted list of dialog library names
114 std::vector<OUString> aDlgLibList;
115 if ( xDlgLibContainer.is() )
117 Sequence< OUString > aDlgLibNames = xDlgLibContainer->getElementNames();
118 sal_Int32 nDlgLibCount = aDlgLibNames.getLength();
119 const OUString* pDlgLibNames = aDlgLibNames.getConstArray();
120 for ( sal_Int32 i = 0 ; i < nDlgLibCount ; i++ )
121 aDlgLibList.push_back( pDlgLibNames[ i ] );
122 std::sort( aDlgLibList.begin() , aDlgLibList.end() , StringCompareLessThan );
125 // merge both lists
126 std::vector<OUString> aLibList( aModLibList.size() + aDlgLibList.size() );
127 std::merge( aModLibList.begin(), aModLibList.end(), aDlgLibList.begin(), aDlgLibList.end(), aLibList.begin(), StringCompareLessThan );
128 std::vector<OUString>::iterator aIterEnd = std::unique( aLibList.begin(), aLibList.end() ); // move unique elements to the front
129 aLibList.erase( aIterEnd, aLibList.end() ); // remove duplicates
131 // copy to sequence
132 sal_Int32 nLibCount = aLibList.size();
133 Sequence< OUString > aSeqLibNames( nLibCount );
134 for ( sal_Int32 i = 0 ; i < nLibCount ; i++ )
135 aSeqLibNames.getArray()[ i ] = aLibList[ i ];
137 return aSeqLibNames;
140 bool RenameModule (
141 weld::Widget* pErrorParent,
142 const ScriptDocument& rDocument,
143 const OUString& rLibName,
144 const OUString& rOldName,
145 const OUString& rNewName
148 if ( !rDocument.hasModule( rLibName, rOldName ) )
150 SAL_WARN( "basctl.basicide","basctl::RenameModule: old module name is invalid!" );
151 return false;
154 if ( rDocument.hasModule( rLibName, rNewName ) )
156 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent,
157 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2)));
158 xError->run();
159 return false;
162 // #i74440
163 if ( rNewName.isEmpty() )
165 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent,
166 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME)));
167 xError->run();
168 return false;
171 if ( !rDocument.renameModule( rLibName, rOldName, rNewName ) )
172 return false;
174 if (Shell* pShell = GetShell())
176 if (VclPtr<ModulWindow> pWin = pShell->FindBasWin(rDocument, rLibName, rNewName, false, true))
178 // set new name in window
179 pWin->SetName( rNewName );
181 // set new module in module window
182 pWin->SetSbModule( pWin->GetBasic()->FindModule( rNewName ) );
184 // update tabwriter
185 sal_uInt16 nId = pShell->GetWindowId( pWin );
186 SAL_WARN_IF( nId == 0 , "basctl.basicide", "No entry in Tabbar!");
187 if ( nId )
189 TabBar& rTabBar = pShell->GetTabBar();
190 rTabBar.SetPageText(nId, rNewName);
191 rTabBar.Sort();
192 rTabBar.MakeVisible(rTabBar.GetCurPageId());
196 return true;
199 namespace
201 struct MacroExecutionData
203 ScriptDocument aDocument;
204 SbMethodRef xMethod;
206 MacroExecutionData()
207 :aDocument( ScriptDocument::NoDocument )
208 ,xMethod( nullptr )
213 class MacroExecution
215 public:
216 DECL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, void );
219 IMPL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, p, void )
221 MacroExecutionData* i_pData = static_cast<MacroExecutionData*>(p);
222 ENSURE_OR_RETURN_VOID( i_pData, "wrong MacroExecutionData" );
223 // take ownership of the data
224 std::unique_ptr< MacroExecutionData > pData( i_pData );
226 SAL_WARN_IF( (pData->xMethod->GetParent()->GetFlags() & SbxFlagBits::ExtSearch) == SbxFlagBits::NONE, "basctl.basicide","No EXTSEARCH!" );
228 // in case this is a document-local macro, try to protect the document's Undo Manager from
229 // flawed scripts
230 std::unique_ptr< ::framework::DocumentUndoGuard > pUndoGuard;
231 if ( pData->aDocument.isDocument() )
232 pUndoGuard.reset( new ::framework::DocumentUndoGuard( pData->aDocument.getDocument() ) );
234 RunMethod( pData->xMethod.get() );
238 OUString ChooseMacro( const uno::Reference< frame::XModel >& rxLimitToDocument,
239 const uno::Reference< frame::XFrame >& xDocFrame,
240 bool bChooseOnly )
242 EnsureIde();
244 GetExtraData()->ChoosingMacro() = true;
246 OUString aScriptURL;
247 SbMethod* pMethod = nullptr;
249 ScopedVclPtrInstance< MacroChooser > pChooser( nullptr, xDocFrame, true );
250 if ( bChooseOnly || !SvtModuleOptions::IsBasicIDE() )
251 pChooser->SetMode(MacroChooser::ChooseOnly);
253 if ( !bChooseOnly && rxLimitToDocument.is() )
254 // Hack!
255 pChooser->SetMode(MacroChooser::Recording);
257 short nRetValue = pChooser->Execute();
259 GetExtraData()->ChoosingMacro() = false;
261 switch ( nRetValue )
263 case Macro_OkRun:
265 bool bError = false;
267 pMethod = pChooser->GetMacro();
268 if ( !pMethod && pChooser->GetMode() == MacroChooser::Recording )
269 pMethod = pChooser->CreateMacro();
271 if ( !pMethod )
272 break;
274 SbModule* pModule = pMethod->GetModule();
275 if ( !pModule )
277 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Module found!" );
278 break;
281 StarBASIC* pBasic = dynamic_cast<StarBASIC*>(pModule->GetParent());
282 if ( !pBasic )
284 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Basic found!" );
285 break;
288 BasicManager* pBasMgr = FindBasicManager( pBasic );
289 if ( !pBasMgr )
291 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No BasicManager found!" );
292 break;
295 // name
296 OUString aName = pBasic->GetName() + "." + pModule->GetName() + "." + pMethod->GetName();
298 // location
299 OUString aLocation;
300 ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) );
301 if ( aDocument.isDocument() )
303 // document basic
304 aLocation = "document" ;
306 if ( rxLimitToDocument.is() )
308 uno::Reference< frame::XModel > xLimitToDocument( rxLimitToDocument );
310 uno::Reference< document::XEmbeddedScripts > xScripts( rxLimitToDocument, UNO_QUERY );
311 if ( !xScripts.is() )
312 { // the document itself does not support embedding scripts
313 uno::Reference< document::XScriptInvocationContext > xContext( rxLimitToDocument, UNO_QUERY );
314 if ( xContext.is() )
315 xScripts = xContext->getScriptContainer();
316 if ( xScripts.is() )
317 { // but it is able to refer to a document which actually does support this
318 xLimitToDocument.set( xScripts, UNO_QUERY );
319 if ( !xLimitToDocument.is() )
321 SAL_WARN_IF(!xLimitToDocument.is(), "basctl.basicide", "basctl::ChooseMacro: a script container which is no document!?" );
322 xLimitToDocument = rxLimitToDocument;
327 if ( xLimitToDocument != aDocument.getDocument() )
329 // error
330 bError = true;
331 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(nullptr,
332 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_ERRORCHOOSEMACRO)));
333 xError->run();
337 else
339 // application basic
340 aLocation = "application" ;
343 // script URL
344 if ( !bError )
346 aScriptURL = "vnd.sun.star.script:" + aName + "?language=Basic&location=" + aLocation;
349 if ( !rxLimitToDocument.is() )
351 MacroExecutionData* pExecData = new MacroExecutionData;
352 pExecData->aDocument = aDocument;
353 pExecData->xMethod = pMethod; // keep alive until the event has been processed
354 Application::PostUserEvent( LINK( nullptr, MacroExecution, ExecuteMacroEvent ), pExecData );
357 break;
360 return aScriptURL;
363 Sequence< OUString > GetMethodNames( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName )
365 Sequence< OUString > aSeqMethods;
367 // get module
368 OUString aOUSource;
369 if ( rDocument.getModule( rLibName, rModName, aOUSource ) )
371 BasicManager* pBasMgr = rDocument.getBasicManager();
372 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr;
373 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr;
375 SbModuleRef xModule;
376 // Only reparse modules if ScriptDocument source is out of sync
377 // with basic's Module
378 if ( !pMod || pMod->GetSource() != aOUSource )
380 xModule = new SbModule( rModName );
381 xModule->SetSource32( aOUSource );
382 pMod = xModule.get();
385 sal_uInt16 nCount = pMod->GetMethods()->Count();
386 sal_uInt16 nRealCount = nCount;
387 for ( sal_uInt16 i = 0; i < nCount; i++ )
389 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get( i ));
390 if( pMethod->IsHidden() )
391 --nRealCount;
393 aSeqMethods.realloc( nRealCount );
395 sal_uInt16 iTarget = 0;
396 for ( sal_uInt16 i = 0 ; i < nCount; ++i )
398 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get( i ));
399 if( pMethod->IsHidden() )
400 continue;
401 SAL_WARN_IF( !pMethod, "basctl.basicide","Method not found! (NULL)" );
402 aSeqMethods.getArray()[ iTarget++ ] = pMethod->GetName();
406 return aSeqMethods;
409 bool HasMethod (
410 ScriptDocument const& rDocument,
411 OUString const& rLibName,
412 OUString const& rModName,
413 OUString const& rMethName
416 bool bHasMethod = false;
418 OUString aOUSource;
419 if ( rDocument.hasModule( rLibName, rModName ) && rDocument.getModule( rLibName, rModName, aOUSource ) )
421 // Check if we really need to scan the source ( again )
422 BasicManager* pBasMgr = rDocument.getBasicManager();
423 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr;
424 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr;
425 SbModuleRef xModule;
426 // Only reparse modules if ScriptDocument source is out of sync
427 // with basic's Module
428 if ( !pMod || pMod->GetSource() != aOUSource )
430 xModule = new SbModule( rModName );
431 xModule->SetSource32( aOUSource );
432 pMod = xModule.get();
434 SbxArray* pMethods = pMod->GetMethods().get();
435 if ( pMethods )
437 SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Find( rMethName, SbxClassType::Method ));
438 if ( pMethod && !pMethod->IsHidden() )
439 bHasMethod = true;
443 return bHasMethod;
446 } // namespace basctl
448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */