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