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