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 <framework/documentundoguard.hxx> 31 #include <sal/log.hxx> 32 #include <tools/diagnose_ex.h> 33 #include <unotools/moduleoptions.hxx> 34 #include <vcl/weld.hxx> 35 36 #include <memory> 37 #include <vector> 38 #include <algorithm> 39 #include <basic/basmgr.hxx> 40 namespace basctl 41 { 42 43 using namespace ::com::sun::star; 44 using namespace ::com::sun::star::uno; 45 using namespace ::com::sun::star::container; 46 47 extern "C" { 48 SAL_DLLPUBLIC_EXPORT rtl_uString* basicide_choose_macro( void* pOnlyInDocument_AsXModel, void* pDocFrame_AsXFrame, sal_Bool bChooseOnly ) 49 { 50 Reference< frame::XModel > aDocument( static_cast< frame::XModel* >( pOnlyInDocument_AsXModel ) ); 51 Reference< frame::XFrame > aDocFrame( static_cast< frame::XFrame* >( pDocFrame_AsXFrame ) ); 52 OUString aScriptURL = basctl::ChooseMacro( aDocument, aDocFrame, bChooseOnly ); 53 rtl_uString* pScriptURL = aScriptURL.pData; 54 rtl_uString_acquire( pScriptURL ); 55 56 return pScriptURL; 57 } 58 SAL_DLLPUBLIC_EXPORT void basicide_macro_organizer( sal_Int16 nTabId ) 59 { 60 SAL_INFO("basctl.basicide","in basicide_macro_organizer"); 61 basctl::Organize( nTabId ); 62 } 63 } 64 65 void Organize( sal_Int16 tabId ) 66 { 67 EnsureIde(); 68 69 EntryDescriptor aDesc; 70 if (Shell* pShell = GetShell()) 71 if (BaseWindow* pCurWin = pShell->GetCurWindow()) 72 aDesc = pCurWin->CreateEntryDescriptor(); 73 74 vcl::Window* pParent = Application::GetDefDialogParent(); 75 ScopedVclPtrInstance<OrganizeDialog>(pParent, tabId, aDesc)->Execute(); 76 } 77 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 95 static bool StringCompareLessThan( const OUString& rStr1, const OUString& rStr2 ) 96 { 97 return rStr1.compareToIgnoreAsciiCase( rStr2 ) < 0; 98 } 99 100 Sequence< OUString > GetMergedLibraryNames( const Reference< script::XLibraryContainer >& xModLibContainer, const Reference< script::XLibraryContainer >& xDlgLibContainer ) 101 { 102 // create a sorted list of module library names 103 std::vector<OUString> aModLibList; 104 if ( xModLibContainer.is() ) 105 { 106 Sequence< OUString > aModLibNames = xModLibContainer->getElementNames(); 107 sal_Int32 nModLibCount = aModLibNames.getLength(); 108 const OUString* pModLibNames = aModLibNames.getConstArray(); 109 for ( sal_Int32 i = 0 ; i < nModLibCount ; i++ ) 110 aModLibList.push_back( pModLibNames[ i ] ); 111 std::sort( aModLibList.begin() , aModLibList.end() , StringCompareLessThan ); 112 } 113 114 // create a sorted list of dialog library names 115 std::vector<OUString> aDlgLibList; 116 if ( xDlgLibContainer.is() ) 117 { 118 Sequence< OUString > aDlgLibNames = xDlgLibContainer->getElementNames(); 119 sal_Int32 nDlgLibCount = aDlgLibNames.getLength(); 120 const OUString* pDlgLibNames = aDlgLibNames.getConstArray(); 121 for ( sal_Int32 i = 0 ; i < nDlgLibCount ; i++ ) 122 aDlgLibList.push_back( pDlgLibNames[ i ] ); 123 std::sort( aDlgLibList.begin() , aDlgLibList.end() , StringCompareLessThan ); 124 } 125 126 // merge both lists 127 std::vector<OUString> aLibList( aModLibList.size() + aDlgLibList.size() ); 128 std::merge( aModLibList.begin(), aModLibList.end(), aDlgLibList.begin(), aDlgLibList.end(), aLibList.begin(), StringCompareLessThan ); 129 std::vector<OUString>::iterator aIterEnd = std::unique( aLibList.begin(), aLibList.end() ); // move unique elements to the front 130 aLibList.erase( aIterEnd, aLibList.end() ); // remove duplicates 131 132 // copy to sequence 133 sal_Int32 nLibCount = aLibList.size(); 134 Sequence< OUString > aSeqLibNames( nLibCount ); 135 for ( sal_Int32 i = 0 ; i < nLibCount ; i++ ) 136 aSeqLibNames.getArray()[ i ] = aLibList[ i ]; 137 138 return aSeqLibNames; 139 } 140 141 bool RenameModule ( 142 weld::Widget* pErrorParent, 143 const ScriptDocument& rDocument, 144 const OUString& rLibName, 145 const OUString& rOldName, 146 const OUString& rNewName 147 ) 148 { 149 if ( !rDocument.hasModule( rLibName, rOldName ) ) 150 { 151 SAL_WARN( "basctl.basicide","basctl::RenameModule: old module name is invalid!" ); 152 return false; 153 } 154 155 if ( rDocument.hasModule( rLibName, rNewName ) ) 156 { 157 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent, 158 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_SBXNAMEALLREADYUSED2))); 159 xError->run(); 160 return false; 161 } 162 163 // #i74440 164 if ( rNewName.isEmpty() ) 165 { 166 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(pErrorParent, 167 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME))); 168 xError->run(); 169 return false; 170 } 171 172 if ( !rDocument.renameModule( rLibName, rOldName, rNewName ) ) 173 return false; 174 175 if (Shell* pShell = GetShell()) 176 { 177 if (VclPtr<ModulWindow> pWin = pShell->FindBasWin(rDocument, rLibName, rNewName, false, true)) 178 { 179 // set new name in window 180 pWin->SetName( rNewName ); 181 182 // set new module in module window 183 pWin->SetSbModule( pWin->GetBasic()->FindModule( rNewName ) ); 184 185 // update tabwriter 186 sal_uInt16 nId = pShell->GetWindowId( pWin ); 187 SAL_WARN_IF( nId == 0 , "basctl.basicide", "No entry in Tabbar!"); 188 if ( nId ) 189 { 190 TabBar& rTabBar = pShell->GetTabBar(); 191 rTabBar.SetPageText(nId, rNewName); 192 rTabBar.Sort(); 193 rTabBar.MakeVisible(rTabBar.GetCurPageId()); 194 } 195 } 196 } 197 return true; 198 } 199 200 namespace 201 { 202 struct MacroExecutionData 203 { 204 ScriptDocument aDocument; 205 SbMethodRef xMethod; 206 207 MacroExecutionData() 208 :aDocument( ScriptDocument::NoDocument ) 209 { 210 } 211 }; 212 213 class MacroExecution 214 { 215 public: 216 DECL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, void ); 217 }; 218 219 IMPL_STATIC_LINK( MacroExecution, ExecuteMacroEvent, void*, p, void ) 220 { 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 ); 225 226 SAL_WARN_IF( (pData->xMethod->GetParent()->GetFlags() & SbxFlagBits::ExtSearch) == SbxFlagBits::NONE, "basctl.basicide","No EXTSEARCH!" ); 227 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() ) ); 233 234 RunMethod( pData->xMethod.get() ); 235 } 236 } 237 238 OUString ChooseMacro( const uno::Reference< frame::XModel >& rxLimitToDocument, 239 const uno::Reference< frame::XFrame >& xDocFrame, 240 bool bChooseOnly ) 241 { 242 EnsureIde(); 243 244 GetExtraData()->ChoosingMacro() = true; 245 246 OUString aScriptURL; 247 SbMethod* pMethod = nullptr; 248 249 ScopedVclPtrInstance< MacroChooser > pChooser( nullptr, xDocFrame, true ); 250 if ( bChooseOnly || !SvtModuleOptions::IsBasicIDE() ) 251 pChooser->SetMode(MacroChooser::ChooseOnly); 252 253 if ( !bChooseOnly && rxLimitToDocument.is() ) 254 // Hack! 255 pChooser->SetMode(MacroChooser::Recording); 256 257 short nRetValue = pChooser->Execute(); 258 259 GetExtraData()->ChoosingMacro() = false; 260 261 switch ( nRetValue ) 262 { 263 case Macro_OkRun: 264 { 265 bool bError = false; 266 267 pMethod = pChooser->GetMacro(); 268 if ( !pMethod && pChooser->GetMode() == MacroChooser::Recording ) 269 pMethod = pChooser->CreateMacro(); 270 271 if ( !pMethod ) 272 break; 273 274 SbModule* pModule = pMethod->GetModule(); 275 if ( !pModule ) 276 { 277 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Module found!" ); 278 break; 279 } 280 281 StarBASIC* pBasic = dynamic_cast<StarBASIC*>(pModule->GetParent()); 282 if ( !pBasic ) 283 { 284 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No Basic found!" ); 285 break; 286 } 287 288 BasicManager* pBasMgr = FindBasicManager( pBasic ); 289 if ( !pBasMgr ) 290 { 291 SAL_WARN( "basctl.basicide", "basctl::ChooseMacro: No BasicManager found!" ); 292 break; 293 } 294 295 // name 296 OUString aName = pBasic->GetName() + "." + pModule->GetName() + "." + pMethod->GetName(); 297 298 // location 299 OUString aLocation; 300 ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) ); 301 if ( aDocument.isDocument() ) 302 { 303 // document basic 304 aLocation = "document" ; 305 306 if ( rxLimitToDocument.is() ) 307 { 308 uno::Reference< frame::XModel > xLimitToDocument( rxLimitToDocument ); 309 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() ) 320 { 321 SAL_WARN_IF(!xLimitToDocument.is(), "basctl.basicide", "basctl::ChooseMacro: a script container which is no document!?" ); 322 xLimitToDocument = rxLimitToDocument; 323 } 324 } 325 } 326 327 if ( xLimitToDocument != aDocument.getDocument() ) 328 { 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(); 334 } 335 } 336 } 337 else 338 { 339 // application basic 340 aLocation = "application" ; 341 } 342 343 // script URL 344 if ( !bError ) 345 { 346 aScriptURL = "vnd.sun.star.script:" + aName + "?language=Basic&location=" + aLocation; 347 } 348 349 if ( !rxLimitToDocument.is() ) 350 { 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 ); 355 } 356 } 357 break; 358 } 359 360 return aScriptURL; 361 } 362 363 Sequence< OUString > GetMethodNames( const ScriptDocument& rDocument, const OUString& rLibName, const OUString& rModName ) 364 { 365 Sequence< OUString > aSeqMethods; 366 367 // get module 368 OUString aOUSource; 369 if ( rDocument.getModule( rLibName, rModName, aOUSource ) ) 370 { 371 BasicManager* pBasMgr = rDocument.getBasicManager(); 372 StarBASIC* pSb = pBasMgr ? pBasMgr->GetLib( rLibName ) : nullptr; 373 SbModule* pMod = pSb ? pSb->FindModule( rModName ) : nullptr; 374 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 ) 379 { 380 xModule = new SbModule( rModName ); 381 xModule->SetSource32( aOUSource ); 382 pMod = xModule.get(); 383 } 384 385 sal_uInt16 nCount = pMod->GetMethods()->Count(); 386 sal_uInt16 nRealCount = nCount; 387 for ( sal_uInt16 i = 0; i < nCount; i++ ) 388 { 389 SbMethod* pMethod = static_cast<SbMethod*>(pMod->GetMethods()->Get( i )); 390 if( pMethod->IsHidden() ) 391 --nRealCount; 392 } 393 aSeqMethods.realloc( nRealCount ); 394 395 sal_uInt16 iTarget = 0; 396 for ( sal_uInt16 i = 0 ; i < nCount; ++i ) 397 { 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(); 403 } 404 } 405 406 return aSeqMethods; 407 } 408 409 bool HasMethod ( 410 ScriptDocument const& rDocument, 411 OUString const& rLibName, 412 OUString const& rModName, 413 OUString const& rMethName 414 ) 415 { 416 bool bHasMethod = false; 417 418 OUString aOUSource; 419 if ( rDocument.hasModule( rLibName, rModName ) && rDocument.getModule( rLibName, rModName, aOUSource ) ) 420 { 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 ) 429 { 430 xModule = new SbModule( rModName ); 431 xModule->SetSource32( aOUSource ); 432 pMod = xModule.get(); 433 } 434 SbxArray* pMethods = pMod->GetMethods().get(); 435 if ( pMethods ) 436 { 437 SbMethod* pMethod = static_cast<SbMethod*>(pMethods->Find( rMethName, SbxClassType::Method )); 438 if ( pMethod && !pMethod->IsHidden() ) 439 bHasMethod = true; 440 } 441 } 442 443 return bHasMethod; 444 } 445 446 } // namespace basctl 447 448 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 449
