xref: /trunk/main/svx/source/table/cellcursor.cxx (revision f6e50924)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 
27 #include "svx/svdotable.hxx"
28 #include "cellcursor.hxx"
29 #include "tablelayouter.hxx"
30 #include "cell.hxx"
31 #include "svx/svdmodel.hxx"
32 #include "svx/svdstr.hrc"
33 #include "svx/svdglob.hxx"
34 
35 // -----------------------------------------------------------------------------
36 
37 using ::rtl::OUString;
38 using namespace ::com::sun::star::uno;
39 using namespace ::com::sun::star::lang;
40 using namespace ::com::sun::star::container;
41 using namespace ::com::sun::star::beans;
42 using namespace ::com::sun::star::table;
43 
44 // -----------------------------------------------------------------------------
45 
46 namespace sdr { namespace table {
47 
48 // -----------------------------------------------------------------------------
49 // CellCursor
50 // -----------------------------------------------------------------------------
51 
CellCursor(const TableModelRef & xTable,sal_Int32 nLeft,sal_Int32 nTop,sal_Int32 nRight,sal_Int32 nBottom)52 CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
53 : CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
54 {
55 }
56 
57 // -----------------------------------------------------------------------------
58 
~CellCursor()59 CellCursor::~CellCursor()
60 {
61 }
62 
63 // -----------------------------------------------------------------------------
64 // XCellCursor
65 // -----------------------------------------------------------------------------
66 
getCellByPosition(sal_Int32 nColumn,sal_Int32 nRow)67 Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) throw (IndexOutOfBoundsException, RuntimeException)
68 {
69 	return CellRange::getCellByPosition( nColumn, nRow );
70 }
71 
72 // -----------------------------------------------------------------------------
73 
getCellRangeByPosition(sal_Int32 nLeft,sal_Int32 nTop,sal_Int32 nRight,sal_Int32 nBottom)74 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) throw (IndexOutOfBoundsException, RuntimeException)
75 {
76 	return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
77 }
78 
79 // -----------------------------------------------------------------------------
80 
getCellRangeByName(const OUString & aRange)81 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange ) throw (RuntimeException)
82 {
83 	return CellRange::getCellRangeByName( aRange );
84 }
85 
86 // -----------------------------------------------------------------------------
87 // XCellCursor
88 // -----------------------------------------------------------------------------
89 
gotoStart()90 void SAL_CALL CellCursor::gotoStart(  ) throw (RuntimeException)
91 {
92 	mnRight = mnLeft;
93 	mnBottom = mnTop;
94 }
95 
96 // -----------------------------------------------------------------------------
97 
gotoEnd()98 void SAL_CALL CellCursor::gotoEnd(  ) throw (RuntimeException)
99 {
100 	mnLeft = mnRight;
101 	mnTop = mnBottom;
102 }
103 
104 // -----------------------------------------------------------------------------
105 
gotoNext()106 void SAL_CALL CellCursor::gotoNext(  ) throw (RuntimeException)
107 {
108 	if( mxTable.is() )
109 	{
110 		mnRight++;
111 		if( mnRight >= mxTable->getColumnCount() )
112 		{
113 			// if we past the last column, try skip to the row line
114 			mnTop++;
115 			if( mnTop >= mxTable->getRowCount() )
116 			{
117 				// if we past the last row, do not move cursor at all
118 				mnTop--;
119 				mnRight--;
120 			}
121 			else
122 			{
123 				// restart at the first column on the next row
124 				mnRight = 0;
125 			}
126 		}
127 	}
128 
129 	mnLeft = mnRight;
130 	mnTop = mnBottom;
131 }
132 
133 // -----------------------------------------------------------------------------
134 
gotoPrevious()135 void SAL_CALL CellCursor::gotoPrevious(  ) throw (RuntimeException)
136 {
137 	if( mxTable.is() )
138 	{
139 		if( mnLeft > 0 )
140 		{
141 			--mnLeft;
142 		}
143 		else if( mnTop > 0 )
144 		{
145 			--mnTop;
146 			mnLeft = mxTable->getColumnCount() - 1;
147 		}
148 	}
149 
150 	mnRight = mnLeft;
151 	mnBottom = mnTop;
152 }
153 
154 // -----------------------------------------------------------------------------
155 
gotoOffset(::sal_Int32 nColumnOffset,::sal_Int32 nRowOffset)156 void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) throw (RuntimeException)
157 {
158 	if( mxTable.is() )
159 	{
160 		const sal_Int32 nLeft = mnLeft + nColumnOffset;
161 		if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
162 			mnRight = mnLeft = nLeft;
163 
164 		const sal_Int32 nTop = mnTop + nRowOffset;
165 		if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
166 			mnTop = mnBottom = nTop;
167 	}
168 }
169 
170 // -----------------------------------------------------------------------------
171 // XMergeableCellCursor
172 // -----------------------------------------------------------------------------
173 
174 /** returns true and the merged cell positions if a merge is valid or false if a merge is
175 	not valid for that range */
GetMergedSelection(CellPos & rStart,CellPos & rEnd)176 bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
177 {
178 	rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
179 	rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
180 
181 	// single cell merge is never valid
182 	if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
183 	{
184 		CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
185 
186 		// check if first cell is merged
187 		if( xCell.is() && xCell->isMerged() )
188 			findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
189 
190 		// check if last cell is merged
191 		xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
192 		if( xCell.is() )
193 		{
194 			if( xCell->isMerged() )
195 			{
196 				findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
197                 // merge not possible if selection is only one cell and all its merges
198                 if( rEnd == rStart )
199                     return false;
200 				xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
201 			}
202 		}
203 		if( xCell.is() )
204 		{
205 			rEnd.mnCol += xCell->getColumnSpan()-1;
206 			rEnd.mnRow += xCell->getRowSpan()-1;
207 		}
208 
209 		// now check if everything is inside the given bounds
210 		sal_Int32 nRow, nCol;
211 		for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
212 		{
213 			for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
214 			{
215 				xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
216 				if( !xCell.is() )
217 					continue;
218 
219 				if( xCell->isMerged() )
220 				{
221 					sal_Int32 nOriginCol, nOriginRow;
222 					if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
223 					{
224 						if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
225 							return false;
226 
227 						xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
228 						if( xCell.is() )
229 						{
230 							nOriginCol += xCell->getColumnSpan()-1;
231 							nOriginRow += xCell->getRowSpan()-1;
232 
233 							if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
234 								return false;
235 						}
236 					}
237 				}
238 				else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
239 				{
240 					return false;
241 				}
242 			}
243 		}
244 		return true;
245 	}
246 	catch( Exception& )
247 	{
248 		DBG_ERROR("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
249 	}
250 	return false;
251 }
252 
253 // -----------------------------------------------------------------------------
254 
merge()255 void SAL_CALL CellCursor::merge(  ) throw (NoSupportException, RuntimeException)
256 {
257 	CellPos aStart, aEnd;
258 	if( !GetMergedSelection( aStart, aEnd ) )
259 		throw NoSupportException();
260 
261 	if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
262 		throw DisposedException();
263 
264 	SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
265     const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
266 
267 	if( bUndo )
268 		pModel->BegUndo( ImpGetResStr(STR_TABLE_MERGE) );
269 
270 	try
271 	{
272 		mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
273 		mxTable->optimize();
274 		mxTable->setModified(sal_True);
275 	}
276 	catch( Exception& )
277 	{
278 		DBG_ERROR("sdr::table::CellCursor::merge(), exception caught!");
279 	}
280 
281 	if( bUndo )
282 		pModel->EndUndo();
283 
284 	if( pModel )
285 		pModel->SetChanged();
286 }
287 
288 // -----------------------------------------------------------------------------
289 
split_column(sal_Int32 nCol,sal_Int32 nColumns,std::vector<sal_Int32> & rLeftOvers)290 void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
291 {
292 	const sal_Int32 nRowCount = mxTable->getRowCount();
293 
294 	sal_Int32 nNewCols = 0, nRow;
295 
296 	// first check how many columns we need to add
297 	for( nRow = mnTop; nRow <= mnBottom; ++nRow )
298 	{
299 		CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
300 		if( xCell.is() && !xCell->isMerged() )
301 			nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
302 	}
303 
304 	if( nNewCols > 0 )
305 	{
306 		const OUString sWidth( RTL_CONSTASCII_USTRINGPARAM("Width") );
307 		Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
308 		Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
309 		sal_Int32 nWidth = 0;
310 		xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
311 		const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
312 
313 		// reference column gets new width + rounding errors
314 		xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
315 
316 		xCols->insertByIndex( nCol + 1, nNewCols );
317 		mnRight += nNewCols;
318 
319 		// distribute new width
320 		for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
321 		{
322 			Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
323 			xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
324 		}
325 	}
326 
327 	for( nRow = 0; nRow < nRowCount; ++nRow )
328 	{
329 		CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
330 		if( !xCell.is() || xCell->isMerged() )
331 		{
332             if( nNewCols > 0 )
333             {
334 		        // merged cells are ignored, but newly added columns will be added to leftovers
335                 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
336                 if( !xCell.is() || !xCell->isMerged() )
337 			    rLeftOvers[nRow] += nNewCols;
338             }
339 		}
340 		else
341 		{
342 			sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
343 			sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
344 
345 			if( (nRow >= mnTop) && (nRow <= mnBottom) )
346 			{
347 				sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
348 				if( nColSpan == 0 )
349 					nCellsAvailable += nNewCols;
350 
351 				DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
352 
353 				sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
354 
355 				sal_Int32 nSplitCol = nCol;
356 				sal_Int32 nSplits = nColumns + 1;
357 				while( nSplits-- )
358 				{
359 					// last split eats rounding cells
360 					if( nSplits == 0 )
361 						nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
362 
363 					mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
364 					if( nSplits > 0 )
365 						nSplitCol += nSplitSpan + 1;
366 				}
367 
368 				do
369 				{
370 					rLeftOvers[nRow++] = 0;
371 				}
372 				while( nRowSpan-- );
373 				--nRow;
374 			}
375 			else
376 			{
377 				// cope with outside cells, merge if needed
378 				if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
379 					mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
380 
381 				do
382 				{
383 					rLeftOvers[nRow++] = 0; // consumed
384 				}
385 				while( nRowSpan-- );
386 				--nRow;
387 			}
388 		}
389 	}
390 }
391 
392 // -----------------------------------------------------------------------------
393 
split_horizontal(sal_Int32 nColumns)394 void CellCursor::split_horizontal( sal_Int32 nColumns )
395 {
396 	const sal_Int32 nRowCount = mxTable->getRowCount();
397 
398 	std::vector< sal_Int32 > aLeftOvers( nRowCount );
399 
400 	for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
401 		split_column( nCol, nColumns, aLeftOvers );
402 }
403 
404 // -----------------------------------------------------------------------------
405 
split_row(sal_Int32 nRow,sal_Int32 nRows,std::vector<sal_Int32> & rLeftOvers)406 void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
407 {
408 	const sal_Int32 nColCount = mxTable->getColumnCount();
409 
410 	sal_Int32 nNewRows = 0, nCol;
411 
412 	// first check how many columns we need to add
413 	for( nCol = mnLeft; nCol <= mnRight; ++nCol )
414 	{
415 		CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
416 		if( xCell.is() && !xCell->isMerged() )
417 			nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
418 	}
419 
420 	if( nNewRows > 0 )
421 	{
422 		const OUString sHeight( RTL_CONSTASCII_USTRINGPARAM("Height") );
423 		Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
424 		Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
425 		sal_Int32 nHeight = 0;
426 		xRefRow->getPropertyValue( sHeight ) >>= nHeight;
427 		const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
428 
429 		// reference row gets new height + rounding errors
430 		xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
431 
432 		xRows->insertByIndex( nRow + 1, nNewRows );
433 		mnBottom += nNewRows;
434 
435 		// distribute new width
436 		for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
437 		{
438 			Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
439 			xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
440 		}
441 	}
442 
443 	for( nCol = 0; nCol < nColCount; ++nCol )
444 	{
445 		CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
446 		if( !xCell.is() || xCell->isMerged() )
447 		{
448             if( nNewRows )
449             {
450 			    // merged cells are ignored, but newly added columns will be added to leftovers
451                 xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
452                 if( !xCell.is() || !xCell->isMerged() )
453 			        rLeftOvers[nCol] += nNewRows;
454             }
455 		}
456 		else
457 		{
458 			sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
459 			sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
460 
461 			if( (nCol >= mnLeft) && (nCol <= mnRight) )
462 			{
463 				sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
464 				if( nRowSpan == 0 )
465 					nCellsAvailable += nNewRows;
466 
467 				DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
468 
469 				sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
470 
471 				sal_Int32 nSplitRow = nRow;
472 				sal_Int32 nSplits = nRows + 1;
473 				while( nSplits-- )
474 				{
475 					// last split eats rounding cells
476 					if( nSplits == 0 )
477 						nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
478 
479                     mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
480 					if( nSplits > 0 )
481 						nSplitRow += nSplitSpan + 1;
482 				}
483 
484 				do
485 				{
486 					rLeftOvers[nCol++] = 0;
487 				}
488 				while( nColSpan-- );
489 				--nCol;
490 			}
491 			else
492 			{
493 				// cope with outside cells, merge if needed
494 				if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
495 					mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
496 
497 				do
498 				{
499 					rLeftOvers[nCol++] = 0; // consumed
500 				}
501 				while( nColSpan-- );
502 				--nCol;
503 			}
504 		}
505 	}
506 }
507 
508 // -----------------------------------------------------------------------------
509 
split_vertical(sal_Int32 nRows)510 void CellCursor::split_vertical( sal_Int32 nRows )
511 {
512 	const sal_Int32 nColCount = mxTable->getColumnCount();
513 
514 	std::vector< sal_Int32 > aLeftOvers( nColCount );
515 
516 	for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
517 		split_row( nRow, nRows, aLeftOvers );
518 }
519 
520 // -----------------------------------------------------------------------------
521 
split(sal_Int32 nColumns,sal_Int32 nRows)522 void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows ) throw (NoSupportException, IllegalArgumentException, RuntimeException)
523 {
524 	if( (nColumns < 0) || (nRows < 0) )
525 		throw IllegalArgumentException();
526 
527 	if( !mxTable.is() || (mxTable->getSdrTableObj() == 0) )
528 		throw DisposedException();
529 
530 	SdrModel* pModel = mxTable->getSdrTableObj()->GetModel();
531 	const bool bUndo = pModel && mxTable->getSdrTableObj()->IsInserted() && pModel->IsUndoEnabled();
532 	if( bUndo )
533 		pModel->BegUndo( ImpGetResStr(STR_TABLE_SPLIT) );
534 
535 	try
536 	{
537 		if( nColumns > 0 )
538 			split_horizontal( nColumns );
539 
540 		if( nRows > 0 )
541 			split_vertical( nRows );
542 
543 		if( nColumns > 0 ||nRows > 0 )
544 			mxTable->setModified(sal_True);
545 	}
546 	catch( Exception& )
547 	{
548 		DBG_ERROR("sdr::table::CellCursor::split(), exception caught!");
549 		throw NoSupportException();
550 	}
551 
552 	if( bUndo )
553 		pModel->EndUndo();
554 
555 	if( pModel )
556 		pModel->SetChanged();
557 }
558 
559 // -----------------------------------------------------------------------------
560 
isMergeable()561 sal_Bool SAL_CALL CellCursor::isMergeable(  ) throw (RuntimeException)
562 {
563 	CellPos aStart, aEnd;
564 	return GetMergedSelection( aStart, aEnd ) ? sal_True : sal_False;
565 }
566 
567 // -----------------------------------------------------------------------------
568 
isUnmergeable()569 sal_Bool SAL_CALL CellCursor::isUnmergeable(  ) throw (RuntimeException)
570 {
571 	// this is true if there is at least one merged cell in the current range
572 	for( sal_Int32 nRow = mnTop; nRow <= mnBottom; nRow++ )
573 	{
574 		for( sal_Int32 nCol = mnLeft; nCol <= mnRight; nCol++ )
575 		{
576 			CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
577 			if( xCell.is() && ( (xCell->getRowSpan() > 1) || (xCell->getColumnSpan() > 1) ) )
578 				return sal_True;
579 		}
580 	}
581 	return sal_False;
582 }
583 
584 // -----------------------------------------------------------------------------
585 
586 } }
587