1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 #include <comphelper/processfactory.hxx> 28 29 #include <cppuhelper/implbase1.hxx> 30 #include <cppuhelper/implbase3.hxx> 31 32 #include <com/sun/star/frame/XDesktop.hpp> 33 #include <com/sun/star/sheet/XSpreadsheetDocument.hpp> 34 #include <com/sun/star/container/XEnumerationAccess.hpp> 35 #include <com/sun/star/frame/XComponentLoader.hpp> 36 #include <com/sun/star/lang/XComponent.hpp> 37 #include <com/sun/star/frame/XModel.hpp> 38 #include <com/sun/star/frame/XFrame.hpp> 39 #include <com/sun/star/frame/FrameSearchFlag.hpp> 40 #include <com/sun/star/util/XModifiable.hpp> 41 #include <com/sun/star/frame/XStorable.hpp> 42 #include <com/sun/star/lang/DisposedException.hpp> 43 #include <com/sun/star/beans/PropertyVetoException.hpp> 44 #include <com/sun/star/util/XCloseable.hpp> 45 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> 46 #include <com/sun/star/document/XTypeDetection.hpp> 47 #include <com/sun/star/uri/XUriReference.hpp> 48 #include <com/sun/star/uri/XUriReferenceFactory.hpp> 49 #include <com/sun/star/script/vba/VBAEventId.hpp> 50 #include <com/sun/star/script/vba/XVBACompatibility.hpp> 51 #include <com/sun/star/script/vba/XVBAEventProcessor.hpp> 52 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp> 53 #include <com/sun/star/script/ModuleInfo.hpp> 54 #include <com/sun/star/script/ModuleType.hpp> 55 56 #include <sfx2/objsh.hxx> 57 #include <tools/urlobj.hxx> 58 59 #include "vbaglobals.hxx" 60 #include "vbaworkbook.hxx" 61 #include "vbaworkbooks.hxx" 62 #include <vbahelper/vbahelper.hxx> 63 64 #include <hash_map> 65 #include <vector> 66 #include <osl/file.hxx> 67 using namespace ::ooo::vba; 68 using namespace ::com::sun::star; 69 70 const sal_Int16 CUSTOM_CHAR = 5; 71 72 void setUpDocumentModules( const uno::Reference< sheet::XSpreadsheetDocument >& xDoc ) 73 { 74 uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY ); 75 ScDocShell* pShell = excel::getDocShell( xModel ); 76 if ( pShell ) 77 { 78 String aPrjName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) ); 79 pShell->GetBasicManager()->SetName( aPrjName ); 80 81 /* Set library container to VBA compatibility mode. This will create 82 the VBA Globals object and store it in the Basic manager of the 83 document. */ 84 uno::Reference<script::XLibraryContainer> xLibContainer = pShell->GetBasicContainer(); 85 uno::Reference<script::vba::XVBACompatibility> xVBACompat( xLibContainer, uno::UNO_QUERY_THROW ); 86 xVBACompat->setVBACompatibilityMode( sal_True ); 87 88 if( xLibContainer.is() ) 89 { 90 if( !xLibContainer->hasByName( aPrjName ) ) 91 xLibContainer->createLibrary( aPrjName ); 92 uno::Any aLibAny = xLibContainer->getByName( aPrjName ); 93 uno::Reference< container::XNameContainer > xLib; 94 aLibAny >>= xLib; 95 if( xLib.is() ) 96 { 97 uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY_THROW ); 98 uno::Reference< lang::XMultiServiceFactory> xSF( pShell->GetModel(), uno::UNO_QUERY_THROW); 99 uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess( xSF->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "ooo.vba.VBAObjectModuleObjectProvider"))), uno::UNO_QUERY_THROW ); 100 // set up the module info for the workbook and sheets in the nealy created 101 // spreadsheet 102 ScDocument* pDoc = pShell->GetDocument(); 103 String sCodeName = pDoc->GetCodeName(); 104 if ( sCodeName.Len() == 0 ) 105 { 106 sCodeName = String( RTL_CONSTASCII_USTRINGPARAM("ThisWorkbook") ); 107 pDoc->SetCodeName( sCodeName ); 108 } 109 110 std::vector< rtl::OUString > sDocModuleNames; 111 sDocModuleNames.push_back( sCodeName ); 112 113 uno::Reference<container::XNameAccess > xSheets( xDoc->getSheets(), uno::UNO_QUERY_THROW ); 114 uno::Sequence< rtl::OUString > sSheets( xSheets->getElementNames() ); 115 116 for ( sal_Int32 index=0; index < sSheets.getLength() ; ++index ) 117 { 118 sDocModuleNames.push_back( sSheets[ index ] ); 119 } 120 121 std::vector<rtl::OUString>::iterator it_end = sDocModuleNames.end(); 122 123 for ( std::vector<rtl::OUString>::iterator it = sDocModuleNames.begin(); it != it_end; ++it ) 124 { 125 script::ModuleInfo sModuleInfo; 126 127 sModuleInfo.ModuleObject.set( xVBACodeNamedObjectAccess->getByName( *it ), uno::UNO_QUERY ); 128 sModuleInfo.ModuleType = script::ModuleType::DOCUMENT; 129 xVBAModuleInfo->insertModuleInfo( *it, sModuleInfo ); 130 if( xLib->hasByName( *it ) ) 131 xLib->replaceByName( *it, uno::makeAny( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "Option VBASupport 1\n") ) ) ); 132 else 133 xLib->insertByName( *it, uno::makeAny( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "Option VBASupport 1\n" ) ) ) ); 134 } 135 } 136 } 137 } 138 } 139 140 static uno::Any 141 getWorkbook( uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< sheet::XSpreadsheetDocument > &xDoc, const uno::Reference< XHelperInterface >& xParent ) 142 { 143 // FIXME: fine as long as ScVbaWorkbook is stateless ... 144 uno::Reference< frame::XModel > xModel( xDoc, uno::UNO_QUERY ); 145 if( !xModel.is() ) 146 return uno::Any(); 147 148 uno::Reference< excel::XWorkbook > xWb( getVBADocument( xModel ), uno::UNO_QUERY ); 149 if ( xWb.is() ) 150 { 151 OSL_TRACE(" *** Returning Module uno Object *** "); 152 return uno::Any( xWb ); 153 } 154 155 ScVbaWorkbook *pWb = new ScVbaWorkbook( xParent, xContext, xModel ); 156 return uno::Any( uno::Reference< excel::XWorkbook > (pWb) ); 157 } 158 159 class WorkBookEnumImpl : public EnumerationHelperImpl 160 { 161 uno::Any m_aApplication; 162 public: 163 WorkBookEnumImpl( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< uno::XComponentContext >& xContext, const uno::Reference< container::XEnumeration >& xEnumeration, const uno::Any& aApplication ) throw ( uno::RuntimeException ) : EnumerationHelperImpl( xParent, xContext, xEnumeration ), m_aApplication( aApplication ) {} 164 165 virtual uno::Any SAL_CALL nextElement( ) throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException) 166 { 167 uno::Reference< sheet::XSpreadsheetDocument > xDoc( m_xEnumeration->nextElement(), uno::UNO_QUERY_THROW ); 168 return getWorkbook( m_xContext, xDoc, m_xParent ); 169 } 170 171 }; 172 173 ScVbaWorkbooks::ScVbaWorkbooks( const uno::Reference< XHelperInterface >& xParent, const uno::Reference< css::uno::XComponentContext >& xContext ) : ScVbaWorkbooks_BASE( xParent, xContext, VbaDocumentsBase::EXCEL_DOCUMENT ) 174 { 175 } 176 // XEnumerationAccess 177 uno::Type 178 ScVbaWorkbooks::getElementType() throw (uno::RuntimeException) 179 { 180 return excel::XWorkbook::static_type(0); 181 } 182 uno::Reference< container::XEnumeration > 183 ScVbaWorkbooks::createEnumeration() throw (uno::RuntimeException) 184 { 185 // #FIXME its possible the WorkBookEnumImpl here doens't reflect 186 // the state of this object ( although it should ) would be 187 // safer to create an enumeration based on this objects state 188 // rather than one effectively based of the desktop component 189 uno::Reference< container::XEnumerationAccess > xEnumerationAccess( m_xIndexAccess, uno::UNO_QUERY_THROW ); 190 return new WorkBookEnumImpl( mxParent, mxContext, xEnumerationAccess->createEnumeration(), Application() ); 191 } 192 193 uno::Any 194 ScVbaWorkbooks::createCollectionObject( const css::uno::Any& aSource ) 195 { 196 uno::Reference< sheet::XSpreadsheetDocument > xDoc( aSource, uno::UNO_QUERY_THROW ); 197 return getWorkbook( mxContext, xDoc, mxParent ); 198 } 199 200 201 uno::Any SAL_CALL 202 ScVbaWorkbooks::Add( const uno::Any& Template ) throw (uno::RuntimeException) 203 { 204 uno::Reference< sheet::XSpreadsheetDocument > xSpreadDoc; 205 sal_Int32 nWorkbookType = 0; 206 ::rtl::OUString aTemplateFileName; 207 if( Template >>= nWorkbookType ) 208 { 209 // nWorkbookType is a constant from XlWBATemplate (added in Excel 2007) 210 // TODO: create chart-sheet if supported by Calc 211 212 xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW ); 213 // create a document with one sheet only 214 uno::Reference< sheet::XSpreadsheets > xSheets( xSpreadDoc->getSheets(), uno::UNO_SET_THROW ); 215 uno::Reference< container::XIndexAccess > xSheetsIA( xSheets, uno::UNO_QUERY_THROW ); 216 while( xSheetsIA->getCount() > 1 ) 217 { 218 uno::Reference< container::XNamed > xSheetName( xSheetsIA->getByIndex( xSheetsIA->getCount() - 1 ), uno::UNO_QUERY_THROW ); 219 xSheets->removeByName( xSheetName->getName() ); 220 } 221 } 222 else if( Template >>= aTemplateFileName ) 223 { 224 // TODO: create document from template 225 xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW ); 226 } 227 else if( !Template.hasValue() ) 228 { 229 // regular spreadsheet document with configured number of sheets 230 xSpreadDoc.set( createDocument(), uno::UNO_QUERY_THROW ); 231 } 232 else 233 { 234 // illegal argument 235 throw uno::RuntimeException(); 236 } 237 238 // need to set up the document modules ( and vba mode ) here 239 setUpDocumentModules( xSpreadDoc ); 240 if( xSpreadDoc.is() ) 241 return getWorkbook( mxContext, xSpreadDoc, mxParent ); 242 return uno::Any(); 243 } 244 245 void SAL_CALL 246 ScVbaWorkbooks::Close() throw (uno::RuntimeException) 247 { 248 closeDocuments(); 249 } 250 251 bool 252 ScVbaWorkbooks::isTextFile( const rtl::OUString& sType ) 253 { 254 // will return true if the file is 255 // a) a variant of a text file 256 // b) a csv file 257 // c) unknown 258 // returning true basically means treat this like a csv file 259 const static rtl::OUString txtType( RTL_CONSTASCII_USTRINGPARAM("writer_Text" ) ); 260 const static rtl::OUString csvType( RTL_CONSTASCII_USTRINGPARAM("calc_Text_txt_csv_StarCalc" ) ); 261 const static rtl::OUString encodedTxtType( RTL_CONSTASCII_USTRINGPARAM("writer_Text_encoded" ) ); 262 return sType.equals( txtType ) || sType.equals( csvType ) || ( sType.getLength() == 0 ) || sType.equals( encodedTxtType ); 263 } 264 265 bool 266 ScVbaWorkbooks::isSpreadSheetFile( const rtl::OUString& sType ) 267 { 268 // include calc_QPro etc. ? ( not for the moment anyway ) 269 if ( sType.indexOf( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("calc_MS"))) == 0 270 || sType.indexOf( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("calc8"))) == 0 271 || sType.indexOf( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("calc_StarOffice"))) == 0 ) 272 return true; 273 return false; 274 } 275 276 rtl::OUString 277 ScVbaWorkbooks::getFileFilterType( const rtl::OUString& rFileName ) 278 { 279 uno::Reference< document::XTypeDetection > xTypeDetect( mxContext->getServiceManager()->createInstanceWithContext(::rtl::OUString::createFromAscii("com.sun.star.document.TypeDetection"), mxContext), uno::UNO_QUERY_THROW ); 280 uno::Sequence< beans::PropertyValue > aMediaDesc(1); 281 aMediaDesc[ 0 ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ("URL" ) ); 282 aMediaDesc[ 0 ].Value <<= rFileName; 283 rtl::OUString sType = xTypeDetect->queryTypeByDescriptor( aMediaDesc, sal_True ); 284 return sType; 285 } 286 287 // #TODO# #FIXME# can any of the unused params below be used? 288 uno::Any SAL_CALL 289 ScVbaWorkbooks::Open( const rtl::OUString& rFileName, const uno::Any& /*UpdateLinks*/, const uno::Any& ReadOnly, const uno::Any& Format, const uno::Any& /*Password*/, const uno::Any& /*WriteResPassword*/, const uno::Any& /*IgnoreReadOnlyRecommended*/, const uno::Any& /*Origin*/, const uno::Any& Delimiter, const uno::Any& /*Editable*/, const uno::Any& /*Notify*/, const uno::Any& /*Converter*/, const uno::Any& /*AddToMru*/ ) throw (uno::RuntimeException) 290 { 291 // we need to detect if this is a URL, if not then assume its a file path 292 rtl::OUString aURL; 293 INetURLObject aObj; 294 aObj.SetURL( rFileName ); 295 bool bIsURL = aObj.GetProtocol() != INET_PROT_NOT_VALID; 296 if ( bIsURL ) 297 aURL = rFileName; 298 else 299 osl::FileBase::getFileURLFromSystemPath( rFileName, aURL ); 300 301 uno::Sequence< beans::PropertyValue > sProps(0); 302 sal_Int32 nIndex = 0; 303 304 rtl::OUString sType = getFileFilterType( aURL ); 305 // A text file means it needs to be processed as a csv file 306 if ( isTextFile( sType ) ) 307 { 308 // Values for format 309 // 1 Tabs 310 // 2 Commas 311 // 3 Spaces 312 // 4 Semicolons 313 // 5 Nothing 314 // 6 Custom character (see the Delimiter argument 315 // no format means use the current delimiter 316 sProps.realloc( 3 ); 317 sProps[ nIndex ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("FilterOptions" ) ); 318 sal_Int16 delims[] = { 0 /*default not used*/, 9/*tab*/, 44/*comma*/, 32/*space*/, 59/*semicolon*/ }; 319 static rtl::OUString sRestOfFormat( RTL_CONSTASCII_USTRINGPARAM(",34,0,1" ) ); 320 321 rtl::OUString sFormat; 322 sal_Int16 nFormat = 0; // default indicator 323 324 325 if ( Format.hasValue() ) 326 { 327 Format >>= nFormat; // val of nFormat overwritten if extracted 328 // validate param 329 if ( nFormat < 1 || nFormat > 6 ) 330 throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Illegal value for Format" ) ), uno::Reference< uno::XInterface >() ); 331 } 332 333 sal_Int16 nDelim = getCurrentDelim(); 334 335 if ( nFormat > 0 && nFormat < CUSTOM_CHAR ) 336 { 337 nDelim = delims[ nFormat ]; 338 } 339 else if ( nFormat > CUSTOM_CHAR ) 340 { 341 // Need to check Delimiter param 342 if ( !Delimiter.hasValue() ) 343 throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Expected value for Delimiter" ) ), uno::Reference< uno::XInterface >() ); 344 rtl::OUString sStr; 345 Delimiter >>= sStr; 346 String aUniStr( sStr ); 347 if ( aUniStr.Len() ) 348 nDelim = aUniStr.GetChar(0); 349 else 350 throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Incorrect value for Delimiter" ) ), uno::Reference< uno::XInterface >() ); 351 } 352 353 getCurrentDelim() = nDelim; //set new current 354 355 sFormat = rtl::OUString::valueOf( (sal_Int32)nDelim ) + sRestOfFormat; 356 sProps[ nIndex++ ].Value <<= sFormat; 357 sProps[ nIndex ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("FilterName") ); 358 sProps[ nIndex++ ].Value <<= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Text - txt - csv (StarCalc)") ); 359 // Ensure WORKAROUND_CSV_TXT_BUG_i60158 gets called in typedetection.cxx so 360 // csv is forced for deep detected 'writerxxx' types 361 sProps[ nIndex ].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DocumentService") ); 362 sProps[ nIndex ].Value <<= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.sheet.SpreadsheetDocument") ); 363 } 364 else if ( !isSpreadSheetFile( sType ) ) 365 throw uno::RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Bad Format")), uno::Reference< uno::XInterface >() ); 366 367 uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( openDocument( rFileName, ReadOnly, sProps ), uno::UNO_QUERY_THROW ); 368 uno::Any aRet = getWorkbook( mxContext, xSpreadDoc, mxParent ); 369 uno::Reference< excel::XWorkbook > xWBook( aRet, uno::UNO_QUERY ); 370 if ( xWBook.is() ) 371 xWBook->Activate(); 372 return aRet; 373 } 374 375 rtl::OUString& 376 ScVbaWorkbooks::getServiceImplName() 377 { 378 static rtl::OUString sImplName( RTL_CONSTASCII_USTRINGPARAM("ScVbaWorkbooks") ); 379 return sImplName; 380 } 381 382 css::uno::Sequence<rtl::OUString> 383 ScVbaWorkbooks::getServiceNames() 384 { 385 static uno::Sequence< rtl::OUString > sNames; 386 if ( sNames.getLength() == 0 ) 387 { 388 sNames.realloc( 1 ); 389 sNames[0] = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ooo.vba.excel.Workbooks") ); 390 } 391 return sNames; 392 } 393