xref: /aoo41x/main/sc/source/ui/docshell/dbdocimp.cxx (revision b3f79822)
1*b3f79822SAndrew Rist /**************************************************************
2cdf0e10cSrcweir  *
3*b3f79822SAndrew Rist  * Licensed to the Apache Software Foundation (ASF) under one
4*b3f79822SAndrew Rist  * or more contributor license agreements.  See the NOTICE file
5*b3f79822SAndrew Rist  * distributed with this work for additional information
6*b3f79822SAndrew Rist  * regarding copyright ownership.  The ASF licenses this file
7*b3f79822SAndrew Rist  * to you under the Apache License, Version 2.0 (the
8*b3f79822SAndrew Rist  * "License"); you may not use this file except in compliance
9*b3f79822SAndrew Rist  * with the License.  You may obtain a copy of the License at
10*b3f79822SAndrew Rist  *
11*b3f79822SAndrew Rist  *   http://www.apache.org/licenses/LICENSE-2.0
12*b3f79822SAndrew Rist  *
13*b3f79822SAndrew Rist  * Unless required by applicable law or agreed to in writing,
14*b3f79822SAndrew Rist  * software distributed under the License is distributed on an
15*b3f79822SAndrew Rist  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*b3f79822SAndrew Rist  * KIND, either express or implied.  See the License for the
17*b3f79822SAndrew Rist  * specific language governing permissions and limitations
18*b3f79822SAndrew Rist  * under the License.
19*b3f79822SAndrew Rist  *
20*b3f79822SAndrew Rist  *************************************************************/
21*b3f79822SAndrew Rist 
22*b3f79822SAndrew Rist 
23cdf0e10cSrcweir 
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_sc.hxx"
26cdf0e10cSrcweir 
27cdf0e10cSrcweir 
28cdf0e10cSrcweir 
29cdf0e10cSrcweir // INCLUDE ---------------------------------------------------------
30cdf0e10cSrcweir 
31cdf0e10cSrcweir #include <comphelper/processfactory.hxx>
32cdf0e10cSrcweir #include <comphelper/types.hxx>
33cdf0e10cSrcweir #include <vcl/msgbox.hxx>
34cdf0e10cSrcweir #include <tools/debug.hxx>
35cdf0e10cSrcweir #include <svx/dataaccessdescriptor.hxx>
36cdf0e10cSrcweir #include <sfx2/viewfrm.hxx>
37cdf0e10cSrcweir 
38cdf0e10cSrcweir #include <com/sun/star/sdb/CommandType.hpp>
39cdf0e10cSrcweir #include <com/sun/star/sdb/XCompletedExecution.hpp>
40cdf0e10cSrcweir #include <com/sun/star/sdbc/XRow.hpp>
41cdf0e10cSrcweir #include <com/sun/star/sdbc/XRowSet.hpp>
42cdf0e10cSrcweir #include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp>
43cdf0e10cSrcweir #include <com/sun/star/sdbcx/XRowLocate.hpp>
44cdf0e10cSrcweir #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45cdf0e10cSrcweir #include <com/sun/star/beans/XPropertySet.hpp>
46cdf0e10cSrcweir #include <com/sun/star/frame/XDispatchProvider.hpp>
47cdf0e10cSrcweir #include <com/sun/star/frame/FrameSearchFlag.hpp>
48cdf0e10cSrcweir #include <com/sun/star/view/XSelectionSupplier.hpp>
49cdf0e10cSrcweir 
50cdf0e10cSrcweir 
51cdf0e10cSrcweir #include "dbdocfun.hxx"
52cdf0e10cSrcweir #include "docsh.hxx"
53cdf0e10cSrcweir #include "globstr.hrc"
54cdf0e10cSrcweir #include "scerrors.hxx"
55cdf0e10cSrcweir #include "dbcolect.hxx"
56cdf0e10cSrcweir #include "markdata.hxx"
57cdf0e10cSrcweir #include "undodat.hxx"
58cdf0e10cSrcweir #include "progress.hxx"
59cdf0e10cSrcweir #include "patattr.hxx"
60cdf0e10cSrcweir #include "docpool.hxx"
61cdf0e10cSrcweir #include "attrib.hxx"
62cdf0e10cSrcweir #include "dbdocutl.hxx"
63cdf0e10cSrcweir #include "editable.hxx"
64cdf0e10cSrcweir #include "hints.hxx"
65cdf0e10cSrcweir #include "miscuno.hxx"
66cdf0e10cSrcweir 
67cdf0e10cSrcweir using namespace com::sun::star;
68cdf0e10cSrcweir 
69cdf0e10cSrcweir #define SC_SERVICE_ROWSET			"com.sun.star.sdb.RowSet"
70cdf0e10cSrcweir #define SC_SERVICE_INTHANDLER		"com.sun.star.task.InteractionHandler"
71cdf0e10cSrcweir 
72cdf0e10cSrcweir //!	move to a header file?
73cdf0e10cSrcweir #define SC_DBPROP_DATASOURCENAME	"DataSourceName"
74cdf0e10cSrcweir #define SC_DBPROP_COMMAND			"Command"
75cdf0e10cSrcweir #define SC_DBPROP_COMMANDTYPE		"CommandType"
76cdf0e10cSrcweir #define SC_DBPROP_SELECTION			"Selection"
77cdf0e10cSrcweir #define SC_DBPROP_CURSOR			"Cursor"
78cdf0e10cSrcweir 
79cdf0e10cSrcweir // static
ShowInBeamer(const ScImportParam & rParam,SfxViewFrame * pFrame)80cdf0e10cSrcweir void ScDBDocFunc::ShowInBeamer( const ScImportParam& rParam, SfxViewFrame* pFrame )
81cdf0e10cSrcweir {
82cdf0e10cSrcweir 	//	called after opening the database beamer
83cdf0e10cSrcweir 
84cdf0e10cSrcweir 	if ( !pFrame || !rParam.bImport )
85cdf0e10cSrcweir 		return;
86cdf0e10cSrcweir 
87cdf0e10cSrcweir 	uno::Reference<frame::XFrame> xFrame = pFrame->GetFrame().GetFrameInterface();
88cdf0e10cSrcweir 	uno::Reference<frame::XDispatchProvider> xDP(xFrame, uno::UNO_QUERY);
89cdf0e10cSrcweir 
90cdf0e10cSrcweir 	uno::Reference<frame::XFrame> xBeamerFrame = xFrame->findFrame(
91cdf0e10cSrcweir 										rtl::OUString::createFromAscii("_beamer"),
92cdf0e10cSrcweir 										frame::FrameSearchFlag::CHILDREN);
93cdf0e10cSrcweir 	if (xBeamerFrame.is())
94cdf0e10cSrcweir 	{
95cdf0e10cSrcweir 		uno::Reference<frame::XController> xController = xBeamerFrame->getController();
96cdf0e10cSrcweir 		uno::Reference<view::XSelectionSupplier> xControllerSelection(xController, uno::UNO_QUERY);
97cdf0e10cSrcweir 		if (xControllerSelection.is())
98cdf0e10cSrcweir 		{
99cdf0e10cSrcweir 			sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND :
100cdf0e10cSrcweir 						( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY :
101cdf0e10cSrcweir 														sdb::CommandType::TABLE );
102cdf0e10cSrcweir 
103cdf0e10cSrcweir             ::svx::ODataAccessDescriptor aSelection;
104cdf0e10cSrcweir             aSelection.setDataSource(rtl::OUString( rParam.aDBName ));
105cdf0e10cSrcweir             aSelection[svx::daCommand]      <<= rtl::OUString( rParam.aStatement );
106cdf0e10cSrcweir             aSelection[svx::daCommandType]  <<= nType;
107cdf0e10cSrcweir 
108cdf0e10cSrcweir 			xControllerSelection->select(uno::makeAny(aSelection.createPropertyValueSequence()));
109cdf0e10cSrcweir 		}
110cdf0e10cSrcweir 		else
111cdf0e10cSrcweir 		{
112cdf0e10cSrcweir 			DBG_ERROR("no selection supplier in the beamer!");
113cdf0e10cSrcweir 		}
114cdf0e10cSrcweir 	}
115cdf0e10cSrcweir }
116cdf0e10cSrcweir 
117cdf0e10cSrcweir // -----------------------------------------------------------------
118cdf0e10cSrcweir 
DoImportUno(const ScAddress & rPos,const uno::Sequence<beans::PropertyValue> & aArgs)119cdf0e10cSrcweir sal_Bool ScDBDocFunc::DoImportUno( const ScAddress& rPos,
120cdf0e10cSrcweir 								const uno::Sequence<beans::PropertyValue>& aArgs )
121cdf0e10cSrcweir {
122cdf0e10cSrcweir     svx::ODataAccessDescriptor aDesc( aArgs );      // includes selection and result set
123cdf0e10cSrcweir 
124cdf0e10cSrcweir     //  create database range
125cdf0e10cSrcweir 	ScDBData* pDBData = rDocShell.GetDBData( ScRange(rPos), SC_DB_IMPORT, SC_DBSEL_KEEP );
126cdf0e10cSrcweir 	DBG_ASSERT(pDBData, "can't create DB data");
127cdf0e10cSrcweir 	String sTarget = pDBData->GetName();
128cdf0e10cSrcweir 
129cdf0e10cSrcweir     UpdateImport( sTarget, aDesc );
130cdf0e10cSrcweir 
131cdf0e10cSrcweir 	return sal_True;
132cdf0e10cSrcweir }
133cdf0e10cSrcweir 
134cdf0e10cSrcweir // -----------------------------------------------------------------
135cdf0e10cSrcweir 
DoImport(SCTAB nTab,const ScImportParam & rParam,const svx::ODataAccessDescriptor * pDescriptor,sal_Bool bRecord,sal_Bool bAddrInsert)136cdf0e10cSrcweir sal_Bool ScDBDocFunc::DoImport( SCTAB nTab, const ScImportParam& rParam,
137cdf0e10cSrcweir         const svx::ODataAccessDescriptor* pDescriptor, sal_Bool bRecord, sal_Bool bAddrInsert )
138cdf0e10cSrcweir {
139cdf0e10cSrcweir 	ScDocument* pDoc = rDocShell.GetDocument();
140cdf0e10cSrcweir 
141cdf0e10cSrcweir 	if (bRecord && !pDoc->IsUndoEnabled())
142cdf0e10cSrcweir 		bRecord = sal_False;
143cdf0e10cSrcweir 
144cdf0e10cSrcweir 	ScDBData* pDBData = 0;
145cdf0e10cSrcweir 	if ( !bAddrInsert )
146cdf0e10cSrcweir 	{
147cdf0e10cSrcweir 		pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
148cdf0e10cSrcweir 											rParam.nCol2, rParam.nRow2 );
149cdf0e10cSrcweir 		if (!pDBData)
150cdf0e10cSrcweir 		{
151cdf0e10cSrcweir 			DBG_ERROR( "DoImport: no DBData" );
152cdf0e10cSrcweir 			return sal_False;
153cdf0e10cSrcweir 		}
154cdf0e10cSrcweir 	}
155cdf0e10cSrcweir 
156cdf0e10cSrcweir 	Window* pWaitWin = rDocShell.GetActiveDialogParent();
157cdf0e10cSrcweir 	if (pWaitWin)
158cdf0e10cSrcweir 		pWaitWin->EnterWait();
159cdf0e10cSrcweir 	ScDocShellModificator aModificator( rDocShell );
160cdf0e10cSrcweir 
161cdf0e10cSrcweir 	sal_Bool bSuccess = sal_False;
162cdf0e10cSrcweir 	sal_Bool bApi = sal_False;						//! pass as argument
163cdf0e10cSrcweir 	sal_Bool bTruncated = sal_False;				// for warning
164cdf0e10cSrcweir 	sal_uInt16 nErrStringId = 0;
165cdf0e10cSrcweir 	String aErrorMessage;
166cdf0e10cSrcweir 
167cdf0e10cSrcweir 	SCCOL nCol = rParam.nCol1;
168cdf0e10cSrcweir 	SCROW nRow = rParam.nRow1;
169cdf0e10cSrcweir 	SCCOL nEndCol = nCol;					// end of resulting database area
170cdf0e10cSrcweir 	SCROW nEndRow = nRow;
171cdf0e10cSrcweir 	long i;
172cdf0e10cSrcweir 
173cdf0e10cSrcweir 	sal_Bool bDoSelection = sal_False;
174cdf0e10cSrcweir 	sal_Bool bRealSelection = sal_False;			// sal_True if not everything is selected
175cdf0e10cSrcweir     sal_Bool bBookmarkSelection = sal_False;
176cdf0e10cSrcweir     sal_Int32 nListPos = 0;
177cdf0e10cSrcweir     sal_Int32 nRowsRead = 0;
178cdf0e10cSrcweir     sal_Int32 nListCount = 0;
179cdf0e10cSrcweir 
180cdf0e10cSrcweir     uno::Sequence<uno::Any> aSelection;
181cdf0e10cSrcweir     if ( pDescriptor && pDescriptor->has(svx::daSelection) )
182cdf0e10cSrcweir     {
183cdf0e10cSrcweir         (*pDescriptor)[svx::daSelection] >>= aSelection;
184cdf0e10cSrcweir         nListCount = aSelection.getLength();
185cdf0e10cSrcweir         if ( nListCount > 0 )
186cdf0e10cSrcweir         {
187cdf0e10cSrcweir             bDoSelection = sal_True;
188cdf0e10cSrcweir             if ( pDescriptor->has(svx::daBookmarkSelection) )
189cdf0e10cSrcweir                 bBookmarkSelection = ScUnoHelpFunctions::GetBoolFromAny( (*pDescriptor)[svx::daBookmarkSelection] );
190cdf0e10cSrcweir             if ( bBookmarkSelection )
191cdf0e10cSrcweir             {
192cdf0e10cSrcweir                 // From bookmarks, there's no way to detect if all records are selected.
193cdf0e10cSrcweir                 // Rely on base to pass no selection in that case.
194cdf0e10cSrcweir                 bRealSelection = sal_True;
195cdf0e10cSrcweir             }
196cdf0e10cSrcweir         }
197cdf0e10cSrcweir     }
198cdf0e10cSrcweir 
199cdf0e10cSrcweir     uno::Reference<sdbc::XResultSet> xResultSet;
200cdf0e10cSrcweir     if ( pDescriptor && pDescriptor->has(svx::daCursor) )
201cdf0e10cSrcweir         xResultSet.set((*pDescriptor)[svx::daCursor], uno::UNO_QUERY);
202cdf0e10cSrcweir 
203cdf0e10cSrcweir 	// ImportDoc - also used for Redo
204cdf0e10cSrcweir 	ScDocument* pImportDoc = new ScDocument( SCDOCMODE_UNDO );
205cdf0e10cSrcweir 	pImportDoc->InitUndo( pDoc, nTab, nTab );
206cdf0e10cSrcweir 	ScColumn::bDoubleAlloc = sal_True;
207cdf0e10cSrcweir 
208cdf0e10cSrcweir 	//
209cdf0e10cSrcweir 	//	get data from database into import document
210cdf0e10cSrcweir 	//
211cdf0e10cSrcweir 
212cdf0e10cSrcweir 	try
213cdf0e10cSrcweir 	{
214cdf0e10cSrcweir 		//	progress bar
215cdf0e10cSrcweir 		//	only text (title is still needed, for the cancel button)
216cdf0e10cSrcweir 		ScProgress aProgress( &rDocShell, ScGlobal::GetRscString(STR_UNDO_IMPORTDATA), 0 );
217cdf0e10cSrcweir 		sal_uInt16 nInserted = 0;
218cdf0e10cSrcweir 
219cdf0e10cSrcweir         uno::Reference<sdbc::XRowSet> xRowSet = uno::Reference<sdbc::XRowSet>(
220cdf0e10cSrcweir                 xResultSet, uno::UNO_QUERY );
221cdf0e10cSrcweir         sal_Bool bDispose = sal_False;
222cdf0e10cSrcweir         if ( !xRowSet.is() )
223cdf0e10cSrcweir         {
224cdf0e10cSrcweir             bDispose = sal_True;
225cdf0e10cSrcweir             xRowSet = uno::Reference<sdbc::XRowSet>(
226cdf0e10cSrcweir                     comphelper::getProcessServiceFactory()->createInstance(
227cdf0e10cSrcweir                         rtl::OUString::createFromAscii( SC_SERVICE_ROWSET ) ),
228cdf0e10cSrcweir                     uno::UNO_QUERY);
229cdf0e10cSrcweir             uno::Reference<beans::XPropertySet> xRowProp( xRowSet, uno::UNO_QUERY );
230cdf0e10cSrcweir             DBG_ASSERT( xRowProp.is(), "can't get RowSet" );
231cdf0e10cSrcweir             if ( xRowProp.is() )
232cdf0e10cSrcweir             {
233cdf0e10cSrcweir                 //
234cdf0e10cSrcweir                 //	set source parameters
235cdf0e10cSrcweir                 //
236cdf0e10cSrcweir 
237cdf0e10cSrcweir                 sal_Int32 nType = rParam.bSql ? sdb::CommandType::COMMAND :
238cdf0e10cSrcweir                             ( (rParam.nType == ScDbQuery) ? sdb::CommandType::QUERY :
239cdf0e10cSrcweir                                                             sdb::CommandType::TABLE );
240cdf0e10cSrcweir                 uno::Any aAny;
241cdf0e10cSrcweir 
242cdf0e10cSrcweir                 aAny <<= rtl::OUString( rParam.aDBName );
243cdf0e10cSrcweir                 xRowProp->setPropertyValue(
244cdf0e10cSrcweir                             rtl::OUString::createFromAscii(SC_DBPROP_DATASOURCENAME), aAny );
245cdf0e10cSrcweir 
246cdf0e10cSrcweir                 aAny <<= rtl::OUString( rParam.aStatement );
247cdf0e10cSrcweir                 xRowProp->setPropertyValue(
248cdf0e10cSrcweir                             rtl::OUString::createFromAscii(SC_DBPROP_COMMAND), aAny );
249cdf0e10cSrcweir 
250cdf0e10cSrcweir                 aAny <<= nType;
251cdf0e10cSrcweir                 xRowProp->setPropertyValue(
252cdf0e10cSrcweir                             rtl::OUString::createFromAscii(SC_DBPROP_COMMANDTYPE), aAny );
253cdf0e10cSrcweir 
254cdf0e10cSrcweir                 uno::Reference<sdb::XCompletedExecution> xExecute( xRowSet, uno::UNO_QUERY );
255cdf0e10cSrcweir                 if ( xExecute.is() )
256cdf0e10cSrcweir                 {
257cdf0e10cSrcweir                     uno::Reference<task::XInteractionHandler> xHandler(
258cdf0e10cSrcweir                             comphelper::getProcessServiceFactory()->createInstance(
259cdf0e10cSrcweir                                 rtl::OUString::createFromAscii( SC_SERVICE_INTHANDLER ) ),
260cdf0e10cSrcweir                             uno::UNO_QUERY);
261cdf0e10cSrcweir                     xExecute->executeWithCompletion( xHandler );
262cdf0e10cSrcweir                 }
263cdf0e10cSrcweir                 else
264cdf0e10cSrcweir                     xRowSet->execute();
265cdf0e10cSrcweir             }
266cdf0e10cSrcweir         }
267cdf0e10cSrcweir         if ( xRowSet.is() )
268cdf0e10cSrcweir         {
269cdf0e10cSrcweir 			//
270cdf0e10cSrcweir 			//	get column descriptions
271cdf0e10cSrcweir 			//
272cdf0e10cSrcweir 
273cdf0e10cSrcweir 			long nColCount = 0;
274cdf0e10cSrcweir 			uno::Reference<sdbc::XResultSetMetaData> xMeta;
275cdf0e10cSrcweir 			uno::Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp( xRowSet, uno::UNO_QUERY );
276cdf0e10cSrcweir 			if ( xMetaSupp.is() )
277cdf0e10cSrcweir 				xMeta = xMetaSupp->getMetaData();
278cdf0e10cSrcweir 			if ( xMeta.is() )
279cdf0e10cSrcweir 				nColCount = xMeta->getColumnCount();	// this is the number of real columns
280cdf0e10cSrcweir 
281cdf0e10cSrcweir 			if ( rParam.nCol1 + nColCount - 1 > MAXCOL )
282cdf0e10cSrcweir 			{
283cdf0e10cSrcweir 				nColCount = 0;
284cdf0e10cSrcweir 				//!	error message
285cdf0e10cSrcweir 			}
286cdf0e10cSrcweir 
287cdf0e10cSrcweir             uno::Reference<sdbcx::XRowLocate> xLocate;
288cdf0e10cSrcweir             if ( bBookmarkSelection )
289cdf0e10cSrcweir             {
290cdf0e10cSrcweir                 xLocate.set( xRowSet, uno::UNO_QUERY );
291cdf0e10cSrcweir                 if ( !xLocate.is() )
292cdf0e10cSrcweir                 {
293cdf0e10cSrcweir                     DBG_ERRORFILE("can't get XRowLocate");
294cdf0e10cSrcweir                     bDoSelection = bRealSelection = bBookmarkSelection = sal_False;
295cdf0e10cSrcweir                 }
296cdf0e10cSrcweir             }
297cdf0e10cSrcweir 
298cdf0e10cSrcweir 			uno::Reference<sdbc::XRow> xRow( xRowSet, uno::UNO_QUERY );
299cdf0e10cSrcweir 			if ( nColCount > 0 && xRow.is() )
300cdf0e10cSrcweir 			{
301cdf0e10cSrcweir 				nEndCol = (SCCOL)( rParam.nCol1 + nColCount - 1 );
302cdf0e10cSrcweir 
303cdf0e10cSrcweir 				uno::Sequence<sal_Int32> aColTypes( nColCount );	// column types
304cdf0e10cSrcweir 				uno::Sequence<sal_Bool> aColCurr( nColCount );		// currency flag is not in types
305cdf0e10cSrcweir 				sal_Int32* pTypeArr = aColTypes.getArray();
306cdf0e10cSrcweir 				sal_Bool* pCurrArr = aColCurr.getArray();
307cdf0e10cSrcweir 				for (i=0; i<nColCount; i++)
308cdf0e10cSrcweir 				{
309cdf0e10cSrcweir 					pTypeArr[i] = xMeta->getColumnType( i+1 );
310cdf0e10cSrcweir 					pCurrArr[i] = xMeta->isCurrency( i+1 );
311cdf0e10cSrcweir 				}
312cdf0e10cSrcweir 
313cdf0e10cSrcweir 				if ( !bAddrInsert )					// read column names
314cdf0e10cSrcweir 				{
315cdf0e10cSrcweir 					nCol = rParam.nCol1;
316cdf0e10cSrcweir 					for (i=0; i<nColCount; i++)
317cdf0e10cSrcweir 					{
318cdf0e10cSrcweir 						pImportDoc->SetString( nCol, nRow, nTab,
319cdf0e10cSrcweir 												xMeta->getColumnLabel( i+1 ) );
320cdf0e10cSrcweir 						++nCol;
321cdf0e10cSrcweir 					}
322cdf0e10cSrcweir 					++nRow;
323cdf0e10cSrcweir 				}
324cdf0e10cSrcweir 
325cdf0e10cSrcweir 				sal_Bool bEnd = sal_False;
326cdf0e10cSrcweir 				if ( !bDoSelection )
327cdf0e10cSrcweir 					xRowSet->beforeFirst();
328cdf0e10cSrcweir 				while ( !bEnd )
329cdf0e10cSrcweir 				{
330cdf0e10cSrcweir 					//	skip rows that are not selected
331cdf0e10cSrcweir 					if ( !bDoSelection )
332cdf0e10cSrcweir 					{
333cdf0e10cSrcweir                         if ( (bEnd = !xRowSet->next()) == sal_False )
334cdf0e10cSrcweir 							++nRowsRead;
335cdf0e10cSrcweir 					}
336cdf0e10cSrcweir 					else
337cdf0e10cSrcweir 					{
338cdf0e10cSrcweir 						if (nListPos < nListCount)
339cdf0e10cSrcweir 						{
340cdf0e10cSrcweir                             if ( bBookmarkSelection )
341cdf0e10cSrcweir                             {
342cdf0e10cSrcweir                                 bEnd = !xLocate->moveToBookmark(aSelection[nListPos]);
343cdf0e10cSrcweir                             }
344cdf0e10cSrcweir                             else    // use record numbers
345cdf0e10cSrcweir                             {
346cdf0e10cSrcweir                                 sal_Int32 nNextRow = 0;
347cdf0e10cSrcweir                                 aSelection[nListPos] >>= nNextRow;
348cdf0e10cSrcweir                                 if ( nRowsRead+1 < nNextRow )
349cdf0e10cSrcweir                                     bRealSelection = sal_True;
350cdf0e10cSrcweir                                 bEnd = !xRowSet->absolute(nRowsRead = nNextRow);
351cdf0e10cSrcweir                             }
352cdf0e10cSrcweir                             ++nListPos;
353cdf0e10cSrcweir 						}
354cdf0e10cSrcweir 						else
355cdf0e10cSrcweir 						{
356cdf0e10cSrcweir                             if ( !bBookmarkSelection && xRowSet->next() )
357cdf0e10cSrcweir                                 bRealSelection = sal_True;                      // more data available but not used
358cdf0e10cSrcweir                             bEnd = sal_True;
359cdf0e10cSrcweir 						}
360cdf0e10cSrcweir 					}
361cdf0e10cSrcweir 
362cdf0e10cSrcweir 					if ( !bEnd )
363cdf0e10cSrcweir 					{
364cdf0e10cSrcweir 						if ( ValidRow(nRow) )
365cdf0e10cSrcweir 						{
366cdf0e10cSrcweir 							nCol = rParam.nCol1;
367cdf0e10cSrcweir 							for (i=0; i<nColCount; i++)
368cdf0e10cSrcweir 							{
369cdf0e10cSrcweir 								ScDatabaseDocUtil::PutData( pImportDoc, nCol, nRow, nTab,
370cdf0e10cSrcweir 												xRow, i+1, pTypeArr[i], pCurrArr[i] );
371cdf0e10cSrcweir 								++nCol;
372cdf0e10cSrcweir 							}
373cdf0e10cSrcweir 							nEndRow = nRow;
374cdf0e10cSrcweir 							++nRow;
375cdf0e10cSrcweir 
376cdf0e10cSrcweir 							//	progress bar
377cdf0e10cSrcweir 
378cdf0e10cSrcweir 							++nInserted;
379cdf0e10cSrcweir 							if (!(nInserted & 15))
380cdf0e10cSrcweir 							{
381cdf0e10cSrcweir 								String aPict = ScGlobal::GetRscString( STR_PROGRESS_IMPORT );
382cdf0e10cSrcweir 								String aText = aPict.GetToken(0,'#');
383cdf0e10cSrcweir 								aText += String::CreateFromInt32( nInserted );
384cdf0e10cSrcweir 								aText += aPict.GetToken(1,'#');
385cdf0e10cSrcweir 
386cdf0e10cSrcweir 								if (!aProgress.SetStateText( 0, aText ))	// stopped by user?
387cdf0e10cSrcweir 								{
388cdf0e10cSrcweir 									bEnd = sal_True;
389cdf0e10cSrcweir 									bSuccess = sal_False;
390cdf0e10cSrcweir 									nErrStringId = STR_DATABASE_ABORTED;
391cdf0e10cSrcweir 								}
392cdf0e10cSrcweir 							}
393cdf0e10cSrcweir 						}
394cdf0e10cSrcweir 						else		// past the end of the spreadsheet
395cdf0e10cSrcweir 						{
396cdf0e10cSrcweir 							bEnd = sal_True;			// don't continue
397cdf0e10cSrcweir 							bTruncated = sal_True;		// warning flag
398cdf0e10cSrcweir 						}
399cdf0e10cSrcweir 					}
400cdf0e10cSrcweir 				}
401cdf0e10cSrcweir 
402cdf0e10cSrcweir 				bSuccess = sal_True;
403cdf0e10cSrcweir 			}
404cdf0e10cSrcweir 
405cdf0e10cSrcweir             if ( bDispose )
406cdf0e10cSrcweir                 ::comphelper::disposeComponent( xRowSet );
407cdf0e10cSrcweir 		}
408cdf0e10cSrcweir 	}
409cdf0e10cSrcweir 	catch ( sdbc::SQLException& rError )
410cdf0e10cSrcweir 	{
411cdf0e10cSrcweir 		aErrorMessage = rError.Message;
412cdf0e10cSrcweir 	}
413cdf0e10cSrcweir 	catch ( uno::Exception& )
414cdf0e10cSrcweir 	{
415cdf0e10cSrcweir 		DBG_ERROR("Unexpected exception in database");
416cdf0e10cSrcweir 	}
417cdf0e10cSrcweir 
418cdf0e10cSrcweir 	ScColumn::bDoubleAlloc = sal_False;
419cdf0e10cSrcweir 	pImportDoc->DoColResize( nTab, rParam.nCol1,nEndCol, 0 );
420cdf0e10cSrcweir 
421cdf0e10cSrcweir 	//
422cdf0e10cSrcweir 	//	test for cell protection
423cdf0e10cSrcweir 	//
424cdf0e10cSrcweir 
425cdf0e10cSrcweir 	sal_Bool bKeepFormat = !bAddrInsert && pDBData->IsKeepFmt();
426cdf0e10cSrcweir 	sal_Bool bMoveCells = !bAddrInsert && pDBData->IsDoSize();
427cdf0e10cSrcweir 	SCCOL nFormulaCols = 0;	// columns to be filled with formulas
428cdf0e10cSrcweir 	if (bMoveCells && nEndCol == rParam.nCol2)
429cdf0e10cSrcweir 	{
430cdf0e10cSrcweir 		//	if column count changes, formulas would become invalid anyway
431cdf0e10cSrcweir 		//	-> only set nFormulaCols for unchanged column count
432cdf0e10cSrcweir 
433cdf0e10cSrcweir 		SCCOL nTestCol = rParam.nCol2 + 1;		// right of the data
434cdf0e10cSrcweir 		SCROW nTestRow = rParam.nRow1 + 1;		// below the title row
435cdf0e10cSrcweir 		while ( nTestCol <= MAXCOL &&
436cdf0e10cSrcweir 				pDoc->GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
437cdf0e10cSrcweir 			++nTestCol, ++nFormulaCols;
438cdf0e10cSrcweir 	}
439cdf0e10cSrcweir 
440cdf0e10cSrcweir 	if (bSuccess)
441cdf0e10cSrcweir 	{
442cdf0e10cSrcweir 		//	old and new range editable?
443cdf0e10cSrcweir 		ScEditableTester aTester;
444cdf0e10cSrcweir 		aTester.TestBlock( pDoc, nTab, rParam.nCol1,rParam.nRow1,rParam.nCol2,rParam.nRow2 );
445cdf0e10cSrcweir 		aTester.TestBlock( pDoc, nTab, rParam.nCol1,rParam.nRow1,nEndCol,nEndRow );
446cdf0e10cSrcweir 		if ( !aTester.IsEditable() )
447cdf0e10cSrcweir 		{
448cdf0e10cSrcweir 			nErrStringId = aTester.GetMessageId();
449cdf0e10cSrcweir 			bSuccess = sal_False;
450cdf0e10cSrcweir 		}
451cdf0e10cSrcweir 		else if ( pDoc->GetChangeTrack() != NULL )
452cdf0e10cSrcweir 		{
453cdf0e10cSrcweir 			nErrStringId = STR_PROTECTIONERR;
454cdf0e10cSrcweir 			bSuccess = sal_False;
455cdf0e10cSrcweir 		}
456cdf0e10cSrcweir 	}
457cdf0e10cSrcweir 
458cdf0e10cSrcweir 	if ( bSuccess && bMoveCells )
459cdf0e10cSrcweir 	{
460cdf0e10cSrcweir 		ScRange aOld( rParam.nCol1, rParam.nRow1, nTab,
461cdf0e10cSrcweir 						rParam.nCol2+nFormulaCols, rParam.nRow2, nTab );
462cdf0e10cSrcweir 		ScRange aNew( rParam.nCol1, rParam.nRow1, nTab,
463cdf0e10cSrcweir 						nEndCol+nFormulaCols, nEndRow, nTab );
464cdf0e10cSrcweir 		if (!pDoc->CanFitBlock( aOld, aNew ))
465cdf0e10cSrcweir 		{
466cdf0e10cSrcweir 			nErrStringId = STR_MSSG_DOSUBTOTALS_2;		// can't insert cells
467cdf0e10cSrcweir 			bSuccess = sal_False;
468cdf0e10cSrcweir 		}
469cdf0e10cSrcweir 	}
470cdf0e10cSrcweir 
471cdf0e10cSrcweir 	//
472cdf0e10cSrcweir 	//	copy data from import doc into real document
473cdf0e10cSrcweir 	//
474cdf0e10cSrcweir 
475cdf0e10cSrcweir 	if ( bSuccess )
476cdf0e10cSrcweir 	{
477cdf0e10cSrcweir 		if (bKeepFormat)
478cdf0e10cSrcweir 		{
479cdf0e10cSrcweir 			//	keep formatting of title and first data row from the document
480cdf0e10cSrcweir 			//	CopyToDocument also copies styles, Apply... needs separate calls
481cdf0e10cSrcweir 
482cdf0e10cSrcweir 			SCCOL nMinEndCol = Min( rParam.nCol2, nEndCol );	// not too much
483cdf0e10cSrcweir             nMinEndCol = sal::static_int_cast<SCCOL>( nMinEndCol + nFormulaCols );  // only if column count unchanged
484cdf0e10cSrcweir 			pImportDoc->DeleteAreaTab( 0,0, MAXCOL,MAXROW, nTab, IDF_ATTRIB );
485cdf0e10cSrcweir 			pDoc->CopyToDocument( rParam.nCol1, rParam.nRow1, nTab,
486cdf0e10cSrcweir 									nMinEndCol, rParam.nRow1, nTab,
487cdf0e10cSrcweir 									IDF_ATTRIB, sal_False, pImportDoc );
488cdf0e10cSrcweir 
489cdf0e10cSrcweir 			SCROW nDataStartRow = rParam.nRow1+1;
490cdf0e10cSrcweir 			for (SCCOL nCopyCol=rParam.nCol1; nCopyCol<=nMinEndCol; nCopyCol++)
491cdf0e10cSrcweir 			{
492cdf0e10cSrcweir 				const ScPatternAttr* pSrcPattern = pDoc->GetPattern(
493cdf0e10cSrcweir 													nCopyCol, nDataStartRow, nTab );
494cdf0e10cSrcweir 				pImportDoc->ApplyPatternAreaTab( nCopyCol, nDataStartRow, nCopyCol, nEndRow,
495cdf0e10cSrcweir 													nTab, *pSrcPattern );
496cdf0e10cSrcweir 				const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
497cdf0e10cSrcweir 				if (pStyle)
498cdf0e10cSrcweir 					pImportDoc->ApplyStyleAreaTab( nCopyCol, nDataStartRow, nCopyCol, nEndRow,
499cdf0e10cSrcweir 													nTab, *pStyle );
500cdf0e10cSrcweir 			}
501cdf0e10cSrcweir 		}
502cdf0e10cSrcweir 
503cdf0e10cSrcweir 		//	don't set cell protection attribute if table is protected
504cdf0e10cSrcweir 		if (pDoc->IsTabProtected(nTab))
505cdf0e10cSrcweir 		{
506cdf0e10cSrcweir 			ScPatternAttr aPattern(pImportDoc->GetPool());
507cdf0e10cSrcweir 			aPattern.GetItemSet().Put( ScProtectionAttr( sal_False,sal_False,sal_False,sal_False ) );
508cdf0e10cSrcweir 			pImportDoc->ApplyPatternAreaTab( 0,0,MAXCOL,MAXROW, nTab, aPattern );
509cdf0e10cSrcweir 		}
510cdf0e10cSrcweir 
511cdf0e10cSrcweir 		//
512cdf0e10cSrcweir 		//	copy old data for undo
513cdf0e10cSrcweir 		//
514cdf0e10cSrcweir 
515cdf0e10cSrcweir 		SCCOL nUndoEndCol = Max( nEndCol, rParam.nCol2 );		// rParam = old end
516cdf0e10cSrcweir 		SCROW nUndoEndRow = Max( nEndRow, rParam.nRow2 );
517cdf0e10cSrcweir 
518cdf0e10cSrcweir 		ScDocument* pUndoDoc = NULL;
519cdf0e10cSrcweir 		ScDBData* pUndoDBData = NULL;
520cdf0e10cSrcweir 		if ( bRecord )
521cdf0e10cSrcweir 		{
522cdf0e10cSrcweir 			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
523cdf0e10cSrcweir 			pUndoDoc->InitUndo( pDoc, nTab, nTab );
524cdf0e10cSrcweir 
525cdf0e10cSrcweir 			if ( !bAddrInsert )
526cdf0e10cSrcweir 				pUndoDBData = new ScDBData( *pDBData );
527cdf0e10cSrcweir 		}
528cdf0e10cSrcweir 
529cdf0e10cSrcweir 		ScMarkData aNewMark;
530cdf0e10cSrcweir 		aNewMark.SelectOneTable( nTab );
531cdf0e10cSrcweir 
532cdf0e10cSrcweir 		if (bRecord)
533cdf0e10cSrcweir 		{
534cdf0e10cSrcweir             // do not touch notes (ScUndoImportData does not support drawing undo)
535cdf0e10cSrcweir             sal_uInt16 nCopyFlags = IDF_ALL & ~IDF_NOTE;
536cdf0e10cSrcweir 
537cdf0e10cSrcweir 			//	nFormulaCols is set only if column count is unchanged
538cdf0e10cSrcweir 			pDoc->CopyToDocument( rParam.nCol1, rParam.nRow1, nTab,
539cdf0e10cSrcweir 									nEndCol+nFormulaCols, nEndRow, nTab,
540cdf0e10cSrcweir                                     nCopyFlags, sal_False, pUndoDoc );
541cdf0e10cSrcweir 			if ( rParam.nCol2 > nEndCol )
542cdf0e10cSrcweir 				pDoc->CopyToDocument( nEndCol+1, rParam.nRow1, nTab,
543cdf0e10cSrcweir 										nUndoEndCol, nUndoEndRow, nTab,
544cdf0e10cSrcweir                                         nCopyFlags, sal_False, pUndoDoc );
545cdf0e10cSrcweir 			if ( rParam.nRow2 > nEndRow )
546cdf0e10cSrcweir 				pDoc->CopyToDocument( rParam.nCol1, nEndRow+1, nTab,
547cdf0e10cSrcweir 										nUndoEndCol+nFormulaCols, nUndoEndRow, nTab,
548cdf0e10cSrcweir                                         nCopyFlags, sal_False, pUndoDoc );
549cdf0e10cSrcweir 		}
550cdf0e10cSrcweir 
551cdf0e10cSrcweir 		//
552cdf0e10cSrcweir 		//	move new data
553cdf0e10cSrcweir 		//
554cdf0e10cSrcweir 
555cdf0e10cSrcweir 		if (bMoveCells)
556cdf0e10cSrcweir 		{
557cdf0e10cSrcweir 			//	clear only the range without the formulas,
558cdf0e10cSrcweir 			//	so the formula title and first row are preserved
559cdf0e10cSrcweir 
560cdf0e10cSrcweir 			ScRange aDelRange( rParam.nCol1, rParam.nRow1, nTab,
561cdf0e10cSrcweir 								rParam.nCol2, rParam.nRow2, nTab );
562cdf0e10cSrcweir             pDoc->DeleteAreaTab( aDelRange, IDF_ALL & ~IDF_NOTE );  // ohne die Formeln
563cdf0e10cSrcweir 
564cdf0e10cSrcweir 			ScRange aOld( rParam.nCol1, rParam.nRow1, nTab,
565cdf0e10cSrcweir 							rParam.nCol2+nFormulaCols, rParam.nRow2, nTab );
566cdf0e10cSrcweir 			ScRange aNew( rParam.nCol1, rParam.nRow1, nTab,
567cdf0e10cSrcweir 							nEndCol+nFormulaCols, nEndRow, nTab );
568cdf0e10cSrcweir 			pDoc->FitBlock( aOld, aNew, sal_False );		// Formeln nicht loeschen
569cdf0e10cSrcweir 		}
570cdf0e10cSrcweir 		else if ( nEndCol < rParam.nCol2 )		// DeleteArea calls PutInOrder
571cdf0e10cSrcweir 			pDoc->DeleteArea( nEndCol+1, rParam.nRow1, rParam.nCol2, rParam.nRow2,
572cdf0e10cSrcweir                                 aNewMark, IDF_CONTENTS & ~IDF_NOTE );
573cdf0e10cSrcweir 
574cdf0e10cSrcweir 		//	CopyToDocument doesn't remove contents
575cdf0e10cSrcweir         pDoc->DeleteAreaTab( rParam.nCol1, rParam.nRow1, nEndCol, nEndRow, nTab, IDF_CONTENTS & ~IDF_NOTE );
576cdf0e10cSrcweir 
577cdf0e10cSrcweir 		//	#41216# remove each column from ImportDoc after copying to reduce memory usage
578cdf0e10cSrcweir 		sal_Bool bOldAutoCalc = pDoc->GetAutoCalc();
579cdf0e10cSrcweir 		pDoc->SetAutoCalc( sal_False );				// outside of the loop
580cdf0e10cSrcweir 		for (SCCOL nCopyCol = rParam.nCol1; nCopyCol <= nEndCol; nCopyCol++)
581cdf0e10cSrcweir 		{
582cdf0e10cSrcweir 			pImportDoc->CopyToDocument( nCopyCol, rParam.nRow1, nTab, nCopyCol, nEndRow, nTab,
583cdf0e10cSrcweir 										IDF_ALL, sal_False, pDoc );
584cdf0e10cSrcweir 			pImportDoc->DeleteAreaTab( nCopyCol, rParam.nRow1, nCopyCol, nEndRow, nTab, IDF_CONTENTS );
585cdf0e10cSrcweir 			pImportDoc->DoColResize( nTab, nCopyCol, nCopyCol, 0 );
586cdf0e10cSrcweir 		}
587cdf0e10cSrcweir 		pDoc->SetAutoCalc( bOldAutoCalc );
588cdf0e10cSrcweir 
589cdf0e10cSrcweir 		if (nFormulaCols > 0)				// copy formulas
590cdf0e10cSrcweir 		{
591cdf0e10cSrcweir 			if (bKeepFormat)			// formats for formulas
592cdf0e10cSrcweir 				pImportDoc->CopyToDocument( nEndCol+1, rParam.nRow1, nTab,
593cdf0e10cSrcweir 											nEndCol+nFormulaCols, nEndRow, nTab,
594cdf0e10cSrcweir 											IDF_ATTRIB, sal_False, pDoc );
595cdf0e10cSrcweir 			// fill formulas
596cdf0e10cSrcweir 			ScMarkData aMark;
597cdf0e10cSrcweir 			aMark.SelectOneTable(nTab);
598cdf0e10cSrcweir 			pDoc->Fill( nEndCol+1, rParam.nRow1+1, nEndCol+nFormulaCols, rParam.nRow1+1,
599cdf0e10cSrcweir 							aMark, nEndRow-rParam.nRow1-1, FILL_TO_BOTTOM, FILL_SIMPLE );
600cdf0e10cSrcweir 		}
601cdf0e10cSrcweir 
602cdf0e10cSrcweir 		//	if new range is smaller, clear old contents
603cdf0e10cSrcweir 
604cdf0e10cSrcweir 		if (!bMoveCells)		// move has happened above
605cdf0e10cSrcweir 		{
606cdf0e10cSrcweir 			if ( rParam.nCol2 > nEndCol )
607cdf0e10cSrcweir 				pDoc->DeleteArea( nEndCol+1, rParam.nRow1, rParam.nCol2, rParam.nRow2,
608cdf0e10cSrcweir 									aNewMark, IDF_CONTENTS );
609cdf0e10cSrcweir 			if ( rParam.nRow2 > nEndRow )
610cdf0e10cSrcweir 				pDoc->DeleteArea( rParam.nCol1, nEndRow+1, rParam.nCol2, rParam.nRow2,
611cdf0e10cSrcweir 									aNewMark, IDF_CONTENTS );
612cdf0e10cSrcweir 		}
613cdf0e10cSrcweir 
614cdf0e10cSrcweir 		if( !bAddrInsert )		// update database range
615cdf0e10cSrcweir 		{
616cdf0e10cSrcweir 			pDBData->SetImportParam( rParam );
617cdf0e10cSrcweir 			pDBData->SetHeader( sal_True );
618cdf0e10cSrcweir 			pDBData->SetByRow( sal_True );
619cdf0e10cSrcweir 			pDBData->SetArea( nTab, rParam.nCol1,rParam.nRow1, nEndCol,nEndRow );
620cdf0e10cSrcweir 			pDBData->SetImportSelection( bRealSelection );
621cdf0e10cSrcweir 			pDoc->CompileDBFormula();
622cdf0e10cSrcweir 		}
623cdf0e10cSrcweir 
624cdf0e10cSrcweir 		if (bRecord)
625cdf0e10cSrcweir 		{
626cdf0e10cSrcweir 			ScDocument* pRedoDoc = pImportDoc;
627cdf0e10cSrcweir 			pImportDoc = NULL;
628cdf0e10cSrcweir 
629cdf0e10cSrcweir 			if (nFormulaCols > 0)					// include filled formulas for redo
630cdf0e10cSrcweir 				pDoc->CopyToDocument( rParam.nCol1, rParam.nRow1, nTab,
631cdf0e10cSrcweir 										nEndCol+nFormulaCols, nEndRow, nTab,
632cdf0e10cSrcweir                                         IDF_ALL & ~IDF_NOTE, sal_False, pRedoDoc );
633cdf0e10cSrcweir 
634cdf0e10cSrcweir 			ScDBData* pRedoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
635cdf0e10cSrcweir 
636cdf0e10cSrcweir 			rDocShell.GetUndoManager()->AddUndoAction(
637cdf0e10cSrcweir 				new ScUndoImportData( &rDocShell, nTab,
638cdf0e10cSrcweir 										rParam, nUndoEndCol, nUndoEndRow,
639cdf0e10cSrcweir 										nFormulaCols,
640cdf0e10cSrcweir 										pUndoDoc, pRedoDoc, pUndoDBData, pRedoDBData ) );
641cdf0e10cSrcweir 		}
642cdf0e10cSrcweir 
643cdf0e10cSrcweir 		pDoc->SetDirty();
644cdf0e10cSrcweir 		rDocShell.PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab, PAINT_GRID );
645cdf0e10cSrcweir 		aModificator.SetDocumentModified();
646cdf0e10cSrcweir 
647cdf0e10cSrcweir 	    ScDBRangeRefreshedHint aHint( rParam );
648cdf0e10cSrcweir 	    pDoc->BroadcastUno( aHint );
649cdf0e10cSrcweir 
650cdf0e10cSrcweir 		if (pWaitWin)
651cdf0e10cSrcweir 			pWaitWin->LeaveWait();
652cdf0e10cSrcweir 
653cdf0e10cSrcweir 		if ( bTruncated && !bApi )			// show warning
654cdf0e10cSrcweir 			ErrorHandler::HandleError(SCWARN_IMPORT_RANGE_OVERFLOW);
655cdf0e10cSrcweir 	}
656cdf0e10cSrcweir 	else if ( !bApi )
657cdf0e10cSrcweir 	{
658cdf0e10cSrcweir 		if (pWaitWin)
659cdf0e10cSrcweir 			pWaitWin->LeaveWait();
660cdf0e10cSrcweir 
661cdf0e10cSrcweir 		if (!aErrorMessage.Len())
662cdf0e10cSrcweir 		{
663cdf0e10cSrcweir 			if (!nErrStringId)
664cdf0e10cSrcweir 				nErrStringId = STR_MSSG_IMPORTDATA_0;
665cdf0e10cSrcweir 			aErrorMessage = ScGlobal::GetRscString( nErrStringId );
666cdf0e10cSrcweir 		}
667cdf0e10cSrcweir 		InfoBox aInfoBox( rDocShell.GetActiveDialogParent(), aErrorMessage );
668cdf0e10cSrcweir 		aInfoBox.Execute();
669cdf0e10cSrcweir 	}
670cdf0e10cSrcweir 
671cdf0e10cSrcweir 	delete pImportDoc;
672cdf0e10cSrcweir 
673cdf0e10cSrcweir 	return bSuccess;
674cdf0e10cSrcweir }
675cdf0e10cSrcweir 
676cdf0e10cSrcweir 
677cdf0e10cSrcweir 
678cdf0e10cSrcweir 
679