xref: /aoo42x/main/sc/inc/column.hxx (revision cdf0e10c) !
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 
28 #ifndef SC_COLUMN_HXX
29 #define SC_COLUMN_HXX
30 
31 #include "markarr.hxx"
32 #include "global.hxx"
33 #include "address.hxx"
34 #include "rangenam.hxx"
35 #include <tools/solar.h>
36 
37 #include <set>
38 
39 class Fraction;
40 class OutputDevice;
41 class Rectangle;
42 class SfxBroadcaster;
43 class SfxItemPoolCache;
44 class SfxItemSet;
45 class SvtListener;
46 class SfxPoolItem;
47 class SfxStyleSheetBase;
48 class SvxBorderLine;
49 class SvxBoxInfoItem;
50 class SvxBoxItem;
51 
52 class ScAttrIterator;
53 class ScAttrArray;
54 class ScBaseCell;
55 class ScDocument;
56 class ScFormulaCell;
57 class ScMarkData;
58 class ScPatternAttr;
59 class ScStyleSheet;
60 class SvtBroadcaster;
61 class TypedScStrCollection;
62 class ScProgress;
63 class ScPostIt;
64 struct ScFunctionData;
65 struct ScLineFlags;
66 struct ScMergePatternState;
67 class ScFlatBoolRowSegments;
68 
69 #define COLUMN_DELTA	4
70 
71 
72 struct ScNeededSizeOptions
73 {
74 	const ScPatternAttr*	pPattern;
75 	sal_Bool					bFormula;
76 	sal_Bool					bSkipMerged;
77 	sal_Bool					bGetFont;
78 	sal_Bool					bTotalSize;
79 
80 	ScNeededSizeOptions()
81 	{
82 		pPattern = NULL;
83 		bFormula = sal_False;
84 		bSkipMerged = sal_True;
85 		bGetFont = sal_True;
86 		bTotalSize = sal_False;
87 	}
88 };
89 
90 struct ColEntry
91 {
92 	SCROW		nRow;
93 	ScBaseCell*	pCell;
94 };
95 
96 
97 class ScColumn
98 {
99 private:
100 	SCCOL			nCol;
101 	SCTAB			nTab;
102 
103 	SCSIZE			nCount;
104 	SCSIZE			nLimit;
105 	ColEntry*		pItems;
106 
107 	ScAttrArray*	pAttrArray;
108 	ScDocument*		pDocument;
109 
110 friend class ScDocument;					// fuer FillInfo
111 friend class ScDocumentIterator;
112 friend class ScValueIterator;
113 friend class ScHorizontalValueIterator;
114 friend class ScDBQueryDataIterator;
115 friend class ScColumnIterator;
116 friend class ScQueryCellIterator;
117 friend class ScMarkedDataIter;
118 friend class ScCellIterator;
119 friend class ScHorizontalCellIterator;
120 friend class ScHorizontalAttrIterator;
121 
122 public:
123 static sal_Bool bDoubleAlloc;			// fuer Import: Groesse beim Allozieren verdoppeln
124 
125 public:
126 				ScColumn();
127 				~ScColumn();
128 
129 	void		Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc);
130 
131 	sal_Bool 		Search( SCROW nRow, SCSIZE& nIndex ) const;
132 	ScBaseCell*	GetCell( SCROW nRow ) const;
133 	void		Insert( SCROW nRow, ScBaseCell* pCell );
134 	void		Insert( SCROW nRow, sal_uLong nFormatIndex, ScBaseCell* pCell );
135 	void		Append( SCROW nRow, ScBaseCell* pCell );
136 	void 		Delete( SCROW nRow );
137 	void		DeleteAtIndex( SCSIZE nIndex );
138 	void 	    FreeAll();
139 	void		Resize( SCSIZE nSize );
140 	void		SwapRow( SCROW nRow1, SCROW nRow2 );
141 	void		SwapCell( SCROW nRow, ScColumn& rCol);
142 
143 //UNUSED2009-05 sal_Bool		HasLines( SCROW nRow1, SCROW nRow2, Rectangle& rSizes,
144 //UNUSED2009-05 			sal_Bool bLeft, sal_Bool bRight ) const;
145     bool        HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const;
146 	sal_Bool		HasAttribSelection( const ScMarkData& rMark, sal_uInt16 nMask ) const;
147 	sal_Bool		ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
148 								SCCOL& rPaintCol, SCROW& rPaintRow,
149 								sal_Bool bRefresh, sal_Bool bAttrs );
150 
151 	sal_Bool		IsEmptyVisData(sal_Bool bNotes) const;		// ohne Broadcaster
152 	sal_Bool		IsEmptyData() const;
153 	sal_Bool		IsEmptyAttr() const;
154 	sal_Bool		IsEmpty() const;
155 
156 				// nur Daten:
157 	sal_Bool		IsEmptyBlock(SCROW nStartRow, SCROW nEndRow, bool bIgnoreNotes = false) const;
158 	SCSIZE	    GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const;
159 	sal_Bool		HasDataAt(SCROW nRow) const;
160 	sal_Bool		HasVisibleDataAt(SCROW nRow) const;
161     SCROW		GetFirstDataPos() const;
162 	SCROW		GetLastDataPos() const;
163 	SCROW		GetLastVisDataPos(sal_Bool bNotes) const;				// ohne Broadcaster
164 	SCROW		GetFirstVisDataPos(sal_Bool bNotes) const;
165 	sal_Bool 		GetPrevDataPos(SCROW& rRow) const;
166 	sal_Bool 		GetNextDataPos(SCROW& rRow) const;
167 	void		FindDataAreaPos(SCROW& rRow, long nMovY) const;	// (ohne Broadcaster)
168 	void		FindUsed( SCROW nStartRow, SCROW nEndRow, sal_Bool* pUsed ) const;
169 
170 	SCSIZE		VisibleCount( SCROW nStartRow, SCROW nEndRow ) const;
171 
172 	sal_uInt16		GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const;
173 	sal_Bool		HasSelectionMatrixFragment(const ScMarkData& rMark) const;
174 
175     sal_Bool        GetFirstVisibleAttr( SCROW& rFirstRow ) const;
176     sal_Bool        GetLastVisibleAttr( SCROW& rLastRow ) const;
177 	sal_Bool		HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const;
178 	sal_Bool		IsVisibleAttrEqual( const ScColumn& rCol, SCROW nStartRow = 0,
179 									SCROW nEndRow = MAXROW ) const;
180 	sal_Bool		IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const;
181 
182 	sal_Bool		TestInsertCol( SCROW nStartRow, SCROW nEndRow) const;
183 	sal_Bool		TestInsertRow( SCSIZE nSize ) const;
184 	void		InsertRow( SCROW nStartRow, SCSIZE nSize );
185 	void		DeleteRow( SCROW nStartRow, SCSIZE nSize );
186     void        DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex, sal_uInt16 nDelFlag );
187     void        DeleteArea(SCROW nStartRow, SCROW nEndRow, sal_uInt16 nDelFlag );
188     void        CopyToClip(SCROW nRow1, SCROW nRow2, ScColumn& rColumn, sal_Bool bKeepScenarioFlags, sal_Bool bCloneNoteCaptions);
189 	void		CopyFromClip(SCROW nRow1, SCROW nRow2, long nDy,
190 								sal_uInt16 nInsFlag, sal_Bool bAsLink, sal_Bool bSkipAttrForEmpty, ScColumn& rColumn);
191 	void		StartListeningInArea( SCROW nRow1, SCROW nRow2 );
192 	void		BroadcastInArea( SCROW nRow1, SCROW nRow2 );
193 
194 	void		RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow );
195 
196 				//	Markierung von diesem Dokument
197 	void		MixMarked( const ScMarkData& rMark, sal_uInt16 nFunction,
198 							sal_Bool bSkipEmpty, ScColumn& rSrcCol );
199 	void		MixData( SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction, sal_Bool bSkipEmpty,
200 							ScColumn& rSrcCol );
201 
202 	ScFormulaCell*	CreateRefCell( ScDocument* pDestDoc, const ScAddress& rDestPos,
203 									SCSIZE nIndex, sal_uInt16 nFlags ) const;
204 
205 	ScAttrIterator* CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) const;
206 
207 	SCCOL		GetCol() const { return nCol; }
208 
209 				//	UpdateSelectionFunction: Mehrfachselektion
210 	void		UpdateSelectionFunction( const ScMarkData& rMark,
211 									ScFunctionData& rData,
212                                     ScFlatBoolRowSegments& rHiddenRows,
213 									sal_Bool bDoExclude, SCROW nExStartRow, SCROW nExEndRow );
214 	void		UpdateAreaFunction( ScFunctionData& rData,
215                                     ScFlatBoolRowSegments& rHiddenRows,
216 									SCROW nStartRow, SCROW nEndRow );
217 
218 	void		CopyToColumn(SCROW nRow1, SCROW nRow2, sal_uInt16 nFlags, sal_Bool bMarked,
219 								ScColumn& rColumn, const ScMarkData* pMarkData = NULL,
220 								sal_Bool bAsLink = sal_False );
221 	void		UndoToColumn(SCROW nRow1, SCROW nRow2, sal_uInt16 nFlags, sal_Bool bMarked,
222 								ScColumn& rColumn, const ScMarkData* pMarkData = NULL );
223 
224 	void		CopyScenarioFrom( const ScColumn& rSrcCol );
225 	void		CopyScenarioTo( ScColumn& rDestCol ) const;
226 	sal_Bool		TestCopyScenarioTo( const ScColumn& rDestCol ) const;
227 	void		MarkScenarioIn( ScMarkData& rDestMark ) const;
228 
229 	void		CopyUpdated( const ScColumn& rPosCol, ScColumn& rDestCol ) const;
230 
231 	void		SwapCol(ScColumn& rCol);
232 	void		MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol);
233 
234 	sal_Bool		HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst) const;
235 
236 				//	sal_True = Zahlformat gesetzt
237 	sal_Bool		SetString( SCROW nRow, SCTAB nTab, const String& rString,
238 						   formula::FormulaGrammar::AddressConvention conv = formula::FormulaGrammar::CONV_OOO,
239                            SvNumberFormatter* pFormatter = NULL,
240                            bool bDetectNumberFormat = true );
241 	void		SetValue( SCROW nRow, const double& rVal);
242 	void		SetError( SCROW nRow, const sal_uInt16 nError);
243 
244 	void		GetString( SCROW nRow, String& rString ) const;
245 	void		GetInputString( SCROW nRow, String& rString ) const;
246 	double		GetValue( SCROW nRow ) const;
247 	void		GetFormula( SCROW nRow, String& rFormula,
248 							sal_Bool bAsciiExport = sal_False ) const;
249 	CellType	GetCellType( SCROW nRow ) const;
250 	SCSIZE		GetCellCount() const { return nCount; }
251 	sal_uLong		GetWeightedCount() const;
252 	sal_uLong		GetCodeCount() const;		// RPN-Code in Formeln
253 	sal_uInt16		GetErrCode( SCROW nRow ) const;
254 
255 	sal_Bool		HasStringData( SCROW nRow ) const;
256 	sal_Bool		HasValueData( SCROW nRow ) const;
257 //UNUSED2009-05 sal_uInt16		GetErrorData( SCROW nRow) const;
258 	sal_Bool		HasStringCells( SCROW nStartRow, SCROW nEndRow ) const;
259 
260     /** Returns the pointer to a cell note object at the passed row. */
261     ScPostIt*   GetNote( SCROW nRow );
262     /** Sets the passed cell note object at the passed row. Takes ownership! */
263     void        TakeNote( SCROW nRow, ScPostIt* pNote );
264     /** Returns and forgets a cell note object at the passed row. */
265     ScPostIt*   ReleaseNote( SCROW nRow );
266     /** Deletes the note at the passed row. */
267     void        DeleteNote( SCROW nRow );
268 
269 	void		SetDirty();
270 	void		SetDirty( const ScRange& );
271 	void		SetDirtyVar();
272 	void		SetDirtyAfterLoad();
273 	void		SetTableOpDirty( const ScRange& );
274 	void		CalcAll();
275 	void		CalcAfterLoad();
276 	void		CompileAll();
277 	void		CompileXML( ScProgress& rProgress );
278 
279 	void		ResetChanged( SCROW nStartRow, SCROW nEndRow );
280 
281 	void		UpdateReference( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
282 									 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
283 									 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
284 									 ScDocument* pUndoDoc = NULL );
285 	void		UpdateInsertTab( SCTAB nTable);
286 	void		UpdateInsertTabOnlyCells( SCTAB nTable);
287 	void		UpdateDeleteTab( SCTAB nTable, sal_Bool bIsMove, ScColumn* pRefUndo = NULL );
288 	void		UpdateMoveTab(SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo);
289 	void		UpdateCompile( sal_Bool bForceIfNameInUse = sal_False );
290 	void		UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
291 									ScDocument* pUndoDoc );
292 	void		UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY );
293 
294 	void		SetTabNo(SCTAB nNewTab);
295 	sal_Bool		IsRangeNameInUse(SCROW nRow1, SCROW nRow2, sal_uInt16 nIndex) const;
296     void        FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, std::set<sal_uInt16>& rIndexes) const;
297 	void 		ReplaceRangeNamesInUse( SCROW nRow1, SCROW nRow2, const ScRangeData::IndexMap& rMap );
298 
299 	const SfxPoolItem*		GetAttr( SCROW nRow, sal_uInt16 nWhich ) const;
300 	const ScPatternAttr*	GetPattern( SCROW nRow ) const;
301     const ScPatternAttr*    GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const;
302 
303 	sal_uLong		GetNumberFormat( SCROW nRow ) const;
304 
305 	void		MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, sal_Bool bDeep ) const;
306 	void		MergePatternArea( ScMergePatternState& rState, SCROW nRow1, SCROW nRow2, sal_Bool bDeep ) const;
307 	void		MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
308 							ScLineFlags& rFlags,
309 							SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight ) const;
310 	void		ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner,
311 							SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight );
312 
313 	void		ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr );
314 	void		ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr );
315 	void		ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr );
316 	void		SetPattern( SCROW nRow, const ScPatternAttr& rPatAttr, sal_Bool bPutToPool = sal_False );
317 	void		SetPatternArea( SCROW nStartRow, SCROW nEndRow,
318 								const ScPatternAttr& rPatAttr, sal_Bool bPutToPool = sal_False );
319 	void		ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
320 							const ScPatternAttr& rPattern, short nNewType );
321 
322 	void		ApplyStyle( SCROW nRow, const ScStyleSheet& rStyle );
323 	void		ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle );
324 	void 		ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark);
325 	void		ApplySelectionLineStyle( const ScMarkData& rMark,
326 									const SvxBorderLine* pLine, sal_Bool bColorOnly );
327 
328 	const ScStyleSheet*	GetStyle( SCROW nRow ) const;
329 	const ScStyleSheet*	GetSelectionStyle( const ScMarkData& rMark, sal_Bool& rFound ) const;
330 	const ScStyleSheet*	GetAreaStyle( sal_Bool& rFound, SCROW nRow1, SCROW nRow2 ) const;
331 
332 	void		FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset );
333 	sal_Bool		IsStyleSheetUsed( const ScStyleSheet& rStyle, sal_Bool bGatherAllStyles ) const;
334 
335                 /// May return -1 if not found
336 	SCsROW		SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle,
337 								sal_Bool bUp, sal_Bool bInSelection, const ScMarkData& rMark );
338 	sal_Bool		SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow, const ScStyleSheet* pSearchStyle,
339 									sal_Bool bUp, sal_Bool bInSelection, const ScMarkData& rMark );
340 
341 	sal_Bool		ApplyFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags );
342 	sal_Bool		RemoveFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags );
343 	void		ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich );
344 
345 	void		RemoveProtected( SCROW nStartRow, SCROW nEndRow );
346 
347 	SCsROW		ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark );
348     void        DeleteSelection( sal_uInt16 nDelFlag, const ScMarkData& rMark );
349 
350 	void		ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark );
351 	void		ChangeSelectionIndent( sal_Bool bIncrement, const ScMarkData& rMark );
352 
353 	long		GetNeededSize( SCROW nRow, OutputDevice* pDev,
354 									double nPPTX, double nPPTY,
355 									const Fraction& rZoomX, const Fraction& rZoomY,
356 									sal_Bool bWidth, const ScNeededSizeOptions& rOptions );
357 	sal_uInt16		GetOptimalColWidth( OutputDevice* pDev, double nPPTX, double nPPTY,
358 									const Fraction& rZoomX, const Fraction& rZoomY,
359 									sal_Bool bFormula, sal_uInt16 nOldWidth,
360 									const ScMarkData* pMarkData,
361 									sal_Bool bSimpleTextImport );
362 	void		GetOptimalHeight( SCROW nStartRow, SCROW nEndRow, sal_uInt16* pHeight,
363 									OutputDevice* pDev,
364 									double nPPTX, double nPPTY,
365 									const Fraction& rZoomX, const Fraction& rZoomY,
366 									sal_Bool bShrink, sal_uInt16 nMinHeight, SCROW nMinStart );
367 private:
368 	long		GetSimpleTextNeededSize( SCSIZE nIndex, OutputDevice* pDev,
369 									sal_Bool bWidth );
370 public:
371 
372                 /// Including current, may return -1
373 	SCsROW		GetNextUnprotected( SCROW nRow, sal_Bool bUp ) const;
374 
375     void		GetFilterEntries(SCROW nStartRow, SCROW nEndRow, TypedScStrCollection& rStrings, bool& rHasDates);
376 	sal_Bool		GetDataEntries(SCROW nRow, TypedScStrCollection& rStrings, sal_Bool bLimit);
377 
378 //UNUSED2008-05  SCROW		NoteCount( SCROW nMaxRow = MAXROW ) const;
379 
380 	void		UpdateInsertTabAbs(SCTAB nNewPos);
381 	sal_Bool		TestTabRefAbs(SCTAB nTable);
382 	sal_Bool 		GetNextSpellingCell(SCROW& nRow, sal_Bool bInSel, const ScMarkData& rData) const;
383 
384 	void		RemoveAutoSpellObj();
385 
386 	void		StartListening( SvtListener& rLst, SCROW nRow );
387 	void		EndListening( SvtListener& rLst, SCROW nRow );
388 	void		MoveListeners( SvtBroadcaster& rSource, SCROW nDestRow );
389 	void		StartAllListeners();
390     void        StartNeededListeners(); // only for cells where NeedsListening()==TRUE
391 	void		SetRelNameDirty();
392 
393 	void 		CompileDBFormula();
394 	void 		CompileDBFormula( sal_Bool bCreateFormulaString );
395 	void 		CompileNameFormula( sal_Bool bCreateFormulaString );
396     void 		CompileColRowNameFormula();
397 
398     sal_Int32	GetMaxStringLen( SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const;
399     xub_StrLen  GetMaxNumberStringLen( sal_uInt16& nPrecision,
400                                        SCROW nRowStart, SCROW nRowEnd ) const;
401 
402 private:
403 	ScBaseCell* CloneCell(SCSIZE nIndex, sal_uInt16 nFlags, ScDocument& rDestDoc, const ScAddress& rDestPos);
404 //UNUSED2008-05  void		CorrectSymbolCells( CharSet eStreamCharSet );
405 };
406 
407 
408 class ScColumnIterator					// alle Daten eines Bereichs durchgehen
409 {
410 	const ScColumn*		pColumn;
411 	SCSIZE				nPos;
412 	SCROW				nTop;
413 	SCROW				nBottom;
414 public:
415 				ScColumnIterator( const ScColumn* pCol, SCROW nStart=0, SCROW nEnd=MAXROW );
416 				~ScColumnIterator();
417 
418 	sal_Bool		Next( SCROW& rRow, ScBaseCell*& rpCell );
419 	SCSIZE		GetIndex() const;
420 };
421 
422 
423 class ScMarkedDataIter					// Daten in selektierten Bereichen durchgehen
424 {
425 	const ScColumn*		pColumn;
426 	SCSIZE				nPos;
427 	ScMarkArrayIter*	pMarkIter;
428 	SCROW				nTop;
429 	SCROW				nBottom;
430 	sal_Bool				bNext;
431 	sal_Bool				bAll;
432 
433 public:
434 				ScMarkedDataIter( const ScColumn* pCol, const ScMarkData* pMarkData,
435 									sal_Bool bAllIfNone = sal_False );
436 				~ScMarkedDataIter();
437 
438 	sal_Bool		Next( SCSIZE& rIndex );
439 };
440 
441 
442 #endif
443 
444 
445