Line data Source code
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 "scitems.hxx"
21 : #include <svx/algitem.hxx>
22 : #include <editeng/justifyitem.hxx>
23 : #include <unotools/textsearch.hxx>
24 : #include <sfx2/objsh.hxx>
25 :
26 : #include "attrib.hxx"
27 : #include "patattr.hxx"
28 : #include "formulacell.hxx"
29 : #include "table.hxx"
30 : #include "document.hxx"
31 : #include "drwlayer.hxx"
32 : #include "olinetab.hxx"
33 : #include "stlsheet.hxx"
34 : #include "global.hxx"
35 : #include "globstr.hrc"
36 : #include "refupdat.hxx"
37 : #include "markdata.hxx"
38 : #include "progress.hxx"
39 : #include "hints.hxx"
40 : #include "prnsave.hxx"
41 : #include "tabprotection.hxx"
42 : #include "sheetevents.hxx"
43 : #include "segmenttree.hxx"
44 : #include "dbdata.hxx"
45 : #include "colorscale.hxx"
46 : #include "conditio.hxx"
47 : #include "globalnames.hxx"
48 : #include "cellvalue.hxx"
49 : #include "scmatrix.hxx"
50 : #include "refupdatecontext.hxx"
51 : #include <rowheightcontext.hxx>
52 :
53 : #include <formula/vectortoken.hxx>
54 :
55 : #include <vector>
56 : #include <boost/scoped_array.hpp>
57 :
58 : using ::std::vector;
59 :
60 : namespace {
61 :
62 825 : ScProgress* GetProgressBar(
63 : SCSIZE nCount, SCSIZE nTotalCount, ScProgress* pOuterProgress, ScDocument* pDoc)
64 : {
65 825 : if (nTotalCount < 1000)
66 : {
67 : // if the total number of rows is less than 1000, don't even bother
68 : // with the progress bar because drawing progress bar can be very
69 : // expensive especially in GTK.
70 825 : return NULL;
71 : }
72 :
73 0 : if (pOuterProgress)
74 0 : return pOuterProgress;
75 :
76 0 : if (nCount > 1)
77 : return new ScProgress(
78 0 : pDoc->GetDocumentShell(), ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING), nTotalCount);
79 :
80 0 : return NULL;
81 : }
82 :
83 825 : void GetOptimalHeightsInColumn(
84 : sc::RowHeightContext& rCxt, ScColumn* pCol, SCROW nStartRow, SCROW nEndRow,
85 : ScProgress* pProgress, sal_uInt32 nProgressStart )
86 : {
87 : assert(nStartRow <= nEndRow);
88 :
89 825 : SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
90 :
91 : // first, one time over the whole range
92 : // (with the last column in the hope that they most likely still are
93 : // on standard format)
94 :
95 825 : std::vector<sal_uInt16>& rHeights = rCxt.getHeightArray();
96 :
97 825 : pCol[MAXCOL].GetOptimalHeight(rCxt, nStartRow, nEndRow, 0, 0);
98 :
99 : // from there search for the standard height that is in use in the lower part
100 :
101 825 : sal_uInt16 nMinHeight = rHeights[nCount-1];
102 825 : SCSIZE nPos = nCount-1;
103 87035721 : while ( nPos && rHeights[nPos-1] >= nMinHeight )
104 87034071 : --nPos;
105 825 : SCROW nMinStart = nStartRow + nPos;
106 :
107 825 : sal_uLong nWeightedCount = 0;
108 844800 : for (SCCOL nCol=0; nCol<MAXCOL; nCol++) // MAXCOL already above
109 : {
110 843975 : pCol[nCol].GetOptimalHeight(rCxt, nStartRow, nEndRow, nMinHeight, nMinStart);
111 :
112 843975 : if (pProgress)
113 : {
114 0 : sal_uLong nWeight = pCol[nCol].GetWeightedCount();
115 0 : if (nWeight) // does not have to be the same Status
116 : {
117 0 : nWeightedCount += nWeight;
118 0 : pProgress->SetState( nWeightedCount + nProgressStart );
119 : }
120 : }
121 : }
122 825 : }
123 :
124 825 : struct OptimalHeightsFuncObjBase
125 : {
126 825 : virtual ~OptimalHeightsFuncObjBase() {}
127 : virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight) = 0;
128 : };
129 :
130 8 : struct SetRowHeightOnlyFunc : public OptimalHeightsFuncObjBase
131 : {
132 : ScTable* mpTab;
133 8 : SetRowHeightOnlyFunc(ScTable* pTab) :
134 8 : mpTab(pTab)
135 8 : {}
136 :
137 8 : virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight) SAL_OVERRIDE
138 : {
139 8 : mpTab->SetRowHeightOnly(nStartRow, nEndRow, nHeight);
140 8 : return false;
141 : }
142 : };
143 :
144 817 : struct SetRowHeightRangeFunc : public OptimalHeightsFuncObjBase
145 : {
146 : ScTable* mpTab;
147 : double mnPPTX;
148 : double mnPPTY;
149 :
150 817 : SetRowHeightRangeFunc(ScTable* pTab, double nPPTX, double nPPTY) :
151 : mpTab(pTab),
152 : mnPPTX(nPPTX),
153 817 : mnPPTY(nPPTY)
154 817 : {}
155 :
156 927 : virtual bool operator() (SCROW nStartRow, SCROW nEndRow, sal_uInt16 nHeight) SAL_OVERRIDE
157 : {
158 927 : return mpTab->SetRowHeightRange(nStartRow, nEndRow, nHeight, mnPPTX, mnPPTY);
159 : }
160 : };
161 :
162 825 : bool SetOptimalHeightsToRows(
163 : sc::RowHeightContext& rCxt,
164 : OptimalHeightsFuncObjBase& rFuncObj,
165 : ScBitMaskCompressedArray<SCROW, sal_uInt8>* pRowFlags, SCROW nStartRow, SCROW nEndRow )
166 : {
167 825 : SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
168 825 : bool bChanged = false;
169 825 : SCROW nRngStart = 0;
170 825 : SCROW nRngEnd = 0;
171 825 : sal_uInt16 nLast = 0;
172 1664 : for (SCSIZE i=0; i<nCount; i++)
173 : {
174 : size_t nIndex;
175 : SCROW nRegionEndRow;
176 839 : sal_uInt8 nRowFlag = pRowFlags->GetValue( nStartRow+i, nIndex, nRegionEndRow );
177 839 : if ( nRegionEndRow > nEndRow )
178 708 : nRegionEndRow = nEndRow;
179 839 : SCSIZE nMoreRows = nRegionEndRow - ( nStartRow+i ); // additional equal rows after first
180 :
181 839 : bool bAutoSize = ((nRowFlag & CR_MANUALSIZE) == 0);
182 839 : if (bAutoSize || rCxt.isForceAutoSize())
183 : {
184 714 : if (rCxt.getExtraHeight())
185 : {
186 0 : if (bAutoSize)
187 0 : pRowFlags->SetValue( nStartRow+i, nRegionEndRow, nRowFlag | CR_MANUALSIZE);
188 : }
189 714 : else if (!bAutoSize)
190 1 : pRowFlags->SetValue( nStartRow+i, nRegionEndRow, nRowFlag & ~CR_MANUALSIZE);
191 :
192 87035146 : for (SCSIZE nInner = i; nInner <= i + nMoreRows; ++nInner)
193 : {
194 87034432 : if (nLast)
195 : {
196 87033718 : if (rCxt.getHeightArray()[nInner] + rCxt.getExtraHeight() == nLast)
197 87033497 : nRngEnd = nStartRow+nInner;
198 : else
199 : {
200 221 : bChanged |= rFuncObj(nRngStart, nRngEnd, nLast);
201 221 : nLast = 0;
202 : }
203 : }
204 87034432 : if (!nLast)
205 : {
206 935 : nLast = rCxt.getHeightArray()[nInner] + rCxt.getExtraHeight();
207 935 : nRngStart = nStartRow+nInner;
208 935 : nRngEnd = nStartRow+nInner;
209 : }
210 : }
211 : }
212 : else
213 : {
214 125 : if (nLast)
215 6 : bChanged |= rFuncObj(nRngStart, nRngEnd, nLast);
216 125 : nLast = 0;
217 : }
218 839 : i += nMoreRows; // already handled - skip
219 : }
220 825 : if (nLast)
221 708 : bChanged |= rFuncObj(nRngStart, nRngEnd, nLast);
222 :
223 825 : return bChanged;
224 : }
225 :
226 : }
227 :
228 2401 : ScTable::ScTable( ScDocument* pDoc, SCTAB nNewTab, const OUString& rNewName,
229 : bool bColInfo, bool bRowInfo ) :
230 : aName( rNewName ),
231 : aCodeName( rNewName ),
232 : nLinkRefreshDelay( 0 ),
233 : nLinkMode( 0 ),
234 2401 : aPageStyle( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ),
235 : nRepeatStartX( SCCOL_REPEAT_NONE ),
236 : nRepeatEndX( SCCOL_REPEAT_NONE ),
237 : nRepeatStartY( SCROW_REPEAT_NONE ),
238 : nRepeatEndY( SCROW_REPEAT_NONE ),
239 : pTabProtection( NULL ),
240 : pColWidth( NULL ),
241 : mpRowHeights( static_cast<ScFlatUInt16RowSegments*>(NULL) ),
242 : pColFlags( NULL ),
243 : pRowFlags( NULL ),
244 2401 : mpHiddenCols(new ScFlatBoolColSegments),
245 2401 : mpHiddenRows(new ScFlatBoolRowSegments),
246 2401 : mpFilteredCols(new ScFlatBoolColSegments),
247 2401 : mpFilteredRows(new ScFlatBoolRowSegments),
248 : pOutlineTable( NULL ),
249 : pSheetEvents( NULL ),
250 : nTableAreaX( 0 ),
251 : nTableAreaY( 0 ),
252 : nTab( nNewTab ),
253 : pDocument( pDoc ),
254 : pSearchText ( NULL ),
255 : pSortCollator( NULL ),
256 : pRepeatColRange( NULL ),
257 : pRepeatRowRange( NULL ),
258 : nLockCount( 0 ),
259 : pScenarioRanges( NULL ),
260 : aScenarioColor( COL_LIGHTGRAY ),
261 : aTabBgColor( COL_AUTO ),
262 : nScenarioFlags( 0 ),
263 : pDBDataNoName(NULL),
264 : mpRangeName(NULL),
265 0 : mpCondFormatList( new ScConditionalFormatList() ),
266 : bScenario(false),
267 : bLayoutRTL(false),
268 : bLoadingRTL(false),
269 : bPageSizeValid(false),
270 : bTableAreaValid(false),
271 : bVisible(true),
272 : bStreamValid(false),
273 : bPendingRowHeights(false),
274 : bCalcNotification(false),
275 : bGlobalKeepQuery(false),
276 : bPrintEntireSheet(true),
277 : bActiveScenario(false),
278 : mbPageBreaksValid(false),
279 14406 : mbForceBreaks(false)
280 : {
281 :
282 2401 : if (bColInfo)
283 : {
284 1508 : pColWidth = new sal_uInt16[ MAXCOL+1 ];
285 1508 : pColFlags = new sal_uInt8[ MAXCOL+1 ];
286 :
287 1545700 : for (SCCOL i=0; i<=MAXCOL; i++)
288 : {
289 1544192 : pColWidth[i] = STD_COL_WIDTH;
290 1544192 : pColFlags[i] = 0;
291 : }
292 : }
293 :
294 2401 : if (bRowInfo)
295 : {
296 1513 : mpRowHeights.reset(new ScFlatUInt16RowSegments(ScGlobal::nStdRowHeight));
297 1513 : pRowFlags = new ScBitMaskCompressedArray< SCROW, sal_uInt8>( MAXROW, 0);
298 : }
299 :
300 2401 : if ( pDocument->IsDocVisible() )
301 : {
302 : // when a sheet is added to a visible document,
303 : // initialize its RTL flag from the system locale
304 47 : bLayoutRTL = ScGlobal::IsSystemRTL();
305 : }
306 :
307 2401 : ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
308 2401 : if (pDrawLayer)
309 : {
310 252 : if ( pDrawLayer->ScAddPage( nTab ) ) // sal_False (not inserted) during Undo
311 : {
312 252 : pDrawLayer->ScRenamePage( nTab, aName );
313 252 : sal_uLong nx = (sal_uLong) ((double) (MAXCOL+1) * STD_COL_WIDTH * HMM_PER_TWIPS );
314 252 : sal_uLong ny = (sal_uLong) ((double) (MAXROW+1) * ScGlobal::nStdRowHeight * HMM_PER_TWIPS );
315 252 : pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( nx, ny ), false );
316 : }
317 : }
318 :
319 2461025 : for (SCCOL k=0; k<=MAXCOL; k++)
320 2458624 : aCol[k].Init( k, nTab, pDocument );
321 2401 : }
322 :
323 2441880 : ScTable::~ScTable()
324 : {
325 2380 : if (!pDocument->IsInDtorClear())
326 : {
327 110592 : for (SCCOL nCol = 0; nCol < MAXCOL; ++nCol)
328 : {
329 110484 : aCol[nCol].FreeNotes();
330 : }
331 : // In the dtor, don't delete the pages in the wrong order.
332 : // (or else nTab does not reflect the page number!)
333 : // In ScDocument::Clear is afterwards used from Clear at the Draw Layer to delete everything.
334 :
335 108 : ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
336 108 : if (pDrawLayer)
337 21 : pDrawLayer->ScRemovePage( nTab );
338 : }
339 :
340 2380 : delete[] pColWidth;
341 2380 : delete[] pColFlags;
342 2380 : delete pRowFlags;
343 2380 : delete pSheetEvents;
344 2380 : delete pOutlineTable;
345 2380 : delete pSearchText;
346 2380 : delete pRepeatColRange;
347 2380 : delete pRepeatRowRange;
348 2380 : delete pScenarioRanges;
349 2380 : delete mpRangeName;
350 2380 : delete pDBDataNoName;
351 2380 : DestroySortCollator();
352 :
353 2439500 : for (SCCOL k=0; k<=MAXCOL; k++)
354 2437120 : aCol[k].PrepareBroadcastersForDestruction();
355 2439500 : }
356 :
357 39321 : void ScTable::GetName( OUString& rName ) const
358 : {
359 39321 : rName = aName;
360 39321 : }
361 :
362 468 : void ScTable::SetName( const OUString& rNewName )
363 : {
364 468 : aName = rNewName;
365 468 : aUpperName.clear(); // invalidated if the name is changed
366 :
367 : // SetStreamValid is handled in ScDocument::RenameTab
368 468 : }
369 :
370 11444 : const OUString& ScTable::GetUpperName() const
371 : {
372 11444 : if (aUpperName.isEmpty() && !aName.isEmpty())
373 859 : aUpperName = ScGlobal::pCharClass->uppercase(aName);
374 11444 : return aUpperName;
375 : }
376 :
377 34 : void ScTable::SetVisible( bool bVis )
378 : {
379 34 : if (bVisible != bVis && IsStreamValid())
380 0 : SetStreamValid(false);
381 :
382 34 : bVisible = bVis;
383 34 : }
384 :
385 3177 : void ScTable::SetStreamValid( bool bSet, bool bIgnoreLock )
386 : {
387 3177 : if ( bIgnoreLock || !pDocument->IsStreamValidLocked() )
388 3177 : bStreamValid = bSet;
389 3177 : }
390 :
391 20 : void ScTable::SetPendingRowHeights( bool bSet )
392 : {
393 20 : bPendingRowHeights = bSet;
394 20 : }
395 :
396 287 : void ScTable::SetLayoutRTL( bool bSet )
397 : {
398 287 : bLayoutRTL = bSet;
399 287 : }
400 :
401 18 : void ScTable::SetLoadingRTL( bool bSet )
402 : {
403 18 : bLoadingRTL = bSet;
404 18 : }
405 :
406 70 : void ScTable::SetTabBgColor(const Color& rColor)
407 : {
408 70 : if (aTabBgColor != rColor)
409 : {
410 : // The tab color has changed. Set this table 'modified'.
411 26 : aTabBgColor = rColor;
412 26 : if (IsStreamValid())
413 0 : SetStreamValid(false);
414 : }
415 70 : }
416 :
417 1 : void ScTable::SetScenario( bool bFlag )
418 : {
419 1 : bScenario = bFlag;
420 1 : }
421 :
422 4 : void ScTable::SetLink( sal_uInt8 nMode,
423 : const OUString& rDoc, const OUString& rFlt, const OUString& rOpt,
424 : const OUString& rTab, sal_uLong nRefreshDelay )
425 : {
426 4 : nLinkMode = nMode;
427 4 : aLinkDoc = rDoc; // File
428 4 : aLinkFlt = rFlt; // Filter
429 4 : aLinkOpt = rOpt; // Filter options
430 4 : aLinkTab = rTab; // Sheet name in source file
431 4 : nLinkRefreshDelay = nRefreshDelay; // refresh delay in seconds, 0==off
432 :
433 4 : if (IsStreamValid())
434 0 : SetStreamValid(false);
435 4 : }
436 :
437 98 : sal_uInt16 ScTable::GetOptimalColWidth( SCCOL nCol, OutputDevice* pDev,
438 : double nPPTX, double nPPTY,
439 : const Fraction& rZoomX, const Fraction& rZoomY,
440 : bool bFormula, const ScMarkData* pMarkData,
441 : const ScColWidthParam* pParam )
442 : {
443 98 : return aCol[nCol].GetOptimalColWidth( pDev, nPPTX, nPPTY, rZoomX, rZoomY,
444 196 : bFormula, STD_COL_WIDTH - STD_EXTRA_WIDTH, pMarkData, pParam );
445 : }
446 :
447 0 : long ScTable::GetNeededSize( SCCOL nCol, SCROW nRow,
448 : OutputDevice* pDev,
449 : double nPPTX, double nPPTY,
450 : const Fraction& rZoomX, const Fraction& rZoomY,
451 : bool bWidth, bool bTotalSize )
452 : {
453 0 : ScNeededSizeOptions aOptions;
454 0 : aOptions.bSkipMerged = false; // count merged cells
455 0 : aOptions.bTotalSize = bTotalSize;
456 :
457 0 : return aCol[nCol].GetNeededSize
458 0 : ( nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, aOptions, NULL );
459 : }
460 :
461 3119 : bool ScTable::SetOptimalHeight(
462 : sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow,
463 : ScProgress* pOuterProgress, sal_uLong nProgressStart )
464 : {
465 : assert(nStartRow <= nEndRow);
466 :
467 : OSL_ENSURE( rCxt.getExtraHeight() == 0 || rCxt.isForceAutoSize(),
468 : "automatic OptimalHeight with Extra" );
469 :
470 3119 : if ( !pDocument->IsAdjustHeightEnabled() )
471 : {
472 2302 : return false;
473 : }
474 :
475 817 : SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
476 :
477 817 : ScProgress* pProgress = GetProgressBar(nCount, GetWeightedCount(), pOuterProgress, pDocument);
478 :
479 817 : rCxt.getHeightArray().resize(nCount, 0);
480 :
481 817 : GetOptimalHeightsInColumn(rCxt, aCol, nStartRow, nEndRow, pProgress, nProgressStart);
482 :
483 817 : SetRowHeightRangeFunc aFunc(this, rCxt.getPPTX(), rCxt.getPPTY());
484 817 : bool bChanged = SetOptimalHeightsToRows(rCxt, aFunc, pRowFlags, nStartRow, nEndRow);
485 :
486 817 : if ( pProgress != pOuterProgress )
487 10 : delete pProgress;
488 :
489 817 : return bChanged;
490 : }
491 :
492 8 : void ScTable::SetOptimalHeightOnly(
493 : sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow,
494 : ScProgress* pOuterProgress, sal_uLong nProgressStart )
495 : {
496 : OSL_ENSURE( rCxt.getExtraHeight() == 0 || rCxt.isForceAutoSize(),
497 : "automatic OptimalHeight with Extra" );
498 :
499 8 : if ( !pDocument->IsAdjustHeightEnabled() )
500 8 : return;
501 :
502 8 : SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1);
503 :
504 8 : ScProgress* pProgress = GetProgressBar(nCount, GetWeightedCount(), pOuterProgress, pDocument);
505 :
506 8 : rCxt.getHeightArray().resize(nCount, 0);
507 :
508 8 : GetOptimalHeightsInColumn(rCxt, aCol, nStartRow, nEndRow, pProgress, nProgressStart);
509 :
510 8 : SetRowHeightOnlyFunc aFunc(this);
511 8 : SetOptimalHeightsToRows(rCxt, aFunc, pRowFlags, nStartRow, nEndRow);
512 :
513 8 : if ( pProgress != pOuterProgress )
514 8 : delete pProgress;
515 : }
516 :
517 31 : bool ScTable::GetCellArea( SCCOL& rEndCol, SCROW& rEndRow ) const
518 : {
519 31 : bool bFound = false;
520 31 : SCCOL nMaxX = 0;
521 31 : SCROW nMaxY = 0;
522 31775 : for (SCCOL i=0; i<=MAXCOL; i++)
523 : {
524 31744 : if (!aCol[i].IsEmptyData())
525 : {
526 159 : bFound = true;
527 159 : nMaxX = i;
528 159 : SCROW nRow = aCol[i].GetLastDataPos();
529 159 : if (nRow > nMaxY)
530 30 : nMaxY = nRow;
531 : }
532 31744 : if ( aCol[i].HasCellNotes() )
533 : {
534 5 : SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow();
535 5 : if (maxNoteRow >= nMaxY)
536 : {
537 2 : bFound = true;
538 2 : nMaxY = maxNoteRow;
539 : }
540 5 : if (i>nMaxX)
541 : {
542 5 : bFound = true;
543 5 : nMaxX = i;
544 : }
545 : }
546 : }
547 :
548 31 : rEndCol = nMaxX;
549 31 : rEndRow = nMaxY;
550 31 : return bFound;
551 : }
552 :
553 5312 : bool ScTable::GetTableArea( SCCOL& rEndCol, SCROW& rEndRow ) const
554 : {
555 5312 : bool bRet = true; //TODO: remember?
556 5312 : if (!bTableAreaValid)
557 : {
558 805 : bRet = GetPrintArea(nTableAreaX, nTableAreaY, true);
559 805 : bTableAreaValid = true;
560 : }
561 5312 : rEndCol = nTableAreaX;
562 5312 : rEndRow = nTableAreaY;
563 5312 : return bRet;
564 : }
565 :
566 : const SCCOL SC_COLUMNS_STOP = 30;
567 :
568 3172 : bool ScTable::GetPrintArea( SCCOL& rEndCol, SCROW& rEndRow, bool bNotes, bool bFullFormattedArea ) const
569 : {
570 3172 : bool bFound = false;
571 3172 : SCCOL nMaxX = 0;
572 3172 : SCROW nMaxY = 0;
573 : SCCOL i;
574 :
575 3251300 : for (i=0; i<=MAXCOL; i++) // Test data
576 : {
577 3248128 : if (!aCol[i].IsEmptyData())
578 : {
579 25367 : bFound = true;
580 25367 : if (i>nMaxX)
581 24356 : nMaxX = i;
582 25367 : SCROW nColY = aCol[i].GetLastDataPos();
583 25367 : if (nColY > nMaxY)
584 1936 : nMaxY = nColY;
585 : }
586 3248128 : if (bNotes)
587 : {
588 2875392 : if ( aCol[i].HasCellNotes() )
589 : {
590 41 : SCROW maxNoteRow = aCol[i].GetCellNotesMaxRow();
591 41 : if (maxNoteRow >= nMaxY)
592 : {
593 37 : bFound = true;
594 37 : nMaxY = maxNoteRow;
595 : }
596 41 : if (i>nMaxX)
597 : {
598 29 : bFound = true;
599 29 : nMaxX = i;
600 : }
601 : }
602 : }
603 : }
604 :
605 3172 : SCCOL nMaxDataX = nMaxX;
606 :
607 3251300 : for (i=0; i<=MAXCOL; i++) // Test attribute
608 : {
609 : SCROW nLastRow;
610 3248128 : if (aCol[i].GetLastVisibleAttr( nLastRow, bFullFormattedArea ))
611 : {
612 1343 : bFound = true;
613 1343 : nMaxX = i;
614 1343 : if (nLastRow > nMaxY)
615 8 : nMaxY = nLastRow;
616 : }
617 : }
618 :
619 3172 : if (nMaxX == MAXCOL) // omit attribute at the right
620 : {
621 0 : --nMaxX;
622 0 : while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1]) )
623 0 : --nMaxX;
624 : }
625 :
626 3172 : if ( nMaxX < nMaxDataX )
627 : {
628 185 : nMaxX = nMaxDataX;
629 : }
630 2987 : else if ( nMaxX > nMaxDataX )
631 : {
632 53 : SCCOL nAttrStartX = nMaxDataX + 1;
633 199 : while ( nAttrStartX < MAXCOL )
634 : {
635 146 : SCCOL nAttrEndX = nAttrStartX;
636 11980 : while ( nAttrEndX < MAXCOL && aCol[nAttrStartX].IsVisibleAttrEqual(aCol[nAttrEndX+1]) )
637 11688 : ++nAttrEndX;
638 146 : if ( nAttrEndX + 1 - nAttrStartX >= SC_COLUMNS_STOP )
639 : {
640 : // found equally-formatted columns behind data -> stop before these columns
641 53 : nMaxX = nAttrStartX - 1;
642 :
643 : // also don't include default-formatted columns before that
644 : SCROW nDummyRow;
645 106 : while ( nMaxX > nMaxDataX && !aCol[nMaxX].GetLastVisibleAttr( nDummyRow ) )
646 0 : --nMaxX;
647 53 : break;
648 : }
649 93 : nAttrStartX = nAttrEndX + 1;
650 : }
651 : }
652 :
653 3172 : rEndCol = nMaxX;
654 3172 : rEndRow = nMaxY;
655 3172 : return bFound;
656 : }
657 :
658 0 : bool ScTable::GetPrintAreaHor( SCROW nStartRow, SCROW nEndRow,
659 : SCCOL& rEndCol, bool /* bNotes */ ) const
660 : {
661 0 : bool bFound = false;
662 0 : SCCOL nMaxX = 0;
663 : SCCOL i;
664 :
665 0 : for (i=0; i<=MAXCOL; i++) // Test attribute
666 : {
667 0 : if (aCol[i].HasVisibleAttrIn( nStartRow, nEndRow ))
668 : {
669 0 : bFound = true;
670 0 : nMaxX = i;
671 : }
672 : }
673 :
674 0 : if (nMaxX == MAXCOL) // omit attribute at the right
675 : {
676 0 : --nMaxX;
677 0 : while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], nStartRow, nEndRow) )
678 0 : --nMaxX;
679 : }
680 :
681 0 : for (i=0; i<=MAXCOL; i++) // Daten testen
682 : {
683 0 : if (!aCol[i].IsEmptyBlock( nStartRow, nEndRow )) //TODO: bNotes ??????
684 : {
685 0 : bFound = true;
686 0 : if (i>nMaxX)
687 0 : nMaxX = i;
688 : }
689 : }
690 :
691 0 : rEndCol = nMaxX;
692 0 : return bFound;
693 : }
694 :
695 40 : bool ScTable::GetPrintAreaVer( SCCOL nStartCol, SCCOL nEndCol,
696 : SCROW& rEndRow, bool bNotes ) const
697 : {
698 40 : bool bFound = false;
699 40 : SCROW nMaxY = 0;
700 : SCCOL i;
701 :
702 164 : for (i=nStartCol; i<=nEndCol; i++) // Test attribute
703 : {
704 : SCROW nLastRow;
705 124 : if (aCol[i].GetLastVisibleAttr( nLastRow ))
706 : {
707 0 : bFound = true;
708 0 : if (nLastRow > nMaxY)
709 0 : nMaxY = nLastRow;
710 : }
711 : }
712 :
713 164 : for (i=nStartCol; i<=nEndCol; i++) // Test data
714 : {
715 124 : if (!aCol[i].IsEmptyData())
716 : {
717 49 : bFound = true;
718 49 : SCROW nColY = aCol[i].GetLastDataPos();
719 49 : if (nColY > nMaxY)
720 13 : nMaxY = nColY;
721 : }
722 124 : if (bNotes)
723 : {
724 8 : if ( aCol[i].HasCellNotes() )
725 : {
726 4 : SCROW maxNoteRow =aCol[i].GetCellNotesMaxRow();
727 4 : if (maxNoteRow > nMaxY)
728 : {
729 4 : bFound = true;
730 4 : nMaxY = maxNoteRow;
731 : }
732 : }
733 : }
734 : }
735 :
736 40 : rEndRow = nMaxY;
737 40 : return bFound;
738 : }
739 :
740 2037 : bool ScTable::GetDataStart( SCCOL& rStartCol, SCROW& rStartRow ) const
741 : {
742 2037 : bool bFound = false;
743 2037 : SCCOL nMinX = MAXCOL;
744 2037 : SCROW nMinY = MAXROW;
745 : SCCOL i;
746 :
747 2087925 : for (i=0; i<=MAXCOL; i++) // Test attribute
748 : {
749 : SCROW nFirstRow;
750 2085888 : if (aCol[i].GetFirstVisibleAttr( nFirstRow ))
751 : {
752 946 : if (!bFound)
753 111 : nMinX = i;
754 946 : bFound = true;
755 946 : if (nFirstRow < nMinY)
756 199 : nMinY = nFirstRow;
757 : }
758 : }
759 :
760 2037 : if (nMinX == 0) // omit attribute at the right
761 : {
762 103 : if ( aCol[0].IsVisibleAttrEqual(aCol[1]) ) // no single ones
763 : {
764 14 : ++nMinX;
765 112 : while ( nMinX<MAXCOL && aCol[nMinX].IsVisibleAttrEqual(aCol[nMinX-1]) )
766 84 : ++nMinX;
767 : }
768 : }
769 :
770 2037 : bool bDatFound = false;
771 2087925 : for (i=0; i<=MAXCOL; i++) // Test data
772 : {
773 2085888 : if (!aCol[i].IsEmptyData())
774 : {
775 12542 : if (!bDatFound && i<nMinX)
776 1125 : nMinX = i;
777 12542 : bFound = bDatFound = true;
778 12542 : SCROW nRow = aCol[i].GetFirstDataPos();
779 12542 : if (nRow < nMinY)
780 1257 : nMinY = nRow;
781 : }
782 2085888 : if ( aCol[i].HasCellNotes() )
783 : {
784 34 : SCROW minNoteRow = aCol[i].GetCellNotesMinRow();
785 34 : if (minNoteRow <= nMinY)
786 : {
787 24 : bFound = true;
788 24 : nMinY = minNoteRow;
789 : }
790 34 : if (i<nMinX)
791 : {
792 20 : bFound = true;
793 20 : nMinX = i;
794 : }
795 : }
796 : }
797 2037 : rStartCol = nMinX;
798 2037 : rStartRow = nMinY;
799 2037 : return bFound;
800 : }
801 :
802 53 : void ScTable::GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow,
803 : bool bIncludeOld, bool bOnlyDown ) const
804 : {
805 : // return the smallest area containing at least all contiguous cells having data. This area
806 : // is a square containing also empty cells. It may shrink or extend the area given as input
807 : // Flags as modifiers:
808 : //
809 : // bIncludeOld = true ensure that the returned area contains at least the initial area,
810 : // independently of the emptniess of rows / columns (i.e. does not allow shrinking)
811 : // bOnlyDown = true means extend / shrink the inputed area only down, i.e modifiy only rEndRow
812 :
813 53 : bool bLeft = false;
814 53 : bool bRight = false;
815 53 : bool bTop = false;
816 53 : bool bBottom = false;
817 53 : bool bChanged = false;
818 :
819 444 : do
820 : {
821 444 : bChanged = false;
822 :
823 444 : if (!bOnlyDown)
824 : {
825 443 : SCROW nStart = rStartRow;
826 443 : SCROW nEnd = rEndRow;
827 443 : if (nStart>0) --nStart;
828 443 : if (nEnd<MAXROW) ++nEnd;
829 :
830 443 : if (rEndCol < MAXCOL)
831 443 : if (!aCol[rEndCol+1].IsEmptyBlock(nStart,nEnd))
832 : {
833 300 : ++rEndCol;
834 300 : bChanged = true;
835 300 : bRight = true;
836 : }
837 :
838 443 : if (rStartCol > 0)
839 107 : if (!aCol[rStartCol-1].IsEmptyBlock(nStart,nEnd))
840 : {
841 0 : --rStartCol;
842 0 : bChanged = true;
843 0 : bLeft = true;
844 : }
845 :
846 443 : if (rStartRow > 0)
847 : {
848 107 : SCROW nTest = rStartRow-1;
849 107 : bool needExtend = false;
850 316 : for ( SCCOL i = rStartCol; i<=rEndCol && !needExtend; i++)
851 209 : if (aCol[i].HasDataAt(nTest))
852 66 : needExtend = true;
853 107 : if (needExtend)
854 : {
855 66 : --rStartRow;
856 66 : bChanged = true;
857 66 : bTop = true;
858 : }
859 : }
860 : }
861 :
862 444 : if (rEndRow < MAXROW)
863 : {
864 444 : SCROW nTest = rEndRow+1;
865 444 : bool needExtend = false;
866 2180 : for ( SCCOL i = rStartCol; i<=rEndCol && !needExtend; i++)
867 1736 : if (aCol[i].HasDataAt(nTest))
868 212 : needExtend = true;
869 444 : if (needExtend)
870 : {
871 212 : ++rEndRow;
872 212 : bChanged = true;
873 212 : bBottom = true;
874 : }
875 : }
876 : }
877 : while( bChanged );
878 :
879 53 : if ( !bIncludeOld && !bOnlyDown )
880 : {
881 16 : if ( !bLeft )
882 32 : while ( aCol[rStartCol].IsEmptyBlock(rStartRow,rEndRow) && rStartCol < MAXCOL && rStartCol < rEndCol)
883 0 : ++rStartCol;
884 :
885 16 : if ( !bRight )
886 0 : while ( aCol[rEndCol].IsEmptyBlock(rStartRow,rEndRow) && rEndCol > 0 && rStartCol < rEndCol)
887 0 : --rEndCol;
888 :
889 16 : if ( !bTop && rStartRow < MAXROW && rStartRow < rEndRow )
890 : {
891 16 : bool bShrink = true;
892 16 : do
893 : {
894 32 : for ( SCCOL i = rStartCol; i<=rEndCol && bShrink; i++)
895 16 : if (aCol[i].HasDataAt(rStartRow))
896 16 : bShrink = false;
897 16 : if (bShrink)
898 0 : ++rStartRow;
899 0 : } while (bShrink && rStartRow < MAXROW && rStartRow < rEndRow);
900 : }
901 : }
902 :
903 53 : if ( !bIncludeOld )
904 : {
905 16 : if ( !bBottom && rEndRow > 0 && rStartRow < rEndRow )
906 : {
907 0 : SCROW nLastDataRow = GetLastDataRow( rStartCol, rEndCol, rEndRow);
908 0 : if (nLastDataRow < rEndRow)
909 0 : rEndRow = std::max( rStartRow, nLastDataRow);
910 : }
911 : }
912 53 : }
913 :
914 39 : bool ScTable::ShrinkToUsedDataArea( bool& o_bShrunk, SCCOL& rStartCol, SCROW& rStartRow,
915 : SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly, bool bStickyTopRow, bool bStickyLeftCol ) const
916 : {
917 39 : o_bShrunk = false;
918 :
919 39 : PutInOrder( rStartCol, rEndCol);
920 39 : PutInOrder( rStartRow, rEndRow);
921 39 : if (rStartCol < 0)
922 0 : rStartCol = 0, o_bShrunk = true;
923 39 : if (rStartRow < 0)
924 0 : rStartRow = 0, o_bShrunk = true;
925 39 : if (rEndCol > MAXCOL)
926 0 : rEndCol = MAXCOL, o_bShrunk = true;
927 39 : if (rEndRow > MAXROW)
928 0 : rEndRow = MAXROW, o_bShrunk = true;
929 :
930 2132 : while (rStartCol < rEndCol)
931 : {
932 2075 : if (aCol[rEndCol].IsEmptyBlock( rStartRow, rEndRow))
933 : {
934 2054 : --rEndCol;
935 2054 : o_bShrunk = true;
936 : }
937 : else
938 21 : break; // while
939 : }
940 :
941 39 : if (!bStickyLeftCol)
942 : {
943 70 : while (rStartCol < rEndCol)
944 : {
945 17 : if (aCol[rStartCol].IsEmptyBlock( rStartRow, rEndRow))
946 : {
947 0 : ++rStartCol;
948 0 : o_bShrunk = true;
949 : }
950 : else
951 17 : break; // while
952 : }
953 : }
954 :
955 39 : if (!bColumnsOnly)
956 : {
957 39 : if (!bStickyTopRow)
958 : {
959 31 : while (rStartRow < rEndRow)
960 : {
961 15 : bool bFound = false;
962 30 : for (SCCOL i=rStartCol; i<=rEndCol && !bFound; i++)
963 : {
964 15 : if (aCol[i].HasDataAt( rStartRow))
965 12 : bFound = true;
966 : }
967 15 : if (!bFound)
968 : {
969 3 : ++rStartRow;
970 3 : o_bShrunk = true;
971 : }
972 : else
973 12 : break; // while
974 : }
975 : }
976 :
977 84 : while (rStartRow < rEndRow)
978 : {
979 41 : SCROW nLastDataRow = GetLastDataRow( rStartCol, rEndCol, rEndRow);
980 41 : if (0 <= nLastDataRow && nLastDataRow < rEndRow)
981 : {
982 6 : rEndRow = std::max( rStartRow, nLastDataRow);
983 6 : o_bShrunk = true;
984 : }
985 : else
986 : break; // while
987 : }
988 : }
989 :
990 96 : return rStartCol != rEndCol || (bColumnsOnly ?
991 0 : !aCol[rStartCol].IsEmptyBlock( rStartRow, rEndRow) :
992 58 : (rStartRow != rEndRow || aCol[rStartCol].HasDataAt( rStartRow)));
993 : }
994 :
995 43 : SCROW ScTable::GetLastDataRow( SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
996 : {
997 43 : if (!ValidCol(nCol1) || !ValidCol(nCol2))
998 0 : return -1;
999 :
1000 43 : SCROW nNewLastRow = 0;
1001 132 : for (SCCOL i = nCol1; i <= nCol2; ++i)
1002 : {
1003 89 : SCROW nThis = aCol[i].GetLastDataPos(nLastRow);
1004 89 : if (nNewLastRow < nThis)
1005 43 : nNewLastRow = nThis;
1006 : }
1007 :
1008 43 : return nNewLastRow;
1009 : }
1010 :
1011 5989 : SCSIZE ScTable::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow,
1012 : SCCOL nEndCol, SCROW nEndRow, ScDirection eDir ) const
1013 : {
1014 5989 : SCSIZE nCount = 0;
1015 : SCCOL nCol;
1016 5999 : if ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP))
1017 : {
1018 10 : nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
1019 43 : for (nCol = nStartCol; nCol <= nEndCol; nCol++)
1020 33 : nCount = std::min(nCount, aCol[nCol].GetEmptyLinesInBlock(nStartRow, nEndRow, eDir));
1021 : }
1022 5979 : else if (eDir == DIR_RIGHT)
1023 : {
1024 5977 : nCol = nEndCol;
1025 12068736 : while (((SCsCOL)nCol >= (SCsCOL)nStartCol) &&
1026 6028938 : aCol[nCol].IsEmptyBlock(nStartRow, nEndRow))
1027 : {
1028 6027844 : nCount++;
1029 6027844 : nCol--;
1030 : }
1031 : }
1032 : else
1033 : {
1034 2 : nCol = nStartCol;
1035 4 : while ((nCol <= nEndCol) && aCol[nCol].IsEmptyBlock(nStartRow, nEndRow))
1036 : {
1037 0 : nCount++;
1038 0 : nCol++;
1039 : }
1040 : }
1041 5989 : return nCount;
1042 : }
1043 :
1044 0 : bool ScTable::IsEmptyLine( SCROW nRow, SCCOL nStartCol, SCCOL nEndCol ) const
1045 : {
1046 0 : bool bFound = false;
1047 0 : for (SCCOL i=nStartCol; i<=nEndCol && !bFound; i++)
1048 0 : if (aCol[i].HasDataAt(nRow))
1049 0 : bFound = true;
1050 0 : return !bFound;
1051 : }
1052 :
1053 0 : void ScTable::LimitChartArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow ) const
1054 : {
1055 0 : while ( rStartCol<rEndCol && aCol[rStartCol].IsEmptyBlock(rStartRow,rEndRow) )
1056 0 : ++rStartCol;
1057 :
1058 0 : while ( rStartCol<rEndCol && aCol[rEndCol].IsEmptyBlock(rStartRow,rEndRow) )
1059 0 : --rEndCol;
1060 :
1061 0 : while ( rStartRow<rEndRow && IsEmptyLine(rStartRow, rStartCol, rEndCol) )
1062 0 : ++rStartRow;
1063 :
1064 0 : while ( rStartRow<rEndRow && IsEmptyLine(rEndRow, rStartCol, rEndCol) )
1065 0 : --rEndRow;
1066 0 : }
1067 :
1068 1616 : SCCOL ScTable::FindNextVisibleCol( SCCOL nCol, bool bRight ) const
1069 : {
1070 1616 : if(bRight)
1071 : {
1072 1601 : nCol++;
1073 1601 : SCCOL nEnd = 0;
1074 1601 : bool bHidden = pDocument->ColHidden(nCol, nTab, NULL, &nEnd);
1075 1601 : if(bHidden)
1076 2 : nCol = nEnd +1;
1077 :
1078 1601 : return std::min<SCCOL>(MAXCOL, nCol);
1079 : }
1080 : else
1081 : {
1082 15 : nCol--;
1083 15 : SCCOL nStart = MAXCOL;
1084 15 : bool bHidden = pDocument->ColHidden(nCol, nTab, &nStart, NULL);
1085 15 : if(bHidden)
1086 4 : nCol = nStart - 1;
1087 :
1088 15 : return std::max<SCCOL>(0, nCol);
1089 : }
1090 : }
1091 :
1092 18 : SCCOL ScTable::FindNextVisibleColWithContent( SCCOL nCol, bool bRight, SCROW nRow ) const
1093 : {
1094 18 : if(bRight)
1095 : {
1096 12 : if(nCol == MAXCOL)
1097 0 : return MAXCOL;
1098 :
1099 6140 : do
1100 : {
1101 6146 : nCol++;
1102 6146 : SCCOL nEndCol = 0;
1103 6146 : bool bHidden = pDocument->ColHidden( nCol, nTab, NULL, &nEndCol );
1104 6146 : if(bHidden)
1105 : {
1106 2 : nCol = nEndCol +1;
1107 2 : if(nEndCol >= MAXCOL)
1108 6 : return MAXCOL;
1109 : }
1110 :
1111 6146 : if(aCol[nCol].HasVisibleDataAt(nRow))
1112 6 : return nCol;
1113 : }
1114 : while(nCol < MAXCOL);
1115 :
1116 6 : return MAXCOL;
1117 : }
1118 : else
1119 : {
1120 6 : if(nCol == 0)
1121 0 : return 0;
1122 :
1123 6 : do
1124 : {
1125 11 : nCol--;
1126 11 : SCCOL nStartCol = MAXCOL;
1127 11 : bool bHidden = pDocument->ColHidden( nCol, nTab, &nStartCol, NULL );
1128 11 : if(bHidden)
1129 : {
1130 0 : nCol = nStartCol -1;
1131 0 : if(nCol <= 0)
1132 5 : return 0;
1133 : }
1134 :
1135 11 : if(aCol[nCol].HasVisibleDataAt(nRow))
1136 5 : return nCol;
1137 : }
1138 : while(nCol > 0);
1139 :
1140 1 : return 0;
1141 : }
1142 : }
1143 :
1144 98 : void ScTable::FindAreaPos( SCCOL& rCol, SCROW& rRow, ScMoveDirection eDirection ) const
1145 : {
1146 98 : if (eDirection == SC_MOVE_LEFT || eDirection == SC_MOVE_RIGHT)
1147 : {
1148 49 : SCCOL nNewCol = rCol;
1149 49 : bool bThere = aCol[nNewCol].HasVisibleDataAt(rRow);
1150 49 : bool bRight = (eDirection == SC_MOVE_RIGHT);
1151 49 : if (bThere)
1152 : {
1153 36 : if(nNewCol >= MAXCOL && eDirection == SC_MOVE_RIGHT)
1154 0 : return;
1155 36 : else if(nNewCol == 0 && eDirection == SC_MOVE_LEFT)
1156 8 : return;
1157 :
1158 28 : SCCOL nNextCol = FindNextVisibleCol( nNewCol, bRight );
1159 :
1160 28 : if(aCol[nNextCol].HasVisibleDataAt(rRow))
1161 : {
1162 23 : bool bFound = false;
1163 23 : nNewCol = nNextCol;
1164 1588 : do
1165 : {
1166 1588 : nNextCol = FindNextVisibleCol( nNewCol, bRight );
1167 1588 : if(aCol[nNextCol].HasVisibleDataAt(rRow))
1168 1567 : nNewCol = nNextCol;
1169 : else
1170 21 : bFound = true;
1171 : }
1172 3155 : while(!bFound && nNextCol > 0 && nNextCol < MAXCOL);
1173 : }
1174 : else
1175 : {
1176 5 : nNewCol = FindNextVisibleColWithContent(nNewCol, bRight, rRow);
1177 : }
1178 : }
1179 : else
1180 : {
1181 13 : nNewCol = FindNextVisibleColWithContent(nNewCol, bRight, rRow);
1182 : }
1183 :
1184 41 : if (nNewCol<0)
1185 0 : nNewCol=0;
1186 41 : if (nNewCol>MAXCOL)
1187 0 : nNewCol=MAXCOL;
1188 41 : rCol = nNewCol;
1189 : }
1190 : else
1191 : {
1192 49 : aCol[rCol].FindDataAreaPos(rRow,eDirection == SC_MOVE_DOWN);
1193 : }
1194 : }
1195 :
1196 0 : bool ScTable::ValidNextPos( SCCOL nCol, SCROW nRow, const ScMarkData& rMark,
1197 : bool bMarked, bool bUnprotected ) const
1198 : {
1199 0 : if (!ValidCol(nCol) || !ValidRow(nRow))
1200 0 : return false;
1201 :
1202 0 : if (pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HASATTR_OVERLAPPED))
1203 : // Skip an overlapped cell.
1204 0 : return false;
1205 :
1206 0 : if (bMarked && !rMark.IsCellMarked(nCol,nRow))
1207 0 : return false;
1208 :
1209 0 : if (bUnprotected && static_cast<const ScProtectionAttr*>(
1210 0 : GetAttr(nCol,nRow,ATTR_PROTECTION))->GetProtection())
1211 0 : return false;
1212 :
1213 0 : if (bMarked || bUnprotected) //TODO: also in other case ???
1214 : {
1215 : // Hidden cells must be skipped, as the cursor would end up on the next cell
1216 : // even if it is protected or not marked.
1217 : //TODO: control per Extra-Parameter, only for Cursor movement ???
1218 :
1219 0 : if (RowHidden(nRow))
1220 0 : return false;
1221 :
1222 0 : if (ColHidden(nCol))
1223 0 : return false;
1224 : }
1225 :
1226 0 : return true;
1227 : }
1228 :
1229 0 : void ScTable::GetNextPos( SCCOL& rCol, SCROW& rRow, SCsCOL nMovX, SCsROW nMovY,
1230 : bool bMarked, bool bUnprotected, const ScMarkData& rMark ) const
1231 : {
1232 0 : if (bUnprotected && !IsProtected()) // Is sheet really protected?
1233 0 : bUnprotected = false;
1234 :
1235 0 : sal_uInt16 nWrap = 0;
1236 0 : SCsCOL nCol = rCol;
1237 0 : SCsROW nRow = rRow;
1238 :
1239 0 : nCol = sal::static_int_cast<SCsCOL>( nCol + nMovX );
1240 0 : nRow = sal::static_int_cast<SCsROW>( nRow + nMovY );
1241 :
1242 : OSL_ENSURE( !nMovY || !bUnprotected,
1243 : "GetNextPos with bUnprotected horizontal not implemented" );
1244 :
1245 0 : if ( nMovY && bMarked )
1246 : {
1247 0 : bool bUp = ( nMovY < 0 );
1248 0 : nRow = rMark.GetNextMarked( nCol, nRow, bUp );
1249 0 : while ( ValidRow(nRow) &&
1250 0 : (RowHidden(nRow) || pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HASATTR_OVERLAPPED)) )
1251 : {
1252 : // skip hidden rows (see above)
1253 0 : nRow += nMovY;
1254 0 : nRow = rMark.GetNextMarked( nCol, nRow, bUp );
1255 : }
1256 :
1257 0 : while ( nRow < 0 || nRow > MAXROW )
1258 : {
1259 0 : nCol = sal::static_int_cast<SCsCOL>( nCol + static_cast<SCsCOL>(nMovY) );
1260 0 : while ( ValidCol(nCol) && ColHidden(nCol) )
1261 0 : nCol = sal::static_int_cast<SCsCOL>( nCol + static_cast<SCsCOL>(nMovY) ); // skip hidden rows (see above)
1262 0 : if (nCol < 0)
1263 : {
1264 0 : nCol = MAXCOL;
1265 0 : if (++nWrap >= 2)
1266 0 : return;
1267 : }
1268 0 : else if (nCol > MAXCOL)
1269 : {
1270 0 : nCol = 0;
1271 0 : if (++nWrap >= 2)
1272 0 : return;
1273 : }
1274 0 : if (nRow < 0)
1275 0 : nRow = MAXROW;
1276 0 : else if (nRow > MAXROW)
1277 0 : nRow = 0;
1278 0 : nRow = rMark.GetNextMarked( nCol, nRow, bUp );
1279 0 : while ( ValidRow(nRow) &&
1280 0 : (RowHidden(nRow) || pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HASATTR_OVERLAPPED)) )
1281 : {
1282 : // skip hidden rows (see above)
1283 0 : nRow += nMovY;
1284 0 : nRow = rMark.GetNextMarked( nCol, nRow, bUp );
1285 : }
1286 : }
1287 : }
1288 :
1289 0 : if ( nMovX && ( bMarked || bUnprotected ) )
1290 : {
1291 : // wrap initial skip counting:
1292 0 : if (nCol<0)
1293 : {
1294 0 : nCol = MAXCOL;
1295 0 : --nRow;
1296 0 : if (nRow<0)
1297 0 : nRow = MAXROW;
1298 : }
1299 0 : if (nCol>MAXCOL)
1300 : {
1301 0 : nCol = 0;
1302 0 : ++nRow;
1303 0 : if (nRow>MAXROW)
1304 0 : nRow = 0;
1305 : }
1306 :
1307 0 : if ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) )
1308 : {
1309 0 : boost::scoped_array<SCsROW> pNextRows(new SCsROW[MAXCOL+1]);
1310 : SCCOL i;
1311 :
1312 0 : if ( nMovX > 0 ) // forward
1313 : {
1314 0 : for (i=0; i<=MAXCOL; i++)
1315 0 : pNextRows[i] = (i<nCol) ? (nRow+1) : nRow;
1316 0 : do
1317 : {
1318 0 : SCsROW nNextRow = pNextRows[nCol] + 1;
1319 0 : if ( bMarked )
1320 0 : nNextRow = rMark.GetNextMarked( nCol, nNextRow, false );
1321 0 : if ( bUnprotected )
1322 0 : nNextRow = aCol[nCol].GetNextUnprotected( nNextRow, false );
1323 0 : pNextRows[nCol] = nNextRow;
1324 :
1325 0 : SCsROW nMinRow = MAXROW+1;
1326 0 : for (i=0; i<=MAXCOL; i++)
1327 0 : if (pNextRows[i] < nMinRow) // when two equal on the left
1328 : {
1329 0 : nMinRow = pNextRows[i];
1330 0 : nCol = i;
1331 : }
1332 0 : nRow = nMinRow;
1333 :
1334 0 : if ( nRow > MAXROW )
1335 : {
1336 0 : if (++nWrap >= 2) break; // handle invalid value
1337 0 : nCol = 0;
1338 0 : nRow = 0;
1339 0 : for (i=0; i<=MAXCOL; i++)
1340 0 : pNextRows[i] = 0; // do it all over again
1341 : }
1342 : }
1343 0 : while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) );
1344 : }
1345 : else // backwards
1346 : {
1347 0 : for (i=0; i<=MAXCOL; i++)
1348 0 : pNextRows[i] = (i>nCol) ? (nRow-1) : nRow;
1349 0 : do
1350 : {
1351 0 : SCsROW nNextRow = pNextRows[nCol] - 1;
1352 0 : if ( bMarked )
1353 0 : nNextRow = rMark.GetNextMarked( nCol, nNextRow, true );
1354 0 : if ( bUnprotected )
1355 0 : nNextRow = aCol[nCol].GetNextUnprotected( nNextRow, true );
1356 0 : pNextRows[nCol] = nNextRow;
1357 :
1358 0 : SCsROW nMaxRow = -1;
1359 0 : for (i=0; i<=MAXCOL; i++)
1360 0 : if (pNextRows[i] >= nMaxRow) // when two equal on the right
1361 : {
1362 0 : nMaxRow = pNextRows[i];
1363 0 : nCol = i;
1364 : }
1365 0 : nRow = nMaxRow;
1366 :
1367 0 : if ( nRow < 0 )
1368 : {
1369 0 : if (++nWrap >= 2) break; // handle invalid value
1370 0 : nCol = MAXCOL;
1371 0 : nRow = MAXROW;
1372 0 : for (i=0; i<=MAXCOL; i++)
1373 0 : pNextRows[i] = MAXROW; // do it all over again
1374 : }
1375 : }
1376 0 : while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) );
1377 0 : }
1378 : }
1379 : }
1380 :
1381 : // Invalid values show up for instane for Tab, when nothing is selected and not
1382 : // protected (left / right edge), then leave values unchanged.
1383 :
1384 0 : if (ValidColRow(nCol,nRow))
1385 : {
1386 0 : rCol = nCol;
1387 0 : rRow = nRow;
1388 : }
1389 : }
1390 :
1391 31 : bool ScTable::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark ) const
1392 : {
1393 31 : const ScMarkArray* pMarkArray = rMark.GetArray();
1394 : OSL_ENSURE(pMarkArray,"GetNextMarkedCell without MarkArray");
1395 31 : if ( !pMarkArray )
1396 0 : return false;
1397 :
1398 31 : ++rRow; // next row
1399 :
1400 2298 : while ( rCol <= MAXCOL )
1401 : {
1402 2265 : const ScMarkArray& rArray = pMarkArray[rCol];
1403 6766 : while ( rRow <= MAXROW )
1404 : {
1405 2265 : SCROW nStart = (SCROW) rArray.GetNextMarked( (SCsROW) rRow, false );
1406 2265 : if ( nStart <= MAXROW )
1407 : {
1408 29 : SCROW nEnd = rArray.GetMarkEnd( nStart, false );
1409 :
1410 29 : const sc::CellStoreType& rCells = aCol[rCol].maCells;
1411 29 : std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nStart);
1412 29 : sc::CellStoreType::const_iterator it = aPos.first;
1413 29 : SCROW nTestRow = nStart;
1414 29 : if (it->type == sc::element_type_empty)
1415 : {
1416 : // Skip the empty block.
1417 24 : nTestRow += it->size - aPos.second;
1418 24 : ++it;
1419 24 : if (it == rCells.end())
1420 : {
1421 : // No more block.
1422 24 : rRow = MAXROW + 1;
1423 53 : return false;
1424 : }
1425 : }
1426 :
1427 5 : if (nTestRow <= nEnd)
1428 : {
1429 : // Cell found.
1430 5 : rRow = nTestRow;
1431 5 : return true;
1432 : }
1433 :
1434 0 : rRow = nEnd + 1; // Search for next selected range
1435 : }
1436 : else
1437 2236 : rRow = MAXROW + 1; // End of column
1438 : }
1439 2236 : rRow = 0;
1440 2236 : ++rCol; // test next column
1441 : }
1442 :
1443 2 : return false; // Through all columns
1444 : }
1445 :
1446 199 : void ScTable::UpdateDrawRef( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
1447 : SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
1448 : SCsCOL nDx, SCsROW nDy, SCsTAB nDz, bool bUpdateNoteCaptionPos )
1449 : {
1450 199 : if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 ) // only within the table
1451 : {
1452 169 : ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer();
1453 169 : if ( eUpdateRefMode != URM_COPY && pDrawLayer )
1454 : {
1455 30 : if ( eUpdateRefMode == URM_MOVE )
1456 : { // source range
1457 0 : nCol1 = sal::static_int_cast<SCCOL>( nCol1 - nDx );
1458 0 : nRow1 = sal::static_int_cast<SCROW>( nRow1 - nDy );
1459 0 : nCol2 = sal::static_int_cast<SCCOL>( nCol2 - nDx );
1460 0 : nRow2 = sal::static_int_cast<SCROW>( nRow2 - nDy );
1461 : }
1462 : pDrawLayer->MoveArea( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy,
1463 30 : (eUpdateRefMode == URM_INSDEL), bUpdateNoteCaptionPos );
1464 : }
1465 : }
1466 199 : }
1467 :
1468 258 : void ScTable::UpdateReference(
1469 : sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, bool bIncludeDraw, bool bUpdateNoteCaptionPos )
1470 : {
1471 258 : bool bUpdated = false;
1472 : SCCOL i;
1473 : SCCOL iMax;
1474 258 : if (rCxt.meMode == URM_COPY )
1475 : {
1476 52 : i = rCxt.maRange.aStart.Col();
1477 52 : iMax = rCxt.maRange.aEnd.Col();
1478 : }
1479 : else
1480 : {
1481 206 : i = 0;
1482 206 : iMax = MAXCOL;
1483 : }
1484 :
1485 258 : UpdateRefMode eUpdateRefMode = rCxt.meMode;
1486 258 : SCCOL nDx = rCxt.mnColDelta;
1487 258 : SCROW nDy = rCxt.mnRowDelta;
1488 258 : SCTAB nDz = rCxt.mnTabDelta;
1489 258 : SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
1490 258 : SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
1491 258 : SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
1492 :
1493 : // Named expressions need to be updated before formulas acessing them.
1494 258 : if (mpRangeName)
1495 56 : mpRangeName->UpdateReference(rCxt, nTab);
1496 :
1497 229739 : for ( ; i<=iMax; i++)
1498 229481 : bUpdated |= aCol[i].UpdateReference(rCxt, pUndoDoc);
1499 :
1500 258 : if ( bIncludeDraw )
1501 145 : UpdateDrawRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz, bUpdateNoteCaptionPos );
1502 :
1503 258 : if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 ) // print ranges: only within the table
1504 : {
1505 212 : SCTAB nSTab = nTab;
1506 212 : SCTAB nETab = nTab;
1507 212 : SCCOL nSCol = 0;
1508 212 : SCROW nSRow = 0;
1509 212 : SCCOL nECol = 0;
1510 212 : SCROW nERow = 0;
1511 212 : bool bRecalcPages = false;
1512 :
1513 212 : for ( ScRangeVec::iterator aIt = aPrintRanges.begin(), aEnd = aPrintRanges.end(); aIt != aEnd; ++aIt )
1514 : {
1515 0 : nSCol = aIt->aStart.Col();
1516 0 : nSRow = aIt->aStart.Row();
1517 0 : nECol = aIt->aEnd.Col();
1518 0 : nERow = aIt->aEnd.Row();
1519 :
1520 : // do not try to modify sheet index of print range
1521 0 : if ( ScRefUpdate::Update( pDocument, eUpdateRefMode,
1522 : nCol1,nRow1,nTab, nCol2,nRow2,nTab,
1523 : nDx,nDy,0,
1524 0 : nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
1525 : {
1526 0 : *aIt = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
1527 0 : bRecalcPages = true;
1528 : }
1529 : }
1530 :
1531 212 : if ( pRepeatColRange )
1532 : {
1533 0 : nSCol = pRepeatColRange->aStart.Col();
1534 0 : nSRow = pRepeatColRange->aStart.Row();
1535 0 : nECol = pRepeatColRange->aEnd.Col();
1536 0 : nERow = pRepeatColRange->aEnd.Row();
1537 :
1538 : // do not try to modify sheet index of repeat range
1539 0 : if ( ScRefUpdate::Update( pDocument, eUpdateRefMode,
1540 : nCol1,nRow1,nTab, nCol2,nRow2,nTab,
1541 : nDx,nDy,0,
1542 0 : nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
1543 : {
1544 0 : *pRepeatColRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
1545 0 : bRecalcPages = true;
1546 0 : nRepeatStartX = nSCol; // for UpdatePageBreaks
1547 0 : nRepeatEndX = nECol;
1548 : }
1549 : }
1550 :
1551 212 : if ( pRepeatRowRange )
1552 : {
1553 0 : nSCol = pRepeatRowRange->aStart.Col();
1554 0 : nSRow = pRepeatRowRange->aStart.Row();
1555 0 : nECol = pRepeatRowRange->aEnd.Col();
1556 0 : nERow = pRepeatRowRange->aEnd.Row();
1557 :
1558 : // do not try to modify sheet index of repeat range
1559 0 : if ( ScRefUpdate::Update( pDocument, eUpdateRefMode,
1560 : nCol1,nRow1,nTab, nCol2,nRow2,nTab,
1561 : nDx,nDy,0,
1562 0 : nSCol,nSRow,nSTab, nECol,nERow,nETab ) )
1563 : {
1564 0 : *pRepeatRowRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 );
1565 0 : bRecalcPages = true;
1566 0 : nRepeatStartY = nSRow; // for UpdatePageBreaks
1567 0 : nRepeatEndY = nERow;
1568 : }
1569 : }
1570 :
1571 : // updating print ranges is not necessary with multiple print ranges
1572 212 : if ( bRecalcPages && GetPrintRangeCount() <= 1 )
1573 : {
1574 0 : UpdatePageBreaks(NULL);
1575 :
1576 0 : pDocument->RepaintRange( ScRange(0,0,nTab,MAXCOL,MAXROW,nTab) );
1577 : }
1578 : }
1579 :
1580 258 : if (bUpdated && IsStreamValid())
1581 2 : SetStreamValid(false);
1582 :
1583 258 : if(mpCondFormatList)
1584 258 : mpCondFormatList->UpdateReference(rCxt);
1585 :
1586 258 : if (pTabProtection)
1587 0 : pTabProtection->updateReference( eUpdateRefMode, pDocument, rCxt.maRange, nDx, nDy, nDz);
1588 258 : }
1589 :
1590 0 : void ScTable::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
1591 : ScDocument* pUndoDoc )
1592 : {
1593 0 : for ( SCCOL i=0; i<=MAXCOL; i++ )
1594 0 : aCol[i].UpdateTranspose( rSource, rDest, pUndoDoc );
1595 0 : }
1596 :
1597 0 : void ScTable::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
1598 : {
1599 0 : for ( SCCOL i=0; i<=MAXCOL; i++ )
1600 0 : aCol[i].UpdateGrow( rArea, nGrowX, nGrowY );
1601 0 : }
1602 :
1603 148 : void ScTable::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
1604 : {
1605 : // Store the old tab number in sc::UpdatedRangeNames for
1606 : // ScTokenArray::AdjustReferenceOnInsertedTab() to check with
1607 : // isNameModified()
1608 148 : if (mpRangeName)
1609 96 : mpRangeName->UpdateInsertTab(rCxt, nTab);
1610 :
1611 148 : if (nTab >= rCxt.mnInsertPos)
1612 : {
1613 132 : nTab += rCxt.mnSheets;
1614 132 : if (pDBDataNoName)
1615 0 : pDBDataNoName->UpdateMoveTab(nTab - 1 ,nTab);
1616 : }
1617 :
1618 148 : if (mpCondFormatList)
1619 148 : mpCondFormatList->UpdateInsertTab(rCxt);
1620 :
1621 148 : if (pTabProtection)
1622 : pTabProtection->updateReference( URM_INSDEL, pDocument,
1623 : ScRange( 0, 0, rCxt.mnInsertPos, MAXCOL, MAXROW, MAXTAB),
1624 0 : 0, 0, rCxt.mnSheets);
1625 :
1626 151700 : for (SCCOL i=0; i <= MAXCOL; i++)
1627 151552 : aCol[i].UpdateInsertTab(rCxt);
1628 :
1629 148 : if (IsStreamValid())
1630 2 : SetStreamValid(false);
1631 148 : }
1632 :
1633 249 : void ScTable::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
1634 : {
1635 : // Store the old tab number in sc::UpdatedRangeNames for
1636 : // ScTokenArray::AdjustReferenceOnDeletedTab() to check with
1637 : // isNameModified()
1638 249 : if (mpRangeName)
1639 33 : mpRangeName->UpdateDeleteTab(rCxt, nTab);
1640 :
1641 249 : if (nTab > rCxt.mnDeletePos)
1642 : {
1643 54 : nTab -= rCxt.mnSheets;
1644 54 : if (pDBDataNoName)
1645 0 : pDBDataNoName->UpdateMoveTab(nTab + 1,nTab);
1646 : }
1647 :
1648 249 : if (mpCondFormatList)
1649 249 : mpCondFormatList->UpdateDeleteTab(rCxt);
1650 :
1651 249 : if (pTabProtection)
1652 : pTabProtection->updateReference( URM_INSDEL, pDocument,
1653 : ScRange( 0, 0, rCxt.mnDeletePos, MAXCOL, MAXROW, MAXTAB),
1654 0 : 0, 0, -rCxt.mnSheets);
1655 :
1656 255225 : for (SCCOL i = 0; i <= MAXCOL; ++i)
1657 254976 : aCol[i].UpdateDeleteTab(rCxt);
1658 :
1659 249 : if (IsStreamValid())
1660 6 : SetStreamValid(false);
1661 249 : }
1662 :
1663 39 : void ScTable::UpdateMoveTab(
1664 : sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo, ScProgress* pProgress )
1665 : {
1666 39 : nTab = nTabNo;
1667 39 : if (mpRangeName)
1668 0 : mpRangeName->UpdateMoveTab(rCxt, nTab);
1669 :
1670 39 : if (pDBDataNoName)
1671 0 : pDBDataNoName->UpdateMoveTab(rCxt.mnOldPos, rCxt.mnNewPos);
1672 :
1673 39 : if(mpCondFormatList)
1674 39 : mpCondFormatList->UpdateMoveTab(rCxt);
1675 :
1676 39 : if (pTabProtection)
1677 : pTabProtection->updateReference( URM_REORDER, pDocument,
1678 : ScRange( 0, 0, rCxt.mnOldPos, MAXCOL, MAXROW, MAXTAB),
1679 0 : 0, 0, rCxt.mnNewPos - rCxt.mnOldPos);
1680 :
1681 39975 : for ( SCCOL i=0; i <= MAXCOL; i++ )
1682 : {
1683 39936 : aCol[i].UpdateMoveTab(rCxt, nTabNo);
1684 39936 : if (pProgress)
1685 0 : pProgress->SetState(pProgress->GetState() + aCol[i].GetCodeCount());
1686 : }
1687 :
1688 39 : if (IsStreamValid())
1689 0 : SetStreamValid(false);
1690 39 : }
1691 :
1692 403 : void ScTable::UpdateCompile( bool bForceIfNameInUse )
1693 : {
1694 413075 : for (SCCOL i=0; i <= MAXCOL; i++)
1695 : {
1696 412672 : aCol[i].UpdateCompile( bForceIfNameInUse );
1697 : }
1698 403 : }
1699 :
1700 13 : void ScTable::SetTabNo(SCTAB nNewTab)
1701 : {
1702 13 : nTab = nNewTab;
1703 13 : for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].SetTabNo(nNewTab);
1704 13 : }
1705 :
1706 6 : void ScTable::FindRangeNamesInUse(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
1707 : std::set<sal_uInt16>& rIndexes) const
1708 : {
1709 17 : for (SCCOL i = nCol1; i <= nCol2 && ValidCol(i); i++)
1710 11 : aCol[i].FindRangeNamesInUse(nRow1, nRow2, rIndexes);
1711 6 : }
1712 :
1713 30 : void ScTable::ExtendPrintArea( OutputDevice* pDev,
1714 : SCCOL /* nStartCol */, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow )
1715 : {
1716 30 : if ( !pColFlags || !pRowFlags )
1717 : {
1718 : OSL_FAIL("ExtendPrintArea: No ColInfo or RowInfo");
1719 0 : return;
1720 : }
1721 :
1722 30 : Point aPix1000 = pDev->LogicToPixel( Point(1000,1000), MAP_TWIP );
1723 30 : double nPPTX = aPix1000.X() / 1000.0;
1724 30 : double nPPTY = aPix1000.Y() / 1000.0;
1725 :
1726 : // First, mark those columns that we need to skip i.e. hidden and empty columns.
1727 :
1728 30 : ScFlatBoolColSegments aSkipCols;
1729 30 : aSkipCols.setFalse(0, MAXCOL);
1730 60 : for (SCCOL i = 0; i <= MAXCOL; ++i)
1731 : {
1732 30 : SCCOL nLastCol = i;
1733 30 : if (ColHidden(i, NULL, &nLastCol))
1734 : {
1735 : // Columns are hidden in this range.
1736 0 : aSkipCols.setTrue(i, nLastCol);
1737 : }
1738 : else
1739 : {
1740 : // These columns are visible. Check for empty columns.
1741 30750 : for (SCCOL j = i; j <= nLastCol; ++j)
1742 : {
1743 30720 : if (aCol[j].GetCellCount() == 0)
1744 : // empty
1745 30682 : aSkipCols.setTrue(j,j);
1746 : }
1747 : }
1748 30 : i = nLastCol;
1749 : }
1750 :
1751 : ScFlatBoolColSegments::RangeData aColData;
1752 68 : for (SCCOL nCol = rEndCol; nCol >= 0; --nCol)
1753 : {
1754 38 : if (!aSkipCols.getRangeData(nCol, aColData))
1755 : // Failed to get the data. This should never happen!
1756 0 : return;
1757 :
1758 38 : if (aColData.mbValue)
1759 : {
1760 : // Skip these columns.
1761 4 : nCol = aColData.mnCol1; // move toward 0.
1762 4 : continue;
1763 : }
1764 :
1765 : // These are visible and non-empty columns.
1766 72 : for (SCCOL nDataCol = nCol; 0 <= nDataCol && nDataCol >= aColData.mnCol1; --nDataCol)
1767 : {
1768 38 : SCCOL nPrintCol = nDataCol;
1769 38 : VisibleDataCellIterator aIter(*mpHiddenRows, aCol[nDataCol]);
1770 76 : ScRefCellValue aCell = aIter.reset(nStartRow);
1771 38 : if (aCell.isEmpty())
1772 : // No visible cells found in this column. Skip it.
1773 0 : continue;
1774 :
1775 124 : while (!aCell.isEmpty())
1776 : {
1777 48 : SCCOL nNewCol = nDataCol;
1778 48 : SCROW nRow = aIter.getRow();
1779 48 : if (nRow > nEndRow)
1780 : // Went past the last row position. Bail out.
1781 0 : break;
1782 :
1783 48 : MaybeAddExtraColumn(nNewCol, nRow, pDev, nPPTX, nPPTY);
1784 48 : if (nNewCol > nPrintCol)
1785 16 : nPrintCol = nNewCol;
1786 48 : aCell = aIter.next();
1787 : }
1788 :
1789 38 : if (nPrintCol > rEndCol)
1790 : // Make sure we don't shrink the print area.
1791 10 : rEndCol = nPrintCol;
1792 38 : }
1793 34 : nCol = aColData.mnCol1; // move toward 0.
1794 30 : }
1795 : }
1796 :
1797 48 : void ScTable::MaybeAddExtraColumn(SCCOL& rCol, SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY)
1798 : {
1799 48 : ScRefCellValue aCell = aCol[rCol].GetCellValue(nRow);
1800 48 : if (!aCell.hasString())
1801 48 : return;
1802 :
1803 48 : bool bFormula = false; //TODO: pass as parameter
1804 48 : long nPixel = aCol[rCol].GetTextWidth(nRow);
1805 :
1806 : // Width already calculated in Idle-Handler ?
1807 48 : if ( TEXTWIDTH_DIRTY == nPixel )
1808 : {
1809 30 : ScNeededSizeOptions aOptions;
1810 30 : aOptions.bTotalSize = true;
1811 30 : aOptions.bFormula = bFormula;
1812 30 : aOptions.bSkipMerged = false;
1813 :
1814 30 : Fraction aZoom(1,1);
1815 30 : nPixel = aCol[rCol].GetNeededSize(
1816 30 : nRow, pDev, nPPTX, nPPTY, aZoom, aZoom, true, aOptions, NULL );
1817 :
1818 30 : aCol[rCol].SetTextWidth(nRow, static_cast<sal_uInt16>(nPixel));
1819 : }
1820 :
1821 48 : long nTwips = (long) (nPixel / nPPTX);
1822 48 : long nDocW = GetColWidth( rCol );
1823 :
1824 48 : long nMissing = nTwips - nDocW;
1825 48 : if ( nMissing > 0 )
1826 : {
1827 : // look at alignment
1828 :
1829 18 : const ScPatternAttr* pPattern = GetPattern( rCol, nRow );
1830 18 : const SfxItemSet* pCondSet = pDocument->GetCondResult( rCol, nRow, nTab );
1831 :
1832 : SvxCellHorJustify eHorJust = (SvxCellHorJustify)static_cast<const SvxHorJustifyItem&>(
1833 18 : pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet )).GetValue();
1834 18 : if ( eHorJust == SVX_HOR_JUSTIFY_CENTER )
1835 0 : nMissing /= 2; // distributed into both directions
1836 : else
1837 : {
1838 : // STANDARD is LEFT (only text is handled here)
1839 18 : bool bRight = ( eHorJust == SVX_HOR_JUSTIFY_RIGHT );
1840 18 : if ( IsLayoutRTL() )
1841 0 : bRight = !bRight;
1842 18 : if ( bRight )
1843 0 : nMissing = 0; // extended only to the left (logical)
1844 : }
1845 : }
1846 :
1847 48 : SCCOL nNewCol = rCol;
1848 114 : while (nMissing > 0 && nNewCol < MAXCOL)
1849 : {
1850 18 : ScRefCellValue aNextCell = aCol[nNewCol+1].GetCellValue(nRow);
1851 18 : if (!aNextCell.isEmpty())
1852 : // Cell content in a next column ends display of this string.
1853 0 : nMissing = 0;
1854 : else
1855 18 : nMissing -= GetColWidth(++nNewCol);
1856 18 : }
1857 48 : rCol = nNewCol;
1858 : }
1859 :
1860 : namespace {
1861 :
1862 : class SetTableIndex : public ::std::unary_function<ScRange, void>
1863 : {
1864 : SCTAB mnTab;
1865 : public:
1866 7 : SetTableIndex(SCTAB nTab) : mnTab(nTab) {}
1867 :
1868 0 : void operator() (ScRange& rRange) const
1869 : {
1870 0 : rRange.aStart.SetTab(mnTab);
1871 0 : rRange.aEnd.SetTab(mnTab);
1872 0 : }
1873 : };
1874 :
1875 14 : void setPrintRange(ScRange*& pRange1, const ScRange* pRange2)
1876 : {
1877 14 : if (pRange2)
1878 : {
1879 6 : if (pRange1)
1880 2 : *pRange1 = *pRange2;
1881 : else
1882 4 : pRange1 = new ScRange(*pRange2);
1883 : }
1884 : else
1885 8 : DELETEZ(pRange1);
1886 14 : }
1887 :
1888 : }
1889 :
1890 7 : void ScTable::CopyPrintRange(const ScTable& rTable)
1891 : {
1892 : // The table index shouldn't be used when the print range is used, but
1893 : // just in case set the correct table index.
1894 :
1895 7 : aPrintRanges = rTable.aPrintRanges;
1896 7 : ::std::for_each(aPrintRanges.begin(), aPrintRanges.end(), SetTableIndex(nTab));
1897 :
1898 7 : bPrintEntireSheet = rTable.bPrintEntireSheet;
1899 :
1900 7 : delete pRepeatColRange;
1901 7 : pRepeatColRange = NULL;
1902 7 : if (rTable.pRepeatColRange)
1903 : {
1904 0 : pRepeatColRange = new ScRange(*rTable.pRepeatColRange);
1905 0 : pRepeatColRange->aStart.SetTab(nTab);
1906 0 : pRepeatColRange->aEnd.SetTab(nTab);
1907 : }
1908 :
1909 7 : delete pRepeatRowRange;
1910 7 : pRepeatRowRange = NULL;
1911 7 : if (rTable.pRepeatRowRange)
1912 : {
1913 0 : pRepeatRowRange = new ScRange(*rTable.pRepeatRowRange);
1914 0 : pRepeatRowRange->aStart.SetTab(nTab);
1915 0 : pRepeatRowRange->aEnd.SetTab(nTab);
1916 : }
1917 7 : }
1918 :
1919 5 : void ScTable::SetRepeatColRange( const ScRange* pNew )
1920 : {
1921 5 : setPrintRange( pRepeatColRange, pNew );
1922 :
1923 5 : if (IsStreamValid())
1924 1 : SetStreamValid(false);
1925 :
1926 5 : InvalidatePageBreaks();
1927 5 : }
1928 :
1929 9 : void ScTable::SetRepeatRowRange( const ScRange* pNew )
1930 : {
1931 9 : setPrintRange( pRepeatRowRange, pNew );
1932 :
1933 9 : if (IsStreamValid())
1934 1 : SetStreamValid(false);
1935 :
1936 9 : InvalidatePageBreaks();
1937 9 : }
1938 :
1939 60 : void ScTable::ClearPrintRanges()
1940 : {
1941 60 : aPrintRanges.clear();
1942 60 : bPrintEntireSheet = false;
1943 :
1944 60 : if (IsStreamValid())
1945 0 : SetStreamValid(false);
1946 :
1947 60 : InvalidatePageBreaks(); // #i117952# forget page breaks for an old print range
1948 60 : }
1949 :
1950 8 : void ScTable::AddPrintRange( const ScRange& rNew )
1951 : {
1952 8 : bPrintEntireSheet = false;
1953 8 : if( aPrintRanges.size() < 0xFFFF )
1954 8 : aPrintRanges.push_back( rNew );
1955 :
1956 8 : if (IsStreamValid())
1957 0 : SetStreamValid(false);
1958 :
1959 8 : InvalidatePageBreaks();
1960 8 : }
1961 :
1962 11 : void ScTable::SetPrintEntireSheet()
1963 : {
1964 11 : if( !IsPrintEntireSheet() )
1965 : {
1966 0 : ClearPrintRanges();
1967 0 : bPrintEntireSheet = true;
1968 : }
1969 11 : }
1970 :
1971 411 : const ScRange* ScTable::GetPrintRange(sal_uInt16 nPos) const
1972 : {
1973 411 : return (nPos < GetPrintRangeCount()) ? &aPrintRanges[ nPos ] : NULL;
1974 : }
1975 :
1976 206 : void ScTable::FillPrintSaver( ScPrintSaverTab& rSaveTab ) const
1977 : {
1978 206 : rSaveTab.SetAreas( aPrintRanges, bPrintEntireSheet );
1979 206 : rSaveTab.SetRepeat( pRepeatColRange, pRepeatRowRange );
1980 206 : }
1981 :
1982 4 : void ScTable::RestorePrintRanges( const ScPrintSaverTab& rSaveTab )
1983 : {
1984 4 : aPrintRanges = rSaveTab.GetPrintRanges();
1985 4 : bPrintEntireSheet = rSaveTab.IsEntireSheet();
1986 4 : SetRepeatColRange( rSaveTab.GetRepeatCol() );
1987 4 : SetRepeatRowRange( rSaveTab.GetRepeatRow() );
1988 :
1989 4 : InvalidatePageBreaks(); // #i117952# forget page breaks for an old print range
1990 4 : UpdatePageBreaks(NULL);
1991 4 : }
1992 :
1993 : SCROW ScTable::VisibleDataCellIterator::ROW_NOT_FOUND = -1;
1994 :
1995 38 : ScTable::VisibleDataCellIterator::VisibleDataCellIterator(ScFlatBoolRowSegments& rRowSegs, ScColumn& rColumn) :
1996 : mrRowSegs(rRowSegs),
1997 : mrColumn(rColumn),
1998 : mnCurRow(ROW_NOT_FOUND),
1999 38 : mnUBound(ROW_NOT_FOUND)
2000 : {
2001 38 : }
2002 :
2003 38 : ScTable::VisibleDataCellIterator::~VisibleDataCellIterator()
2004 : {
2005 38 : }
2006 :
2007 38 : ScRefCellValue ScTable::VisibleDataCellIterator::reset(SCROW nRow)
2008 : {
2009 38 : if (nRow > MAXROW)
2010 : {
2011 0 : mnCurRow = ROW_NOT_FOUND;
2012 0 : return ScRefCellValue();
2013 : }
2014 :
2015 : ScFlatBoolRowSegments::RangeData aData;
2016 38 : if (!mrRowSegs.getRangeData(nRow, aData))
2017 : {
2018 0 : mnCurRow = ROW_NOT_FOUND;
2019 0 : return ScRefCellValue();
2020 : }
2021 :
2022 38 : if (!aData.mbValue)
2023 : {
2024 : // specified row is visible. Take it.
2025 38 : mnCurRow = nRow;
2026 38 : mnUBound = aData.mnRow2;
2027 : }
2028 : else
2029 : {
2030 : // specified row is not-visible. The first visible row is the start of
2031 : // the next segment.
2032 0 : mnCurRow = aData.mnRow2 + 1;
2033 0 : mnUBound = mnCurRow; // get range data on the next iteration.
2034 0 : if (mnCurRow > MAXROW)
2035 : {
2036 : // Make sure the row doesn't exceed our current limit.
2037 0 : mnCurRow = ROW_NOT_FOUND;
2038 0 : return ScRefCellValue();
2039 : }
2040 : }
2041 :
2042 38 : maCell = mrColumn.GetCellValue(mnCurRow);
2043 38 : if (!maCell.isEmpty())
2044 : // First visible cell found.
2045 30 : return maCell;
2046 :
2047 : // Find a first visible cell below this row (if any).
2048 8 : return next();
2049 : }
2050 :
2051 56 : ScRefCellValue ScTable::VisibleDataCellIterator::next()
2052 : {
2053 56 : if (mnCurRow == ROW_NOT_FOUND)
2054 0 : return ScRefCellValue();
2055 :
2056 112 : while (mrColumn.GetNextDataPos(mnCurRow))
2057 : {
2058 18 : if (mnCurRow > mnUBound)
2059 : {
2060 : // We don't know the visibility of this row range. Query it.
2061 : ScFlatBoolRowSegments::RangeData aData;
2062 0 : if (!mrRowSegs.getRangeData(mnCurRow, aData))
2063 : {
2064 0 : mnCurRow = ROW_NOT_FOUND;
2065 0 : return ScRefCellValue();
2066 : }
2067 :
2068 0 : if (aData.mbValue)
2069 : {
2070 : // This row is invisible. Skip to the last invisible row and
2071 : // try again.
2072 0 : mnCurRow = mnUBound = aData.mnRow2;
2073 0 : continue;
2074 : }
2075 :
2076 : // This row is visible.
2077 0 : mnUBound = aData.mnRow2;
2078 : }
2079 :
2080 18 : maCell = mrColumn.GetCellValue(mnCurRow);
2081 18 : if (!maCell.isEmpty())
2082 18 : return maCell;
2083 : }
2084 :
2085 38 : mnCurRow = ROW_NOT_FOUND;
2086 38 : return ScRefCellValue();
2087 : }
2088 :
2089 321 : void ScTable::SetAnonymousDBData(ScDBData* pDBData)
2090 : {
2091 321 : delete pDBDataNoName;
2092 321 : pDBDataNoName = pDBData;
2093 321 : }
2094 :
2095 171 : sal_uLong ScTable::AddCondFormat( ScConditionalFormat* pNew )
2096 : {
2097 171 : if(!mpCondFormatList)
2098 0 : mpCondFormatList.reset(new ScConditionalFormatList());
2099 :
2100 171 : sal_uLong nMax = 0;
2101 1497 : for(ScConditionalFormatList::const_iterator itr = mpCondFormatList->begin();
2102 998 : itr != mpCondFormatList->end(); ++itr)
2103 : {
2104 328 : sal_uLong nKey = itr->GetKey();
2105 328 : if(nKey > nMax)
2106 328 : nMax = nKey;
2107 : }
2108 :
2109 171 : pNew->SetKey(nMax+1);
2110 171 : mpCondFormatList->InsertNew(pNew);
2111 :
2112 171 : return nMax + 1;
2113 : }
2114 :
2115 31225 : SvtScriptType ScTable::GetScriptType( SCCOL nCol, SCROW nRow ) const
2116 : {
2117 31225 : if (!ValidCol(nCol))
2118 0 : return SvtScriptType::NONE;
2119 :
2120 31225 : return aCol[nCol].GetScriptType(nRow);
2121 : }
2122 :
2123 3499 : void ScTable::SetScriptType( SCCOL nCol, SCROW nRow, SvtScriptType nType )
2124 : {
2125 3499 : if (!ValidCol(nCol))
2126 3499 : return;
2127 :
2128 3499 : aCol[nCol].SetScriptType(nRow, nType);
2129 : }
2130 :
2131 100 : SvtScriptType ScTable::GetRangeScriptType(
2132 : sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2133 : {
2134 100 : if (!ValidCol(nCol))
2135 0 : return SvtScriptType::NONE;
2136 :
2137 100 : sc::CellStoreType::iterator itr = aCol[nCol].maCells.begin();
2138 100 : return aCol[nCol].GetRangeScriptType(rBlockPos.miCellTextAttrPos, nRow1, nRow2, itr);
2139 : }
2140 :
2141 38 : size_t ScTable::GetFormulaHash( SCCOL nCol, SCROW nRow ) const
2142 : {
2143 38 : if (!ValidCol(nCol))
2144 0 : return 0;
2145 :
2146 38 : return aCol[nCol].GetFormulaHash(nRow);
2147 : }
2148 :
2149 6 : ScFormulaVectorState ScTable::GetFormulaVectorState( SCCOL nCol, SCROW nRow ) const
2150 : {
2151 6 : if (!ValidCol(nCol))
2152 0 : return FormulaVectorUnknown;
2153 :
2154 6 : return aCol[nCol].GetFormulaVectorState(nRow);
2155 : }
2156 :
2157 0 : formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol, SCROW nRow )
2158 : {
2159 0 : if (!ValidCol(nCol) || !ValidRow(nRow))
2160 0 : return formula::FormulaTokenRef();
2161 :
2162 0 : return aCol[nCol].ResolveStaticReference(nRow);
2163 : }
2164 :
2165 0 : formula::FormulaTokenRef ScTable::ResolveStaticReference( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2166 : {
2167 0 : if (nCol2 < nCol1 || nRow2 < nRow1)
2168 0 : return formula::FormulaTokenRef();
2169 :
2170 0 : if (!ValidCol(nCol1) || !ValidCol(nCol2) || !ValidRow(nRow1) || !ValidRow(nRow2))
2171 0 : return formula::FormulaTokenRef();
2172 :
2173 0 : ScMatrixRef pMat(new ScMatrix(nCol2-nCol1+1, nRow2-nRow1+1, 0.0));
2174 0 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2175 : {
2176 0 : if (!aCol[nCol].ResolveStaticReference(*pMat, nCol2-nCol1, nRow1, nRow2))
2177 : // Column contains non-static cell. Failed.
2178 0 : return formula::FormulaTokenRef();
2179 : }
2180 :
2181 0 : return formula::FormulaTokenRef(new ScMatrixToken(pMat));
2182 : }
2183 :
2184 22 : formula::VectorRefArray ScTable::FetchVectorRefArray( SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2185 : {
2186 22 : if (nRow2 < nRow1)
2187 0 : return formula::VectorRefArray();
2188 :
2189 22 : if (!ValidCol(nCol) || !ValidRow(nRow1) || !ValidRow(nRow2))
2190 0 : return formula::VectorRefArray();
2191 :
2192 22 : return aCol[nCol].FetchVectorRefArray(nRow1, nRow2);
2193 : }
2194 :
2195 317656 : ScRefCellValue ScTable::GetRefCellValue( SCCOL nCol, SCROW nRow )
2196 : {
2197 317656 : if (!ValidColRow(nCol, nRow))
2198 0 : return ScRefCellValue();
2199 :
2200 317656 : return aCol[nCol].GetCellValue(nRow);
2201 : }
2202 :
2203 73118 : SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow )
2204 : {
2205 73118 : if (!ValidColRow(nCol, nRow))
2206 15729 : return NULL;
2207 :
2208 57389 : return aCol[nCol].GetBroadcaster(nRow);
2209 : }
2210 :
2211 125 : void ScTable::DeleteBroadcasters(
2212 : sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
2213 : {
2214 125 : if (!ValidCol(nCol))
2215 125 : return;
2216 :
2217 125 : aCol[nCol].DeleteBroadcasters(rBlockPos, nRow1, nRow2);
2218 : }
2219 :
2220 0 : bool ScTable::HasBroadcaster( SCCOL nCol ) const
2221 : {
2222 0 : if (!ValidCol(nCol))
2223 0 : return false;
2224 :
2225 0 : return aCol[nCol].HasBroadcaster();
2226 : }
2227 :
2228 224 : void ScTable::FillMatrix( ScMatrix& rMat, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
2229 : {
2230 224 : size_t nMatCol = 0;
2231 471 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol, ++nMatCol)
2232 247 : aCol[nCol].FillMatrix(rMat, nMatCol, nRow1, nRow2);
2233 224 : }
2234 :
2235 2945 : void ScTable::InterpretDirtyCells( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2236 : {
2237 25223 : for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2238 22278 : aCol[nCol].InterpretDirtyCells(nRow1, nRow2);
2239 2945 : }
2240 :
2241 0 : void ScTable::SetFormulaResults( SCCOL nCol, SCROW nRow, const double* pResults, size_t nLen )
2242 : {
2243 0 : if (!ValidCol(nCol))
2244 0 : return;
2245 :
2246 0 : aCol[nCol].SetFormulaResults(nRow, pResults, nLen);
2247 : }
2248 :
2249 0 : void ScTable::SetFormulaResults(
2250 : SCCOL nCol, SCROW nRow, const formula::FormulaTokenRef* pResults, size_t nLen )
2251 : {
2252 0 : if (!ValidCol(nCol))
2253 0 : return;
2254 :
2255 0 : aCol[nCol].SetFormulaResults(nRow, pResults, nLen);
2256 : }
2257 :
2258 : #if DEBUG_COLUMN_STORAGE
2259 : void ScTable::DumpFormulaGroups( SCCOL nCol ) const
2260 : {
2261 : if (!ValidCol(nCol))
2262 : return;
2263 :
2264 : aCol[nCol].DumpFormulaGroups();
2265 : }
2266 : #endif
2267 :
2268 14 : const SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow ) const
2269 : {
2270 14 : if (!ValidColRow(nCol, nRow))
2271 0 : return NULL;
2272 :
2273 14 : return aCol[nCol].GetBroadcaster(nRow);
2274 : }
2275 :
2276 0 : void ScTable::DeleteConditionalFormat( sal_uLong nIndex )
2277 : {
2278 0 : mpCondFormatList->erase(nIndex);
2279 0 : }
2280 :
2281 24 : void ScTable::SetCondFormList( ScConditionalFormatList* pNew )
2282 : {
2283 24 : mpCondFormatList.reset( pNew );
2284 24 : }
2285 :
2286 173192 : ScConditionalFormatList* ScTable::GetCondFormList()
2287 : {
2288 173192 : if(!mpCondFormatList)
2289 0 : mpCondFormatList.reset( new ScConditionalFormatList() );
2290 :
2291 173192 : return mpCondFormatList.get();
2292 : }
2293 :
2294 0 : const ScConditionalFormatList* ScTable::GetCondFormList() const
2295 : {
2296 0 : return mpCondFormatList.get();
2297 156 : }
2298 :
2299 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|