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 "excelvbahelper.hxx" 21 22 #include <basic/basmgr.hxx> 23 #include <comphelper/processfactory.hxx> 24 #include <vbahelper/vbahelper.hxx> 25 #include <com/sun/star/frame/XModel.hpp> 26 #include <com/sun/star/sheet/XSheetCellRange.hpp> 27 #include <com/sun/star/sheet/GlobalSheetSettings.hpp> 28 #include <com/sun/star/sheet/XUnnamedDatabaseRanges.hpp> 29 #include <com/sun/star/sheet/XSpreadsheet.hpp> 30 #include <com/sun/star/sheet/XDatabaseRange.hpp> 31 32 #include <document.hxx> 33 #include <docuno.hxx> 34 #include <tabvwsh.hxx> 35 #include <transobj.hxx> 36 #include <scmod.hxx> 37 #include <cellsuno.hxx> 38 #include <gridwin.hxx> 39 40 #include <com/sun/star/script/vba/VBAEventId.hpp> 41 #include <com/sun/star/script/vba/XVBACompatibility.hpp> 42 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp> 43 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp> 44 #include <com/sun/star/script/ModuleInfo.hpp> 45 #include <com/sun/star/script/ModuleType.hpp> 46 47 using namespace ::com::sun::star; 48 using namespace ::ooo::vba; 49 50 namespace ooo { 51 namespace vba { 52 namespace excel { 53 54 uno::Reference< sheet::XUnnamedDatabaseRanges > 55 GetUnnamedDataBaseRanges( const ScDocShell* pShell ) 56 { 57 uno::Reference< frame::XModel > xModel; 58 if ( pShell ) 59 xModel.set( pShell->GetModel(), uno::UNO_QUERY_THROW ); 60 uno::Reference< beans::XPropertySet > xModelProps( xModel, uno::UNO_QUERY_THROW ); 61 uno::Reference< sheet::XUnnamedDatabaseRanges > xUnnamedDBRanges( xModelProps->getPropertyValue("UnnamedDatabaseRanges"), uno::UNO_QUERY_THROW ); 62 return xUnnamedDBRanges; 63 } 64 65 // returns the XDatabaseRange for the autofilter on sheet (nSheet) 66 // also populates sName with the name of range 67 uno::Reference< sheet::XDatabaseRange > 68 GetAutoFiltRange( const ScDocShell* pShell, sal_Int16 nSheet ) 69 { 70 uno::Reference< sheet::XUnnamedDatabaseRanges > xUnnamedDBRanges( GetUnnamedDataBaseRanges( pShell ), uno::UNO_QUERY_THROW ); 71 uno::Reference< sheet::XDatabaseRange > xDataBaseRange; 72 if (xUnnamedDBRanges->hasByTable( nSheet ) ) 73 { 74 uno::Reference< sheet::XDatabaseRange > xDBRange( xUnnamedDBRanges->getByTable( nSheet ) , uno::UNO_QUERY_THROW ); 75 bool bHasAuto = false; 76 uno::Reference< beans::XPropertySet > xProps( xDBRange, uno::UNO_QUERY_THROW ); 77 xProps->getPropertyValue("AutoFilter") >>= bHasAuto; 78 if ( bHasAuto ) 79 { 80 xDataBaseRange=xDBRange; 81 } 82 } 83 return xDataBaseRange; 84 } 85 86 ScDocShell* GetDocShellFromRange( const uno::Reference< uno::XInterface >& xRange ) 87 { 88 ScCellRangesBase* pScCellRangesBase = ScCellRangesBase::getImplementation( xRange ); 89 if ( !pScCellRangesBase ) 90 { 91 throw uno::RuntimeException("Failed to access underlying doc shell uno range object" ); 92 } 93 return pScCellRangesBase->GetDocShell(); 94 } 95 96 uno::Reference< XHelperInterface > 97 getUnoSheetModuleObj( const uno::Reference< table::XCellRange >& xRange ) 98 { 99 uno::Reference< sheet::XSheetCellRange > xSheetRange( xRange, uno::UNO_QUERY_THROW ); 100 uno::Reference< sheet::XSpreadsheet > xSheet( xSheetRange->getSpreadsheet(), uno::UNO_SET_THROW ); 101 return getUnoSheetModuleObj( xSheet ); 102 } 103 104 void implSetZoom( const uno::Reference< frame::XModel >& xModel, sal_Int16 nZoom, std::vector< SCTAB >& nTabs ) 105 { 106 ScTabViewShell* pViewSh = excel::getBestViewShell( xModel ); 107 Fraction aFract( nZoom, 100 ); 108 pViewSh->GetViewData().SetZoom( aFract, aFract, nTabs ); 109 pViewSh->RefreshZoom(); 110 } 111 112 const OUString REPLACE_CELLS_WARNING( "ReplaceCellsWarning"); 113 114 class PasteCellsWarningReseter 115 { 116 private: 117 bool bInitialWarningState; 118 /// @throws uno::RuntimeException 119 static uno::Reference< sheet::XGlobalSheetSettings > const & getGlobalSheetSettings() 120 { 121 static uno::Reference< sheet::XGlobalSheetSettings > xProps = sheet::GlobalSheetSettings::create( comphelper::getProcessComponentContext() ); 122 return xProps; 123 } 124 125 /// @throws uno::RuntimeException 126 static bool getReplaceCellsWarning() 127 { 128 return getGlobalSheetSettings()->getReplaceCellsWarning(); 129 } 130 131 /// @throws uno::RuntimeException 132 static void setReplaceCellsWarning( bool bState ) 133 { 134 getGlobalSheetSettings()->setReplaceCellsWarning( bState ); 135 } 136 public: 137 /// @throws uno::RuntimeException 138 PasteCellsWarningReseter() 139 { 140 bInitialWarningState = getReplaceCellsWarning(); 141 if ( bInitialWarningState ) 142 setReplaceCellsWarning( false ); 143 } 144 ~PasteCellsWarningReseter() 145 { 146 if ( bInitialWarningState ) 147 { 148 // don't allow dtor to throw 149 try 150 { 151 setReplaceCellsWarning( true ); 152 } 153 catch ( uno::Exception& /*e*/ ){} 154 } 155 } 156 }; 157 158 void 159 implnPaste( const uno::Reference< frame::XModel>& xModel ) 160 { 161 PasteCellsWarningReseter resetWarningBox; 162 ScTabViewShell* pViewShell = getBestViewShell( xModel ); 163 if ( pViewShell ) 164 { 165 pViewShell->PasteFromSystem(); 166 pViewShell->CellContentChanged(); 167 } 168 } 169 170 void 171 implnCopy( const uno::Reference< frame::XModel>& xModel ) 172 { 173 ScTabViewShell* pViewShell = getBestViewShell( xModel ); 174 ScDocShell* pDocShell = getDocShell( xModel ); 175 if ( pViewShell && pDocShell ) 176 { 177 pViewShell->CopyToClip(nullptr,false,false,true); 178 179 // mark the copied transfer object so it is used in ScVbaRange::Insert 180 uno::Reference<datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); 181 ScTransferObj* pClipObj = ScTransferObj::GetOwnClipboard(xTransferable); 182 if (pClipObj) 183 { 184 pClipObj->SetUseInApi( true ); 185 pDocShell->SetClipData(xTransferable); 186 } 187 } 188 } 189 190 void 191 implnCut( const uno::Reference< frame::XModel>& xModel ) 192 { 193 ScTabViewShell* pViewShell = getBestViewShell( xModel ); 194 ScDocShell* pDocShell = getDocShell( xModel ); 195 if ( pViewShell && pDocShell ) 196 { 197 pViewShell->CutToClip(); 198 199 // mark the copied transfer object so it is used in ScVbaRange::Insert 200 uno::Reference<datatransfer::XTransferable2> xTransferable(ScTabViewShell::GetClipData(pViewShell->GetViewData().GetActiveWin())); 201 ScTransferObj* pClipObj = ScTransferObj::GetOwnClipboard(xTransferable); 202 if (pClipObj) 203 { 204 pClipObj->SetUseInApi( true ); 205 pDocShell->SetClipData(xTransferable); 206 } 207 } 208 } 209 210 void implnPasteSpecial( const uno::Reference< frame::XModel>& xModel, InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bTranspose) 211 { 212 PasteCellsWarningReseter resetWarningBox; 213 214 ScTabViewShell* pTabViewShell = getBestViewShell( xModel ); 215 ScDocShell* pDocShell = getDocShell( xModel ); 216 if ( pTabViewShell && pDocShell ) 217 { 218 ScViewData& rView = pTabViewShell->GetViewData(); 219 vcl::Window* pWin = rView.GetActiveWin(); 220 if (pWin) 221 { 222 const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(pDocShell->GetClipData()); 223 ScDocument* pDoc = nullptr; 224 if ( pOwnClip ) 225 pDoc = pOwnClip->GetDocument(); 226 pTabViewShell->PasteFromClip( nFlags, pDoc, 227 nFunction, bSkipEmpty, bTranspose, false, 228 INS_NONE, InsertDeleteFlags::NONE, true ); 229 pTabViewShell->CellContentChanged(); 230 } 231 } 232 233 } 234 235 ScDocShell* 236 getDocShell( const css::uno::Reference< css::frame::XModel>& xModel ) 237 { 238 uno::Reference< uno::XInterface > xIf( xModel, uno::UNO_QUERY_THROW ); 239 ScModelObj* pModel = dynamic_cast< ScModelObj* >( xIf.get() ); 240 ScDocShell* pDocShell = nullptr; 241 if ( pModel ) 242 pDocShell = static_cast<ScDocShell*>(pModel->GetEmbeddedObject()); 243 return pDocShell; 244 245 } 246 247 ScTabViewShell* 248 getBestViewShell( const css::uno::Reference< css::frame::XModel>& xModel ) 249 { 250 ScDocShell* pDocShell = getDocShell( xModel ); 251 if ( pDocShell ) 252 return pDocShell->GetBestViewShell(); 253 return nullptr; 254 } 255 256 ScTabViewShell* 257 getCurrentBestViewShell( const uno::Reference< uno::XComponentContext >& xContext ) 258 { 259 uno::Reference< frame::XModel > xModel = getCurrentExcelDoc( xContext ); 260 return getBestViewShell( xModel ); 261 } 262 263 SfxViewFrame* 264 getViewFrame( const uno::Reference< frame::XModel >& xModel ) 265 { 266 ScTabViewShell* pViewShell = getBestViewShell( xModel ); 267 if ( pViewShell ) 268 return pViewShell->GetViewFrame(); 269 return nullptr; 270 } 271 272 uno::Reference< XHelperInterface > 273 getUnoSheetModuleObj( const uno::Reference< sheet::XSpreadsheet >& xSheet ) 274 { 275 uno::Reference< beans::XPropertySet > xProps( xSheet, uno::UNO_QUERY_THROW ); 276 OUString sCodeName; 277 xProps->getPropertyValue("CodeName") >>= sCodeName; 278 // #TODO #FIXME ideally we should 'throw' here if we don't get a valid parent, but... it is possible 279 // to create a module ( and use 'Option VBASupport 1' ) for a calc document, in this scenario there 280 // are *NO* special document module objects ( of course being able to switch between vba/non vba mode at 281 // the document in the future could fix this, especially IF the switching of the vba mode takes care to 282 // create the special document module objects if they don't exist. 283 return getUnoDocModule( sCodeName, GetDocShellFromRange( xSheet ) ); 284 } 285 286 uno::Reference< XHelperInterface > 287 getUnoSheetModuleObj( const uno::Reference< sheet::XSheetCellRangeContainer >& xRanges ) 288 { 289 uno::Reference< container::XEnumerationAccess > xEnumAccess( xRanges, uno::UNO_QUERY_THROW ); 290 uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration(); 291 uno::Reference< table::XCellRange > xRange( xEnum->nextElement(), uno::UNO_QUERY_THROW ); 292 return getUnoSheetModuleObj( xRange ); 293 } 294 295 uno::Reference< XHelperInterface > 296 getUnoSheetModuleObj( const uno::Reference< table::XCell >& xCell ) 297 { 298 uno::Reference< sheet::XSheetCellRange > xSheetRange( xCell, uno::UNO_QUERY_THROW ); 299 uno::Reference< sheet::XSpreadsheet > xSheet( xSheetRange->getSpreadsheet(), uno::UNO_SET_THROW ); 300 return getUnoSheetModuleObj( xSheet ); 301 } 302 303 uno::Reference< XHelperInterface > 304 getUnoSheetModuleObj( const uno::Reference< frame::XModel >& xModel, SCTAB nTab ) 305 { 306 uno::Reference< sheet::XSpreadsheetDocument > xDoc( xModel, uno::UNO_QUERY_THROW ); 307 uno::Reference< container::XIndexAccess > xSheets( xDoc->getSheets(), uno::UNO_QUERY_THROW ); 308 uno::Reference< sheet::XSpreadsheet > xSheet( xSheets->getByIndex( nTab ), uno::UNO_QUERY_THROW ); 309 return getUnoSheetModuleObj( xSheet ); 310 } 311 312 void setUpDocumentModules( const uno::Reference< sheet::XSpreadsheetDocument >& xDoc ) 313 { 314 uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY ); 315 ScDocShell* pShell = excel::getDocShell( xModel ); 316 if ( pShell ) 317 { 318 OUString aPrjName( "Standard" ); 319 pShell->GetBasicManager()->SetName( aPrjName ); 320 321 /* Set library container to VBA compatibility mode. This will create 322 the VBA Globals object and store it in the Basic manager of the 323 document. */ 324 uno::Reference<script::XLibraryContainer> xLibContainer = pShell->GetBasicContainer(); 325 uno::Reference<script::vba::XVBACompatibility> xVBACompat( xLibContainer, uno::UNO_QUERY_THROW ); 326 xVBACompat->setVBACompatibilityMode( true ); 327 328 if( xLibContainer.is() ) 329 { 330 if( !xLibContainer->hasByName( aPrjName ) ) 331 xLibContainer->createLibrary( aPrjName ); 332 uno::Any aLibAny = xLibContainer->getByName( aPrjName ); 333 uno::Reference< container::XNameContainer > xLib; 334 aLibAny >>= xLib; 335 if( xLib.is() ) 336 { 337 uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY_THROW ); 338 uno::Reference< lang::XMultiServiceFactory> xSF( pShell->GetModel(), uno::UNO_QUERY_THROW); 339 uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess( xSF->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), uno::UNO_QUERY_THROW ); 340 // set up the module info for the workbook and sheets in the newly created 341 // spreadsheet 342 ScDocument& rDoc = pShell->GetDocument(); 343 OUString sCodeName = rDoc.GetCodeName(); 344 if ( sCodeName.isEmpty() ) 345 { 346 sCodeName = "ThisWorkbook"; 347 rDoc.SetCodeName( sCodeName ); 348 } 349 350 std::vector< OUString > sDocModuleNames; 351 sDocModuleNames.push_back( sCodeName ); 352 353 for ( SCTAB index = 0; index < rDoc.GetTableCount(); index++) 354 { 355 OUString aName; 356 rDoc.GetCodeName( index, aName ); 357 sDocModuleNames.push_back( aName ); 358 } 359 360 for ( const auto& rName : sDocModuleNames ) 361 { 362 script::ModuleInfo sModuleInfo; 363 364 uno::Any aName= xVBACodeNamedObjectAccess->getByName( rName ); 365 sModuleInfo.ModuleObject.set( aName, uno::UNO_QUERY ); 366 sModuleInfo.ModuleType = script::ModuleType::DOCUMENT; 367 xVBAModuleInfo->insertModuleInfo( rName, sModuleInfo ); 368 if( xLib->hasByName( rName ) ) 369 xLib->replaceByName( rName, uno::makeAny( OUString( "Option VBASupport 1\n") ) ); 370 else 371 xLib->insertByName( rName, uno::makeAny( OUString( "Option VBASupport 1\n" ) ) ); 372 } 373 } 374 } 375 376 /* Trigger the Workbook_Open event, event processor will register 377 itself as listener for specific events. */ 378 try 379 { 380 uno::Reference< script::vba::XVBAEventProcessor > xVbaEvents( pShell->GetDocument().GetVbaEventProcessor(), uno::UNO_SET_THROW ); 381 uno::Sequence< uno::Any > aArgs; 382 xVbaEvents->processVbaEvent( script::vba::VBAEventId::WORKBOOK_OPEN, aArgs ); 383 } 384 catch( uno::Exception& ) 385 { 386 } 387 } 388 } 389 390 SfxItemSet* 391 ScVbaCellRangeAccess::GetDataSet( ScCellRangesBase* pRangeObj ) 392 { 393 return pRangeObj ? pRangeObj->GetCurrentDataSet( true ) : nullptr; 394 } 395 396 } // namespace excel 397 } // namespace vba 398 } // namespace ooo 399 400 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 401
