xref: /core/sc/source/ui/vba/excelvbahelper.cxx (revision 892a5cfe)
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