1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: cellcursor.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include "svx/svdotable.hxx"
35 #include "cellcursor.hxx"
36 #include "tablelayouter.hxx"
38 #include "svx/svdmodel.hxx"
40 #include "svdglob.hxx"
42 // -----------------------------------------------------------------------------
44 using ::rtl::OUString
;
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star::container
;
48 using namespace ::com::sun::star::beans
;
49 using namespace ::com::sun::star::table
;
51 // -----------------------------------------------------------------------------
53 namespace sdr
{ namespace table
{
55 // -----------------------------------------------------------------------------
57 // -----------------------------------------------------------------------------
59 CellCursor::CellCursor( const TableModelRef
& xTable
, sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
)
60 : CellCursorBase( xTable
, nLeft
, nTop
, nRight
, nBottom
)
64 // -----------------------------------------------------------------------------
66 CellCursor::~CellCursor()
70 // -----------------------------------------------------------------------------
72 // -----------------------------------------------------------------------------
74 Reference
< XCell
> SAL_CALL
CellCursor::getCellByPosition( sal_Int32 nColumn
, sal_Int32 nRow
) throw (IndexOutOfBoundsException
, RuntimeException
)
76 return CellRange::getCellByPosition( nColumn
, nRow
);
79 // -----------------------------------------------------------------------------
81 Reference
< XCellRange
> SAL_CALL
CellCursor::getCellRangeByPosition( sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
) throw (IndexOutOfBoundsException
, RuntimeException
)
83 return CellRange::getCellRangeByPosition( nLeft
, nTop
, nRight
, nBottom
);
86 // -----------------------------------------------------------------------------
88 Reference
< XCellRange
> SAL_CALL
CellCursor::getCellRangeByName( const OUString
& aRange
) throw (RuntimeException
)
90 return CellRange::getCellRangeByName( aRange
);
93 // -----------------------------------------------------------------------------
95 // -----------------------------------------------------------------------------
97 void SAL_CALL
CellCursor::gotoStart( ) throw (RuntimeException
)
103 // -----------------------------------------------------------------------------
105 void SAL_CALL
CellCursor::gotoEnd( ) throw (RuntimeException
)
111 // -----------------------------------------------------------------------------
113 void SAL_CALL
CellCursor::gotoNext( ) throw (RuntimeException
)
118 if( mnRight
>= mxTable
->getColumnCount() )
120 // if we past the last column, try skip to the row line
122 if( mnTop
>= mxTable
->getRowCount() )
124 // if we past the last row, do not move cursor at all
130 // restart at the first column on the next row
140 // -----------------------------------------------------------------------------
142 void SAL_CALL
CellCursor::gotoPrevious( ) throw (RuntimeException
)
153 mnLeft
= mxTable
->getColumnCount() - 1;
161 // -----------------------------------------------------------------------------
163 void SAL_CALL
CellCursor::gotoOffset( ::sal_Int32 nColumnOffset
, ::sal_Int32 nRowOffset
) throw (RuntimeException
)
167 const sal_Int32 nLeft
= mnLeft
+ nColumnOffset
;
168 if( (nLeft
>= 0) && (nLeft
< mxTable
->getColumnCount() ) )
169 mnRight
= mnLeft
= nLeft
;
171 const sal_Int32 nTop
= mnTop
+ nRowOffset
;
172 if( (nTop
>= 0) && (nTop
< mxTable
->getRowCount()) )
173 mnTop
= mnBottom
= nTop
;
177 // -----------------------------------------------------------------------------
178 // XMergeableCellCursor
179 // -----------------------------------------------------------------------------
181 /** returns true and the merged cell positions if a merge is valid or false if a merge is
182 not valid for that range */
183 bool CellCursor::GetMergedSelection( CellPos
& rStart
, CellPos
& rEnd
)
185 rStart
.mnCol
= mnLeft
; rStart
.mnRow
= mnTop
;
186 rEnd
.mnCol
= mnRight
; rEnd
.mnRow
= mnBottom
;
188 // single cell merge is never valid
189 if( mxTable
.is() && ((mnLeft
!= mnRight
) || (mnTop
!= mnBottom
)) ) try
191 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( mnLeft
, mnTop
).get() ) );
193 // check if first cell is merged
194 if( xCell
.is() && xCell
->isMerged() )
195 findMergeOrigin( mxTable
, mnLeft
, mnTop
, rStart
.mnCol
, rStart
.mnRow
);
197 // check if last cell is merged
198 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( mnRight
, mnBottom
).get() ) );
201 if( xCell
->isMerged() )
203 findMergeOrigin( mxTable
, mnRight
, mnBottom
, rEnd
.mnCol
, rEnd
.mnRow
);
204 // merge not possible if selection is only one cell and all its merges
207 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( rEnd
.mnCol
, rEnd
.mnRow
).get() ) );
212 rEnd
.mnCol
+= xCell
->getColumnSpan()-1;
213 rEnd
.mnRow
+= xCell
->getRowSpan()-1;
216 // now check if everything is inside the given bounds
217 sal_Int32 nRow
, nCol
;
218 for( nRow
= rStart
.mnRow
; nRow
<= rEnd
.mnRow
; nRow
++ )
220 for( nCol
= rStart
.mnCol
; nCol
<= rEnd
.mnCol
; nCol
++ )
222 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
226 if( xCell
->isMerged() )
228 sal_Int32 nOriginCol
, nOriginRow
;
229 if( findMergeOrigin( mxTable
, nCol
, nRow
, nOriginCol
, nOriginRow
) )
231 if( (nOriginCol
< rStart
.mnCol
) || (nOriginRow
< rStart
.mnRow
) )
234 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nOriginCol
, nOriginRow
).get() ) );
237 nOriginCol
+= xCell
->getColumnSpan()-1;
238 nOriginRow
+= xCell
->getRowSpan()-1;
240 if( (nOriginCol
> rEnd
.mnCol
) || (nOriginRow
> rEnd
.mnRow
) )
245 else if( ((nCol
+ xCell
->getColumnSpan() - 1) > rEnd
.mnCol
) || ((nRow
+ xCell
->getRowSpan() - 1 ) > rEnd
.mnRow
) )
255 DBG_ERROR("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
260 // -----------------------------------------------------------------------------
262 void SAL_CALL
CellCursor::merge( ) throw (NoSupportException
, RuntimeException
)
264 CellPos aStart
, aEnd
;
265 if( !GetMergedSelection( aStart
, aEnd
) )
266 throw NoSupportException();
268 if( !mxTable
.is() || (mxTable
->getSdrTableObj() == 0) )
269 throw DisposedException();
271 SdrModel
* pModel
= mxTable
->getSdrTableObj()->GetModel();
272 const bool bUndo
= pModel
&& mxTable
->getSdrTableObj()->IsInserted() && pModel
->IsUndoEnabled();
275 pModel
->BegUndo( ImpGetResStr(STR_TABLE_MERGE
) );
279 mxTable
->merge( aStart
.mnCol
, aStart
.mnRow
, aEnd
.mnCol
- aStart
.mnCol
+ 1, aEnd
.mnRow
- aStart
.mnRow
+ 1 );
281 mxTable
->setModified(sal_True
);
285 DBG_ERROR("sdr::table::CellCursor::merge(), exception caught!");
292 pModel
->SetChanged();
295 // -----------------------------------------------------------------------------
297 void CellCursor::split_column( sal_Int32 nCol
, sal_Int32 nColumns
, std::vector
< sal_Int32
>& rLeftOvers
)
299 const sal_Int32 nRowCount
= mxTable
->getRowCount();
301 sal_Int32 nNewCols
= 0, nRow
;
303 // first check how many columns we need to add
304 for( nRow
= mnTop
; nRow
<= mnBottom
; ++nRow
)
306 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
307 if( xCell
.is() && !xCell
->isMerged() )
308 nNewCols
= std::max( nNewCols
, nColumns
- xCell
->getColumnSpan() + 1 - rLeftOvers
[nRow
] );
313 const OUString
sWidth( RTL_CONSTASCII_USTRINGPARAM("Width") );
314 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_QUERY_THROW
);
315 Reference
< XPropertySet
> xRefColumn( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
316 sal_Int32 nWidth
= 0;
317 xRefColumn
->getPropertyValue( sWidth
) >>= nWidth
;
318 const sal_Int32 nNewWidth
= nWidth
/ (nNewCols
+ 1);
320 // reference column gets new width + rounding errors
321 xRefColumn
->setPropertyValue( sWidth
, Any( nWidth
- (nNewWidth
* nNewCols
) ) );
323 xCols
->insertByIndex( nCol
+ 1, nNewCols
);
326 // distribute new width
327 for( sal_Int32 nNewCol
= nCol
+ nNewCols
; nNewCol
> nCol
; --nNewCol
)
329 Reference
< XPropertySet
> xNewCol( xCols
->getByIndex( nNewCol
), UNO_QUERY_THROW
);
330 xNewCol
->setPropertyValue( sWidth
, Any( nNewWidth
) );
334 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
336 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
337 if( !xCell
.is() || xCell
->isMerged() )
341 // merged cells are ignored, but newly added columns will be added to leftovers
342 xCell
.set( dynamic_cast< Cell
* >(mxTable
->getCellByPosition( nCol
+1, nRow
).get() ) );
343 if( !xCell
.is() || !xCell
->isMerged() )
344 rLeftOvers
[nRow
] += nNewCols
;
349 sal_Int32 nRowSpan
= xCell
->getRowSpan() - 1;
350 sal_Int32 nColSpan
= xCell
->getColumnSpan() - 1;
352 if( (nRow
>= mnTop
) && (nRow
<= mnBottom
) )
354 sal_Int32 nCellsAvailable
= 1 + nColSpan
+ rLeftOvers
[nRow
];
356 nCellsAvailable
+= nNewCols
;
358 DBG_ASSERT( nCellsAvailable
> nColumns
, "sdr::table::CellCursor::split_column(), somethings wrong" );
360 sal_Int32 nSplitSpan
= (nCellsAvailable
/ (nColumns
+ 1)) - 1;
362 sal_Int32 nSplitCol
= nCol
;
363 sal_Int32 nSplits
= nColumns
+ 1;
366 // last split eats rounding cells
368 nSplitSpan
= nCellsAvailable
- ((nSplitSpan
+1) * nColumns
) - 1;
370 mxTable
->merge( nSplitCol
, nRow
, nSplitSpan
+ 1, nRowSpan
+ 1);
372 nSplitCol
+= nSplitSpan
+ 1;
377 rLeftOvers
[nRow
++] = 0;
384 // cope with outside cells, merge if needed
385 if( nColSpan
< (rLeftOvers
[nRow
] + nNewCols
) )
386 mxTable
->merge( nCol
, nRow
, (rLeftOvers
[nRow
] + nNewCols
) + 1, nRowSpan
+ 1 );
390 rLeftOvers
[nRow
++] = 0; // consumed
399 // -----------------------------------------------------------------------------
401 void CellCursor::split_horizontal( sal_Int32 nColumns
)
403 const sal_Int32 nRowCount
= mxTable
->getRowCount();
405 std::vector
< sal_Int32
> aLeftOvers( nRowCount
);
407 for( sal_Int32 nCol
= mnRight
; nCol
>= mnLeft
; --nCol
)
408 split_column( nCol
, nColumns
, aLeftOvers
);
411 // -----------------------------------------------------------------------------
413 void CellCursor::split_row( sal_Int32 nRow
, sal_Int32 nRows
, std::vector
< sal_Int32
>& rLeftOvers
)
415 const sal_Int32 nColCount
= mxTable
->getColumnCount();
417 sal_Int32 nNewRows
= 0, nCol
;
419 // first check how many columns we need to add
420 for( nCol
= mnLeft
; nCol
<= mnRight
; ++nCol
)
422 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
423 if( xCell
.is() && !xCell
->isMerged() )
424 nNewRows
= std::max( nNewRows
, nRows
- xCell
->getRowSpan() + 1 - rLeftOvers
[nCol
] );
429 const OUString
sHeight( RTL_CONSTASCII_USTRINGPARAM("Height") );
430 Reference
< XTableRows
> xRows( mxTable
->getRows(), UNO_QUERY_THROW
);
431 Reference
< XPropertySet
> xRefRow( xRows
->getByIndex( nRow
), UNO_QUERY_THROW
);
432 sal_Int32 nHeight
= 0;
433 xRefRow
->getPropertyValue( sHeight
) >>= nHeight
;
434 const sal_Int32 nNewHeight
= nHeight
/ (nNewRows
+ 1);
436 // reference row gets new height + rounding errors
437 xRefRow
->setPropertyValue( sHeight
, Any( nHeight
- (nNewHeight
* nNewRows
) ) );
439 xRows
->insertByIndex( nRow
+ 1, nNewRows
);
440 mnBottom
+= nNewRows
;
442 // distribute new width
443 for( sal_Int32 nNewRow
= nRow
+ nNewRows
; nNewRow
> nRow
; --nNewRow
)
445 Reference
< XPropertySet
> xNewRow( xRows
->getByIndex( nNewRow
), UNO_QUERY_THROW
);
446 xNewRow
->setPropertyValue( sHeight
, Any( nNewHeight
) );
450 for( nCol
= 0; nCol
< nColCount
; ++nCol
)
452 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
453 if( !xCell
.is() || xCell
->isMerged() )
457 // merged cells are ignored, but newly added columns will be added to leftovers
458 xCell
.set( dynamic_cast< Cell
* >(mxTable
->getCellByPosition( nCol
, nRow
+1 ).get() ) );
459 if( !xCell
.is() || !xCell
->isMerged() )
460 rLeftOvers
[nCol
] += nNewRows
;
465 sal_Int32 nRowSpan
= xCell
->getRowSpan() - 1;
466 sal_Int32 nColSpan
= xCell
->getColumnSpan() - 1;
468 if( (nCol
>= mnLeft
) && (nCol
<= mnRight
) )
470 sal_Int32 nCellsAvailable
= 1 + nRowSpan
+ rLeftOvers
[nCol
];
472 nCellsAvailable
+= nNewRows
;
474 DBG_ASSERT( nCellsAvailable
> nRows
, "sdr::table::CellCursor::split_row(), somethings wrong" );
476 sal_Int32 nSplitSpan
= (nCellsAvailable
/ (nRows
+ 1)) - 1;
478 sal_Int32 nSplitRow
= nRow
;
479 sal_Int32 nSplits
= nRows
+ 1;
482 // last split eats rounding cells
484 nSplitSpan
= nCellsAvailable
- ((nSplitSpan
+1) * nRows
) - 1;
486 mxTable
->merge( nCol
, nSplitRow
, nColSpan
+ 1, nSplitSpan
+ 1 );
488 nSplitRow
+= nSplitSpan
+ 1;
493 rLeftOvers
[nCol
++] = 0;
500 // cope with outside cells, merge if needed
501 if( nRowSpan
< (rLeftOvers
[nCol
] + nNewRows
) )
502 mxTable
->merge( nCol
, nRow
, nColSpan
+ 1, (rLeftOvers
[nCol
] + nNewRows
) + 1 );
506 rLeftOvers
[nCol
++] = 0; // consumed
515 // -----------------------------------------------------------------------------
517 void CellCursor::split_vertical( sal_Int32 nRows
)
519 const sal_Int32 nColCount
= mxTable
->getColumnCount();
521 std::vector
< sal_Int32
> aLeftOvers( nColCount
);
523 for( sal_Int32 nRow
= mnBottom
; nRow
>= mnTop
; --nRow
)
524 split_row( nRow
, nRows
, aLeftOvers
);
527 // -----------------------------------------------------------------------------
529 void SAL_CALL
CellCursor::split( sal_Int32 nColumns
, sal_Int32 nRows
) throw (NoSupportException
, IllegalArgumentException
, RuntimeException
)
531 if( (nColumns
< 0) || (nRows
< 0) )
532 throw IllegalArgumentException();
534 if( !mxTable
.is() || (mxTable
->getSdrTableObj() == 0) )
535 throw DisposedException();
537 SdrModel
* pModel
= mxTable
->getSdrTableObj()->GetModel();
538 const bool bUndo
= pModel
&& mxTable
->getSdrTableObj()->IsInserted() && pModel
->IsUndoEnabled();
540 pModel
->BegUndo( ImpGetResStr(STR_TABLE_SPLIT
) );
545 split_horizontal( nColumns
);
548 split_vertical( nRows
);
550 if( nColumns
> 0 ||nRows
> 0 )
551 mxTable
->setModified(sal_True
);
555 DBG_ERROR("sdr::table::CellCursor::split(), exception caught!");
556 throw NoSupportException();
563 pModel
->SetChanged();
566 // -----------------------------------------------------------------------------
568 sal_Bool SAL_CALL
CellCursor::isMergeable( ) throw (RuntimeException
)
570 CellPos aStart
, aEnd
;
571 return GetMergedSelection( aStart
, aEnd
) ? sal_True
: sal_False
;
574 // -----------------------------------------------------------------------------
576 sal_Bool SAL_CALL
CellCursor::isUnmergeable( ) throw (RuntimeException
)
578 // this is true if there is at least one merged cell in the current range
579 for( sal_Int32 nRow
= mnTop
; nRow
<= mnBottom
; nRow
++ )
581 for( sal_Int32 nCol
= mnLeft
; nCol
<= mnRight
; nCol
++ )
583 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
584 if( xCell
.is() && ( (xCell
->getRowSpan() > 1) || (xCell
->getColumnSpan() > 1) ) )
591 // -----------------------------------------------------------------------------