1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include "svx/svdotable.hxx"
22 #include "cellcursor.hxx"
23 #include "tablelayouter.hxx"
25 #include "svx/svdmodel.hxx"
26 #include "svx/svdstr.hrc"
27 #include "svdglob.hxx"
31 using namespace ::com::sun::star::uno
;
32 using namespace ::com::sun::star::lang
;
33 using namespace ::com::sun::star::container
;
34 using namespace ::com::sun::star::beans
;
35 using namespace ::com::sun::star::table
;
39 namespace sdr
{ namespace table
{
45 CellCursor::CellCursor( const TableModelRef
& xTable
, sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
)
46 : CellCursorBase( xTable
, nLeft
, nTop
, nRight
, nBottom
)
52 CellCursor::~CellCursor()
60 Reference
< XCell
> SAL_CALL
CellCursor::getCellByPosition( sal_Int32 nColumn
, sal_Int32 nRow
) throw (IndexOutOfBoundsException
, RuntimeException
, std::exception
)
62 return CellRange::getCellByPosition( nColumn
, nRow
);
67 Reference
< XCellRange
> SAL_CALL
CellCursor::getCellRangeByPosition( sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
) throw (IndexOutOfBoundsException
, RuntimeException
, std::exception
)
69 return CellRange::getCellRangeByPosition( nLeft
, nTop
, nRight
, nBottom
);
74 Reference
< XCellRange
> SAL_CALL
CellCursor::getCellRangeByName( const OUString
& aRange
) throw (RuntimeException
, std::exception
)
76 return CellRange::getCellRangeByName( aRange
);
83 void SAL_CALL
CellCursor::gotoStart( ) throw (RuntimeException
, std::exception
)
91 void SAL_CALL
CellCursor::gotoEnd( ) throw (RuntimeException
, std::exception
)
99 void SAL_CALL
CellCursor::gotoNext( ) throw (RuntimeException
, std::exception
)
104 if( mnRight
>= mxTable
->getColumnCount() )
106 // if we past the last column, try skip to the row line
108 if( mnTop
>= mxTable
->getRowCount() )
110 // if we past the last row, do not move cursor at all
116 // restart at the first column on the next row
128 void SAL_CALL
CellCursor::gotoPrevious( ) throw (RuntimeException
, std::exception
)
139 mnLeft
= mxTable
->getColumnCount() - 1;
149 void SAL_CALL
CellCursor::gotoOffset( ::sal_Int32 nColumnOffset
, ::sal_Int32 nRowOffset
) throw (RuntimeException
, std::exception
)
153 const sal_Int32 nLeft
= mnLeft
+ nColumnOffset
;
154 if( (nLeft
>= 0) && (nLeft
< mxTable
->getColumnCount() ) )
155 mnRight
= mnLeft
= nLeft
;
157 const sal_Int32 nTop
= mnTop
+ nRowOffset
;
158 if( (nTop
>= 0) && (nTop
< mxTable
->getRowCount()) )
159 mnTop
= mnBottom
= nTop
;
164 // XMergeableCellCursor
167 /** returns true and the merged cell positions if a merge is valid or false if a merge is
168 not valid for that range */
169 bool CellCursor::GetMergedSelection( CellPos
& rStart
, CellPos
& rEnd
)
171 rStart
.mnCol
= mnLeft
; rStart
.mnRow
= mnTop
;
172 rEnd
.mnCol
= mnRight
; rEnd
.mnRow
= mnBottom
;
174 // single cell merge is never valid
175 if( mxTable
.is() && ((mnLeft
!= mnRight
) || (mnTop
!= mnBottom
)) ) try
177 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( mnLeft
, mnTop
).get() ) );
179 // check if first cell is merged
180 if( xCell
.is() && xCell
->isMerged() )
181 findMergeOrigin( mxTable
, mnLeft
, mnTop
, rStart
.mnCol
, rStart
.mnRow
);
183 // check if last cell is merged
184 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( mnRight
, mnBottom
).get() ) );
187 if( xCell
->isMerged() )
189 findMergeOrigin( mxTable
, mnRight
, mnBottom
, rEnd
.mnCol
, rEnd
.mnRow
);
190 // merge not possible if selection is only one cell and all its merges
193 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( rEnd
.mnCol
, rEnd
.mnRow
).get() ) );
198 rEnd
.mnCol
+= xCell
->getColumnSpan()-1;
199 rEnd
.mnRow
+= xCell
->getRowSpan()-1;
202 // now check if everything is inside the given bounds
203 sal_Int32 nRow
, nCol
;
204 for( nRow
= rStart
.mnRow
; nRow
<= rEnd
.mnRow
; nRow
++ )
206 for( nCol
= rStart
.mnCol
; nCol
<= rEnd
.mnCol
; nCol
++ )
208 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
212 if( xCell
->isMerged() )
214 sal_Int32 nOriginCol
, nOriginRow
;
215 if( findMergeOrigin( mxTable
, nCol
, nRow
, nOriginCol
, nOriginRow
) )
217 if( (nOriginCol
< rStart
.mnCol
) || (nOriginRow
< rStart
.mnRow
) )
220 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nOriginCol
, nOriginRow
).get() ) );
223 nOriginCol
+= xCell
->getColumnSpan()-1;
224 nOriginRow
+= xCell
->getRowSpan()-1;
226 if( (nOriginCol
> rEnd
.mnCol
) || (nOriginRow
> rEnd
.mnRow
) )
231 else if( ((nCol
+ xCell
->getColumnSpan() - 1) > rEnd
.mnCol
) || ((nRow
+ xCell
->getRowSpan() - 1 ) > rEnd
.mnRow
) )
241 OSL_FAIL("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
248 void SAL_CALL
CellCursor::merge( ) throw (NoSupportException
, RuntimeException
, std::exception
)
250 CellPos aStart
, aEnd
;
251 if( !GetMergedSelection( aStart
, aEnd
) )
252 throw NoSupportException();
254 if( !mxTable
.is() || (mxTable
->getSdrTableObj() == 0) )
255 throw DisposedException();
257 SdrModel
* pModel
= mxTable
->getSdrTableObj()->GetModel();
258 const bool bUndo
= pModel
&& mxTable
->getSdrTableObj()->IsInserted() && pModel
->IsUndoEnabled();
261 pModel
->BegUndo( ImpGetResStr(STR_TABLE_MERGE
) );
265 mxTable
->merge( aStart
.mnCol
, aStart
.mnRow
, aEnd
.mnCol
- aStart
.mnCol
+ 1, aEnd
.mnRow
- aStart
.mnRow
+ 1 );
267 mxTable
->setModified(sal_True
);
271 OSL_FAIL("sdr::table::CellCursor::merge(), exception caught!");
278 pModel
->SetChanged();
283 void CellCursor::split_column( sal_Int32 nCol
, sal_Int32 nColumns
, std::vector
< sal_Int32
>& rLeftOvers
)
285 const sal_Int32 nRowCount
= mxTable
->getRowCount();
287 sal_Int32 nNewCols
= 0, nRow
;
289 // first check how many columns we need to add
290 for( nRow
= mnTop
; nRow
<= mnBottom
; ++nRow
)
292 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
293 if( xCell
.is() && !xCell
->isMerged() )
294 nNewCols
= std::max( nNewCols
, nColumns
- xCell
->getColumnSpan() + 1 - rLeftOvers
[nRow
] );
299 const OUString
sWidth("Width");
300 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_QUERY_THROW
);
301 Reference
< XPropertySet
> xRefColumn( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
302 sal_Int32 nWidth
= 0;
303 xRefColumn
->getPropertyValue( sWidth
) >>= nWidth
;
304 const sal_Int32 nNewWidth
= nWidth
/ (nNewCols
+ 1);
306 // reference column gets new width + rounding errors
307 xRefColumn
->setPropertyValue( sWidth
, Any( nWidth
- (nNewWidth
* nNewCols
) ) );
309 xCols
->insertByIndex( nCol
+ 1, nNewCols
);
312 // distribute new width
313 for( sal_Int32 nNewCol
= nCol
+ nNewCols
; nNewCol
> nCol
; --nNewCol
)
315 Reference
< XPropertySet
> xNewCol( xCols
->getByIndex( nNewCol
), UNO_QUERY_THROW
);
316 xNewCol
->setPropertyValue( sWidth
, Any( nNewWidth
) );
320 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
322 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
323 if( !xCell
.is() || xCell
->isMerged() )
327 // merged cells are ignored, but newly added columns will be added to leftovers
328 xCell
.set( dynamic_cast< Cell
* >(mxTable
->getCellByPosition( nCol
+1, nRow
).get() ) );
329 if( !xCell
.is() || !xCell
->isMerged() )
330 rLeftOvers
[nRow
] += nNewCols
;
335 sal_Int32 nRowSpan
= xCell
->getRowSpan() - 1;
336 sal_Int32 nColSpan
= xCell
->getColumnSpan() - 1;
338 if( (nRow
>= mnTop
) && (nRow
<= mnBottom
) )
340 sal_Int32 nCellsAvailable
= 1 + nColSpan
+ rLeftOvers
[nRow
];
342 nCellsAvailable
+= nNewCols
;
344 DBG_ASSERT( nCellsAvailable
> nColumns
, "sdr::table::CellCursor::split_column(), somethings wrong" );
346 sal_Int32 nSplitSpan
= (nCellsAvailable
/ (nColumns
+ 1)) - 1;
348 sal_Int32 nSplitCol
= nCol
;
349 sal_Int32 nSplits
= nColumns
+ 1;
352 // last split eats rounding cells
354 nSplitSpan
= nCellsAvailable
- ((nSplitSpan
+1) * nColumns
) - 1;
356 mxTable
->merge( nSplitCol
, nRow
, nSplitSpan
+ 1, nRowSpan
+ 1);
358 nSplitCol
+= nSplitSpan
+ 1;
363 rLeftOvers
[nRow
++] = 0;
370 // cope with outside cells, merge if needed
371 if( nColSpan
< (rLeftOvers
[nRow
] + nNewCols
) )
372 mxTable
->merge( nCol
, nRow
, (rLeftOvers
[nRow
] + nNewCols
) + 1, nRowSpan
+ 1 );
376 rLeftOvers
[nRow
++] = 0; // consumed
387 void CellCursor::split_horizontal( sal_Int32 nColumns
)
389 const sal_Int32 nRowCount
= mxTable
->getRowCount();
391 std::vector
< sal_Int32
> aLeftOvers( nRowCount
);
393 for( sal_Int32 nCol
= mnRight
; nCol
>= mnLeft
; --nCol
)
394 split_column( nCol
, nColumns
, aLeftOvers
);
399 void CellCursor::split_row( sal_Int32 nRow
, sal_Int32 nRows
, std::vector
< sal_Int32
>& rLeftOvers
)
401 const sal_Int32 nColCount
= mxTable
->getColumnCount();
403 sal_Int32 nNewRows
= 0, nCol
;
405 // first check how many columns we need to add
406 for( nCol
= mnLeft
; nCol
<= mnRight
; ++nCol
)
408 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
409 if( xCell
.is() && !xCell
->isMerged() )
410 nNewRows
= std::max( nNewRows
, nRows
- xCell
->getRowSpan() + 1 - rLeftOvers
[nCol
] );
415 const OUString
sHeight("Height");
416 Reference
< XTableRows
> xRows( mxTable
->getRows(), UNO_QUERY_THROW
);
417 Reference
< XPropertySet
> xRefRow( xRows
->getByIndex( nRow
), UNO_QUERY_THROW
);
418 sal_Int32 nHeight
= 0;
419 xRefRow
->getPropertyValue( sHeight
) >>= nHeight
;
420 const sal_Int32 nNewHeight
= nHeight
/ (nNewRows
+ 1);
422 // reference row gets new height + rounding errors
423 xRefRow
->setPropertyValue( sHeight
, Any( nHeight
- (nNewHeight
* nNewRows
) ) );
425 xRows
->insertByIndex( nRow
+ 1, nNewRows
);
426 mnBottom
+= nNewRows
;
428 // distribute new width
429 for( sal_Int32 nNewRow
= nRow
+ nNewRows
; nNewRow
> nRow
; --nNewRow
)
431 Reference
< XPropertySet
> xNewRow( xRows
->getByIndex( nNewRow
), UNO_QUERY_THROW
);
432 xNewRow
->setPropertyValue( sHeight
, Any( nNewHeight
) );
436 for( nCol
= 0; nCol
< nColCount
; ++nCol
)
438 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
439 if( !xCell
.is() || xCell
->isMerged() )
443 // merged cells are ignored, but newly added columns will be added to leftovers
444 xCell
.set( dynamic_cast< Cell
* >(mxTable
->getCellByPosition( nCol
, nRow
+1 ).get() ) );
445 if( !xCell
.is() || !xCell
->isMerged() )
446 rLeftOvers
[nCol
] += nNewRows
;
451 sal_Int32 nRowSpan
= xCell
->getRowSpan() - 1;
452 sal_Int32 nColSpan
= xCell
->getColumnSpan() - 1;
454 if( (nCol
>= mnLeft
) && (nCol
<= mnRight
) )
456 sal_Int32 nCellsAvailable
= 1 + nRowSpan
+ rLeftOvers
[nCol
];
458 nCellsAvailable
+= nNewRows
;
460 DBG_ASSERT( nCellsAvailable
> nRows
, "sdr::table::CellCursor::split_row(), somethings wrong" );
462 sal_Int32 nSplitSpan
= (nCellsAvailable
/ (nRows
+ 1)) - 1;
464 sal_Int32 nSplitRow
= nRow
;
465 sal_Int32 nSplits
= nRows
+ 1;
468 // last split eats rounding cells
470 nSplitSpan
= nCellsAvailable
- ((nSplitSpan
+1) * nRows
) - 1;
472 mxTable
->merge( nCol
, nSplitRow
, nColSpan
+ 1, nSplitSpan
+ 1 );
474 nSplitRow
+= nSplitSpan
+ 1;
479 rLeftOvers
[nCol
++] = 0;
486 // cope with outside cells, merge if needed
487 if( nRowSpan
< (rLeftOvers
[nCol
] + nNewRows
) )
488 mxTable
->merge( nCol
, nRow
, nColSpan
+ 1, (rLeftOvers
[nCol
] + nNewRows
) + 1 );
492 rLeftOvers
[nCol
++] = 0; // consumed
503 void CellCursor::split_vertical( sal_Int32 nRows
)
505 const sal_Int32 nColCount
= mxTable
->getColumnCount();
507 std::vector
< sal_Int32
> aLeftOvers( nColCount
);
509 for( sal_Int32 nRow
= mnBottom
; nRow
>= mnTop
; --nRow
)
510 split_row( nRow
, nRows
, aLeftOvers
);
515 void SAL_CALL
CellCursor::split( sal_Int32 nColumns
, sal_Int32 nRows
) throw (NoSupportException
, IllegalArgumentException
, RuntimeException
, std::exception
)
517 if( (nColumns
< 0) || (nRows
< 0) )
518 throw IllegalArgumentException();
520 if( !mxTable
.is() || (mxTable
->getSdrTableObj() == 0) )
521 throw DisposedException();
523 SdrModel
* pModel
= mxTable
->getSdrTableObj()->GetModel();
524 const bool bUndo
= pModel
&& mxTable
->getSdrTableObj()->IsInserted() && pModel
->IsUndoEnabled();
526 pModel
->BegUndo( ImpGetResStr(STR_TABLE_SPLIT
) );
531 split_horizontal( nColumns
);
534 split_vertical( nRows
);
536 if( nColumns
> 0 ||nRows
> 0 )
537 mxTable
->setModified(sal_True
);
541 OSL_FAIL("sdr::table::CellCursor::split(), exception caught!");
542 throw NoSupportException();
549 pModel
->SetChanged();
554 sal_Bool SAL_CALL
CellCursor::isMergeable( ) throw (RuntimeException
, std::exception
)
556 CellPos aStart
, aEnd
;
557 return GetMergedSelection( aStart
, aEnd
) ? sal_True
: sal_False
;
564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */