1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <iderdll.hxx>
21 #include "iderdll2.hxx"
22 #include "macrodlg.hxx"
23 #include "moduldlg.hxx"
24 #include <strings.hrc>
25 #include "baside2.hxx"
26
27 #include <com/sun/star/document/XScriptInvocationContext.hpp>
28
29 #include <basic/sbmeth.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/string.hxx>
32 #include <comphelper/sequence.hxx>
33 #include <framework/documentundoguard.hxx>
34 #include <sal/log.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <unotools/moduleoptions.hxx>
37 #include <vcl/settings.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/weld.hxx>
40
41 #include <memory>
42 #include <vector>
43 #include <algorithm>
44 #include <basic/basmgr.hxx>
45 namespace basctl
46 {
47
48 using namespace ::com::sun::star;
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::container;
51
52 extern "C" {
basicide_choose_macro(void * pParent,void * pOnlyInDocument_AsXModel,void * pDocFrame_AsXFrame,sal_Bool bChooseOnly)53 SAL_DLLPUBLIC_EXPORT rtl_uString* basicide_choose_macro(void* pParent, void* pOnlyInDocument_AsXModel, void* pDocFrame_AsXFrame, sal_Bool bChooseOnly )
54 {
55 Reference< frame::XModel > aDocument( static_cast< frame::XModel* >( pOnlyInDocument_AsXModel ) );
56 Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) );
57 OUString aScriptURL = basctl::ChooseMacro(static_cast<weld::Window*>(pParent), aDocument, aDocFrame, bChooseOnly);
58 rtl_uString* pScriptURL = aScriptURL.pData;
59 rtl_uString_acquire( pScriptURL );
60
61 return pScriptURL;
62 }
basicide_macro_organizer(void * pParent,sal_Int16 nTabId)63 SAL_DLLPUBLIC_EXPORT void basicide_macro_organizer(void *pParent, sal_Int16 nTabId)
64 {
65 SAL_INFO("basctl.basicide","in basicide_macro_organizer");
66 basctl::Organize(static_cast<weld::Window*>(pParent), nTabId);
67 }
68 }
69
Organize(weld::Window * pParent,sal_Int16 tabId)70 void Organize(weld::Window* pParent, sal_Int16 tabId)
71 {
72 EnsureIde();
73
74 auto xDlg(std::make_shared<OrganizeDialog>(pParent, tabId));
75 weld::DialogController::runAsync(xDlg, [](int) {});
76 }
77
IsValidSbxName(const OUString & rName)78 bool IsValidSbxName( const OUString& rName )
79 {
80 for ( sal_Int32 nChar = 0; nChar < rName.getLength(); nChar++ )
81 {
82 sal_Unicode c = rName[nChar];
83 bool bValid = (
84 ( c >= 'A' && c <= 'Z' ) ||
85 ( c >= 'a' && c <= 'z' ) ||
86 ( c >= '0' && c <= '9' && nChar ) ||
87 ( c == '_' )
88 );
89 if ( !bValid )
90 return false;
91 }
92 return true;
93 }
94
GetMergedLibraryNames(const Reference<script::XLibraryContainer> & xModLibContainer,const Reference<script::XLibraryContainer> & xDlgLibContainer)95 Sequence< OUString > GetMergedLibraryNames( const Reference< script::XLibraryContainer >& xModLibContainer, const Reference< script::XLibraryContainer >& xDlgLibContainer )
96 {
97 // create a list of module library names
98 std::vector<OUString> aLibList;
99 if ( xModLibContainer.is() )
100 {
101 Sequence< OUString > aModLibNames = xModLibContainer->getElementNames();
102 sal_Int32 nModLibCount = aModLibNames.getLength();
103 const OUString* pModLibNames = aModLibNames.getConstArray();
104 for ( sal_Int32 i = 0 ; i < nModLibCount ; i++ )
105 aLibList.push_back( pModLibNames[ i ] );
106 }
107
108 // create a list of dialog library names
109 if ( xDlgLibContainer.is() )
110 {
111 Sequence< OUString > aDlgLibNames = xDlgLibContainer->getElementNames();
112 sal_Int32 nDlgLibCount = aDlgLibNames.getLength();
113 const OUString* pDlgLibNames = aDlgLibNames.getConstArray();
114 for ( sal_Int32 i = 0 ; i < nDlgLibCount ; i++ )
115 aLibList.push_back( pDlgLibNames[ i ] );
116 }
117
118 // sort list
119 auto const sort = comphelper::string::NaturalStringSorter(
120 comphelper::getProcessComponentContext(),
121 Application::GetSettings().GetUILanguageTag().getLocale());
122 std::sort(aLibList.begin(), aLibList.end(),
123 [&sort](const OUString& rLHS, const OUString& rRHS) {
124 return sort.compare(rLHS, rRHS) < 0;
125 });
126 // remove duplicates
127 std::vector<OUString>::iterator aIterEnd = std::unique( aLibList.begin(), aLibList.end() );
128 aLibList.erase( aIterEnd, aLibList.end() );
129
130 return comphelper::containerToSequence(aLibList);
131 }
132
RenameModule(weld::Widget * pErrorParent,const ScriptDocument & rDocument,const OUString & rLibName,const OUString & rOldName,const OUString & rNewName)133 bool RenameModule (
134 weld::Widget* pErrorParent,
135 const ScriptDocument& rDocument,
136 const OUString& rLibName,
137 const OUString& rOldName,
138 const OUString& rNewName
139 )
140 {
141 if ( !rDocument.hasModule( rLibName, rOldName ) )
142 {
143 SAL_WARN( "basctl.basicide","basctl::RenameModule: old module name is invalid!" );
144 return false;
145 }
146
147 if ( rDocument.hasModule( rLibName, rNewName ) )
148 {
149 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent,
150 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2)));
151 xError->run();
152 return false;
153 }
154
155 // #i74440
156 if ( rNewName.isEmpty() )
157 {
158 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent,
159 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME)));
160 xError->run();
161 return false;
162 }
163
164 if ( !rDocument.renameModule( rLibName, rOldName, rNewName ) )
165 return false;
166
167 if (Shell* pShell = GetShell())
168 {
169 if (VclPtr<ModulWindow> pWin = pShell->FindBasWin(rDocument, rLibName, rNewName, false, true))
170 {
171 // set new name in window
172 pWin->SetName( rNewName );
173
174 // set new module in module window
175 pWin->SetSbModule( pWin->GetBasic()->FindModule( rNewName ) );
176
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 )
181 {
182 TabBar& rTabBar = pShell->GetTabBar();
183 rTabBar.SetPageText(nId, rNewName);
184 rTabBar.Sort();
185 rTabBar.MakeVisible(rTabBar.GetCurPageId());
186 }
187 }
188 }
189 return true;
190 }
191
192 namespace
193 {
194 struct MacroExecutionData
195 {
196 ScriptDocument aDocument;
197 SbMethodRef xMethod;
198
MacroExecutionDatabasctl::__anon76db6ed40311::MacroExecutionData199 MacroExecutionData()
200 :aDocument( ScriptDocument::NoDocument )
201 {
202 }
203 };
204
205 class MacroExecution
206 {
207 public:
208 DECL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, void );
209 };
210
IMPL_STATIC_LINK(MacroExecution,ExecuteMacroEvent,void *,p,void)211 IMPL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, p, void )
212 {
213 MacroExecutionData* i_pData = static_cast<MacroExecutionData*>(p);
214 ENSURE_OR_RETURN_VOID( i_pData, "wrong MacroExecutionData" );
215 // take ownership of the data
216 std::unique_ptr< MacroExecutionData > pData( i_pData );
217
218 SAL_WARN_IF( (pData->xMethod->GetParent()->GetFlags() & SbxFlagBits::ExtSearch) == SbxFlagBits::NONE, "basctl.basicide","No EXTSEARCH!" );
219
220 // in case this is a document-local macro, try to protect the document's Undo Manager from
221 // flawed scripts
222 std::unique_ptr< ::framework::DocumentUndoGuard > pUndoGuard;
223 if ( pData->aDocument.isDocument() )
224 pUndoGuard.reset( new ::framework::DocumentUndoGuard( pData->aDocument.getDocument() ) );
225
226 RunMethod( pData->xMethod.get() );
227 }
228 }
229
ChooseMacro(weld::Window * pParent,const uno::Reference<frame::XModel> & rxLimitToDocument,const uno::Reference<frame::XFrame> & xDocFrame,bool bChooseOnly)230 OUString ChooseMacro(weld::Window* pParent,
231 const uno::Reference< frame::XModel >& rxLimitToDocument,
232 const uno::Reference< frame::XFrame >& xDocFrame,
233 bool bChooseOnly)
234 {
235 EnsureIde();
236
237 GetExtraData()->ChoosingMacro() = true;
238
239 OUString aScriptURL;
240 SbMethod* pMethod = nullptr;
241
242 MacroChooser aChooser(pParent, xDocFrame);
243 if ( bChooseOnly || !SvtModuleOptions::IsBasicIDE() )
244 aChooser.SetMode(MacroChooser::ChooseOnly);
245
246 if ( !bChooseOnly && rxLimitToDocument.is() )
247 {
248 // Hack!
249 aChooser.SetMode(MacroChooser::Recording);
250 }
251
252 short nRetValue = aChooser.run();
253
254 GetExtraData()->ChoosingMacro() = false;
255
256 switch ( nRetValue )
257 {
258 case Macro_OkRun:
259 {
260 bool bError = false;
261
262 pMethod = aChooser.GetMacro();
263 if ( !pMethod && aChooser.GetMode() == MacroChooser::Recording )
264 pMethod = aChooser.CreateMacro();
265
266 if ( !pMethod )
267 break;
268
269 SbModule* pModule = pMethod->GetModule();
270 if ( !pModule )
271 {
272 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Module found!" );
273 break;
274 }
275
276 StarBASIC* pBasic = dynamic_cast<StarBASIC*>(pModule->GetParent());
277 if ( !pBasic )
278 {
279 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Basic found!" );
280 break;
281 }
282
283 BasicManager* pBasMgr = FindBasicManager( pBasic );
284 if ( !pBasMgr )
285 {
286 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No BasicManager found!" );
287 break;
288 }
289
290 // name
291 OUString aName = pBasic->GetName() + "." + pModule->GetName() + "." + pMethod->GetName();
292
293 // location
294 OUString aLocation;
295 ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) );
296 if ( aDocument.isDocument() )
297 {
298 // document basic
299 aLocation = "document" ;
300
301 if ( rxLimitToDocument.is() )
302 {
303 uno::Reference< frame::XModel > xLimitToDocument( rxLimitToDocument );
304
305 uno::Reference< document::XEmbeddedScripts > xScripts( rxLimitToDocument, UNO_QUERY );
306 if ( !xScripts.is() )
307 { // the document itself does not support embedding scripts
308 uno::Reference< document::XScriptInvocationContext > xContext( rxLimitToDocument, UNO_QUERY );
309 if ( xContext.is() )
310 xScripts = xContext->getScriptContainer();
311 if ( xScripts.is() )
312 { // but it is able to refer to a document which actually does support this
313 xLimitToDocument.set( xScripts, UNO_QUERY );
314 if ( !xLimitToDocument.is() )
315 {
316 SAL_WARN_IF(!xLimitToDocument.is(), "basctl.basicide", "basctl::ChooseMacro: a script container which is no document!?" );
317 xLimitToDocument = rxLimitToDocument;
318 }
319 }
320 }
321
322 if ( xLimitToDocument != aDocument.getDocument() )
323 {
324 // error
325 bError = true;
326 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(nullptr,
327 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_ERRORCHOOSEMACRO)));
328 xError->run();
329 }
330 }
331 }
332 else
333 {
334 // application basic
335 aLocation = "application" ;
336 }
337
338 // script URL
339 if ( !bError )
340 {
341 aScriptURL = "vnd.sun.star.script:" + aName + "?language=Basic&location=" + aLocation;
342 }
343
344 if ( !rxLimitToDocument.is() )
345 {
346 MacroExecutionData* pExecData = new MacroExecutionData;
347 pExecData->aDocument = aDocument;
348 pExecData->xMethod = pMethod; // keep alive until the event has been processed
349 Application::PostUserEvent( LINK( nullptr, MacroExecution, ExecuteMacroEvent ), pExecData );
350 }
351 }
352 break;
353 }
354
355 return aScriptURL;
356 }
357
GetMethodNames(const ScriptDocument & rDocument,const OUString & rLibName,const OUString & rModName)358 Sequence< OUString > GetMethodNames( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName )
359 {
360 Sequence< OUString > aSeqMethods;
361
362 // get module
363 OUString aOUSource;
364 if ( rDocument.getModule( rLibName, rModName, aOUSource ) )
365 {
366 BasicManager* pBasMgr = rDocument.getBasicManager();
367 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr;
368 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr;
369
370 SbModuleRef xModule;
371 // Only reparse modules if ScriptDocument source is out of sync
372 // with basic's Module
373 if ( !pMod || pMod->GetSource() != aOUSource )
374 {
375 xModule = new SbModule( rModName );
376 xModule->SetSource32( aOUSource );
377 pMod = xModule.get();
378 }
379
380 sal_uInt16 nCount = pMod->GetMethods()->Count();
381 sal_uInt16 nRealCount = nCount;
382 for ( sal_uInt16 i = 0; i < nCount; i++ )
383 {
384 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get( i ));
385 if( pMethod->IsHidden() )
386 --nRealCount;
387 }
388 aSeqMethods.realloc( nRealCount );
389
390 sal_uInt16 iTarget = 0;
391 for ( sal_uInt16 i = 0 ; i < nCount; ++i )
392 {
393 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get( i ));
394 if( pMethod->IsHidden() )
395 continue;
396 SAL_WARN_IF( !pMethod, "basctl.basicide","Method not found! (NULL)" );
397 aSeqMethods.getArray()[ iTarget++ ] = pMethod->GetName();
398 }
399 }
400
401 return aSeqMethods;
402 }
403
HasMethod(ScriptDocument const & rDocument,OUString const & rLibName,OUString const & rModName,OUString const & rMethName)404 bool HasMethod (
405 ScriptDocument const& rDocument,
406 OUString const& rLibName,
407 OUString const& rModName,
408 OUString const& rMethName
409 )
410 {
411 bool bHasMethod = false;
412
413 OUString aOUSource;
414 if ( rDocument.hasModule( rLibName, rModName ) && rDocument.getModule( rLibName, rModName, aOUSource ) )
415 {
416 // Check if we really need to scan the source ( again )
417 BasicManager* pBasMgr = rDocument.getBasicManager();
418 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr;
419 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr;
420 SbModuleRef xModule;
421 // Only reparse modules if ScriptDocument source is out of sync
422 // with basic's Module
423 if ( !pMod || pMod->GetSource() != aOUSource )
424 {
425 xModule = new SbModule( rModName );
426 xModule->SetSource32( aOUSource );
427 pMod = xModule.get();
428 }
429 SbxArray* pMethods = pMod->GetMethods().get();
430 if ( pMethods )
431 {
432 SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Find( rMethName, SbxClassType::Method ));
433 if ( pMethod && !pMethod->IsHidden() )
434 bHasMethod = true;
435 }
436 }
437
438 return bHasMethod;
439 }
440
441 } // namespace basctl
442
443 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
444