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 .
20 #include <sal/config.h>
22 #include <com/sun/star/lang/NoSupportException.hpp>
23 #include <svx/svdotable.hxx>
24 #include "cellcursor.hxx"
25 #include "tablelayouter.hxx"
27 #include <svx/svdmodel.hxx>
28 #include <svx/strings.hrc>
29 #include <svx/dialmgr.hxx>
30 #include <tools/debug.hxx>
33 using namespace ::com::sun::star::uno
;
34 using namespace ::com::sun::star::lang
;
35 using namespace ::com::sun::star::container
;
36 using namespace ::com::sun::star::beans
;
37 using namespace ::com::sun::star::table
;
40 namespace sdr
{ namespace table
{
42 CellCursor::CellCursor( const TableModelRef
& xTable
, sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
)
43 : CellCursorBase( xTable
, nLeft
, nTop
, nRight
, nBottom
)
48 CellCursor::~CellCursor()
56 Reference
< XCell
> SAL_CALL
CellCursor::getCellByPosition( sal_Int32 nColumn
, sal_Int32 nRow
)
58 return CellRange::getCellByPosition( nColumn
, nRow
);
62 Reference
< XCellRange
> SAL_CALL
CellCursor::getCellRangeByPosition( sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
)
64 return CellRange::getCellRangeByPosition( nLeft
, nTop
, nRight
, nBottom
);
68 Reference
< XCellRange
> SAL_CALL
CellCursor::getCellRangeByName( const OUString
& aRange
)
70 return CellRange::getCellRangeByName( aRange
);
77 void SAL_CALL
CellCursor::gotoStart( )
84 void SAL_CALL
CellCursor::gotoEnd( )
91 void SAL_CALL
CellCursor::gotoNext( )
96 if( mnRight
>= mxTable
->getColumnCount() )
98 // if we past the last column, try skip to the row line
100 if( mnTop
>= mxTable
->getRowCount() )
102 // if we past the last row, do not move cursor at all
108 // restart at the first column on the next row
119 void SAL_CALL
CellCursor::gotoPrevious( )
130 mnLeft
= mxTable
->getColumnCount() - 1;
139 void SAL_CALL
CellCursor::gotoOffset( ::sal_Int32 nColumnOffset
, ::sal_Int32 nRowOffset
)
143 const sal_Int32 nLeft
= mnLeft
+ nColumnOffset
;
144 if( (nLeft
>= 0) && (nLeft
< mxTable
->getColumnCount() ) )
145 mnRight
= mnLeft
= nLeft
;
147 const sal_Int32 nTop
= mnTop
+ nRowOffset
;
148 if( (nTop
>= 0) && (nTop
< mxTable
->getRowCount()) )
149 mnTop
= mnBottom
= nTop
;
154 // XMergeableCellCursor
157 /** returns true and the merged cell positions if a merge is valid or false if a merge is
158 not valid for that range */
159 bool CellCursor::GetMergedSelection( CellPos
& rStart
, CellPos
& rEnd
)
161 rStart
.mnCol
= mnLeft
; rStart
.mnRow
= mnTop
;
162 rEnd
.mnCol
= mnRight
; rEnd
.mnRow
= mnBottom
;
164 // single cell merge is never valid
165 if( mxTable
.is() && ((mnLeft
!= mnRight
) || (mnTop
!= mnBottom
)) ) try
167 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( mnLeft
, mnTop
).get() ) );
169 // check if first cell is merged
170 if( xCell
.is() && xCell
->isMerged() )
171 findMergeOrigin( mxTable
, mnLeft
, mnTop
, rStart
.mnCol
, rStart
.mnRow
);
173 // check if last cell is merged
174 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( mnRight
, mnBottom
).get() ) );
177 if( xCell
->isMerged() )
179 findMergeOrigin( mxTable
, mnRight
, mnBottom
, rEnd
.mnCol
, rEnd
.mnRow
);
180 // merge not possible if selection is only one cell and all its merges
183 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( rEnd
.mnCol
, rEnd
.mnRow
).get() ) );
188 rEnd
.mnCol
+= xCell
->getColumnSpan()-1;
189 rEnd
.mnRow
+= xCell
->getRowSpan()-1;
192 // now check if everything is inside the given bounds
193 sal_Int32 nRow
, nCol
;
194 for( nRow
= rStart
.mnRow
; nRow
<= rEnd
.mnRow
; nRow
++ )
196 for( nCol
= rStart
.mnCol
; nCol
<= rEnd
.mnCol
; nCol
++ )
198 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
202 if( xCell
->isMerged() )
204 sal_Int32 nOriginCol
, nOriginRow
;
205 if( findMergeOrigin( mxTable
, nCol
, nRow
, nOriginCol
, nOriginRow
) )
207 if( (nOriginCol
< rStart
.mnCol
) || (nOriginRow
< rStart
.mnRow
) )
210 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nOriginCol
, nOriginRow
).get() ) );
213 nOriginCol
+= xCell
->getColumnSpan()-1;
214 nOriginRow
+= xCell
->getRowSpan()-1;
216 if( (nOriginCol
> rEnd
.mnCol
) || (nOriginRow
> rEnd
.mnRow
) )
221 else if( ((nCol
+ xCell
->getColumnSpan() - 1) > rEnd
.mnCol
) || ((nRow
+ xCell
->getRowSpan() - 1 ) > rEnd
.mnRow
) )
231 OSL_FAIL("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
237 void SAL_CALL
CellCursor::merge( )
239 CellPos aStart
, aEnd
;
240 if( !GetMergedSelection( aStart
, aEnd
) )
241 throw NoSupportException();
243 if( !mxTable
.is() || (mxTable
->getSdrTableObj() == nullptr) )
244 throw DisposedException();
246 SdrModel
& rModel(mxTable
->getSdrTableObj()->getSdrModelFromSdrObject());
247 const bool bUndo(mxTable
->getSdrTableObj()->IsInserted() && rModel
.IsUndoEnabled());
250 rModel
.BegUndo( SvxResId(STR_TABLE_MERGE
) );
254 mxTable
->merge( aStart
.mnCol
, aStart
.mnRow
, aEnd
.mnCol
- aStart
.mnCol
+ 1, aEnd
.mnRow
- aStart
.mnRow
+ 1 );
256 mxTable
->setModified(true);
260 OSL_FAIL("sdr::table::CellCursor::merge(), exception caught!");
270 void CellCursor::split_column( sal_Int32 nCol
, sal_Int32 nColumns
, std::vector
< sal_Int32
>& rLeftOvers
)
272 const sal_Int32 nRowCount
= mxTable
->getRowCount();
274 sal_Int32 nNewCols
= 0, nRow
;
276 // first check how many columns we need to add
277 for( nRow
= mnTop
; nRow
<= mnBottom
; ++nRow
)
279 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
280 if( xCell
.is() && !xCell
->isMerged() )
281 nNewCols
= std::max( nNewCols
, nColumns
- xCell
->getColumnSpan() + 1 - rLeftOvers
[nRow
] );
286 const OUString
sWidth("Width");
287 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_SET_THROW
);
288 Reference
< XPropertySet
> xRefColumn( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
289 sal_Int32 nWidth
= 0;
290 xRefColumn
->getPropertyValue( sWidth
) >>= nWidth
;
291 const sal_Int32 nNewWidth
= nWidth
/ (nNewCols
+ 1);
293 // reference column gets new width + rounding errors
294 xRefColumn
->setPropertyValue( sWidth
, Any( nWidth
- (nNewWidth
* nNewCols
) ) );
296 xCols
->insertByIndex( nCol
+ 1, nNewCols
);
299 // distribute new width
300 for( sal_Int32 nNewCol
= nCol
+ nNewCols
; nNewCol
> nCol
; --nNewCol
)
302 Reference
< XPropertySet
> xNewCol( xCols
->getByIndex( nNewCol
), UNO_QUERY_THROW
);
303 xNewCol
->setPropertyValue( sWidth
, Any( nNewWidth
) );
307 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
309 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
310 if( !xCell
.is() || xCell
->isMerged() )
314 // merged cells are ignored, but newly added columns will be added to leftovers
315 xCell
.set( dynamic_cast< Cell
* >(mxTable
->getCellByPosition( nCol
+1, nRow
).get() ) );
316 if( !xCell
.is() || !xCell
->isMerged() )
317 rLeftOvers
[nRow
] += nNewCols
;
322 sal_Int32 nRowSpan
= xCell
->getRowSpan() - 1;
323 sal_Int32 nColSpan
= xCell
->getColumnSpan() - 1;
325 if( (nRow
>= mnTop
) && (nRow
<= mnBottom
) )
327 sal_Int32 nCellsAvailable
= 1 + nColSpan
+ rLeftOvers
[nRow
];
329 nCellsAvailable
+= nNewCols
;
331 DBG_ASSERT( nCellsAvailable
> nColumns
, "sdr::table::CellCursor::split_column(), somethings wrong" );
333 sal_Int32 nSplitSpan
= (nCellsAvailable
/ (nColumns
+ 1)) - 1;
335 sal_Int32 nSplitCol
= nCol
;
336 sal_Int32 nSplits
= nColumns
+ 1;
339 // last split eats rounding cells
341 nSplitSpan
= nCellsAvailable
- ((nSplitSpan
+1) * nColumns
) - 1;
343 mxTable
->merge( nSplitCol
, nRow
, nSplitSpan
+ 1, nRowSpan
+ 1);
345 nSplitCol
+= nSplitSpan
+ 1;
350 rLeftOvers
[nRow
++] = 0;
357 // cope with outside cells, merge if needed
358 if( nColSpan
< (rLeftOvers
[nRow
] + nNewCols
) )
359 mxTable
->merge( nCol
, nRow
, (rLeftOvers
[nRow
] + nNewCols
) + 1, nRowSpan
+ 1 );
363 rLeftOvers
[nRow
++] = 0; // consumed
373 void CellCursor::split_horizontal( sal_Int32 nColumns
)
375 const sal_Int32 nRowCount
= mxTable
->getRowCount();
377 std::vector
< sal_Int32
> aLeftOvers( nRowCount
);
379 for( sal_Int32 nCol
= mnRight
; nCol
>= mnLeft
; --nCol
)
380 split_column( nCol
, nColumns
, aLeftOvers
);
384 void CellCursor::split_row( sal_Int32 nRow
, sal_Int32 nRows
, std::vector
< sal_Int32
>& rLeftOvers
)
386 const sal_Int32 nColCount
= mxTable
->getColumnCount();
388 sal_Int32 nNewRows
= 0, nCol
;
390 // first check how many columns we need to add
391 for( nCol
= mnLeft
; nCol
<= mnRight
; ++nCol
)
393 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
394 if( xCell
.is() && !xCell
->isMerged() )
395 nNewRows
= std::max( nNewRows
, nRows
- xCell
->getRowSpan() + 1 - rLeftOvers
[nCol
] );
400 const OUString
sHeight("Height");
401 Reference
< XTableRows
> xRows( mxTable
->getRows(), UNO_SET_THROW
);
402 Reference
< XPropertySet
> xRefRow( xRows
->getByIndex( nRow
), UNO_QUERY_THROW
);
403 sal_Int32 nHeight
= 0;
404 xRefRow
->getPropertyValue( sHeight
) >>= nHeight
;
405 const sal_Int32 nNewHeight
= nHeight
/ (nNewRows
+ 1);
407 // reference row gets new height + rounding errors
408 xRefRow
->setPropertyValue( sHeight
, Any( nHeight
- (nNewHeight
* nNewRows
) ) );
410 xRows
->insertByIndex( nRow
+ 1, nNewRows
);
411 mnBottom
+= nNewRows
;
413 // distribute new width
414 for( sal_Int32 nNewRow
= nRow
+ nNewRows
; nNewRow
> nRow
; --nNewRow
)
416 Reference
< XPropertySet
> xNewRow( xRows
->getByIndex( nNewRow
), UNO_QUERY_THROW
);
417 xNewRow
->setPropertyValue( sHeight
, Any( nNewHeight
) );
421 for( nCol
= 0; nCol
< nColCount
; ++nCol
)
423 CellRef
xCell( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( nCol
, nRow
).get() ) );
424 if( !xCell
.is() || xCell
->isMerged() )
428 // merged cells are ignored, but newly added columns will be added to leftovers
429 xCell
.set( dynamic_cast< Cell
* >(mxTable
->getCellByPosition( nCol
, nRow
+1 ).get() ) );
430 if( !xCell
.is() || !xCell
->isMerged() )
431 rLeftOvers
[nCol
] += nNewRows
;
436 sal_Int32 nRowSpan
= xCell
->getRowSpan() - 1;
437 sal_Int32 nColSpan
= xCell
->getColumnSpan() - 1;
439 if( (nCol
>= mnLeft
) && (nCol
<= mnRight
) )
441 sal_Int32 nCellsAvailable
= 1 + nRowSpan
+ rLeftOvers
[nCol
];
443 nCellsAvailable
+= nNewRows
;
445 DBG_ASSERT( nCellsAvailable
> nRows
, "sdr::table::CellCursor::split_row(), somethings wrong" );
447 sal_Int32 nSplitSpan
= (nCellsAvailable
/ (nRows
+ 1)) - 1;
449 sal_Int32 nSplitRow
= nRow
;
450 sal_Int32 nSplits
= nRows
+ 1;
453 // last split eats rounding cells
455 nSplitSpan
= nCellsAvailable
- ((nSplitSpan
+1) * nRows
) - 1;
457 mxTable
->merge( nCol
, nSplitRow
, nColSpan
+ 1, nSplitSpan
+ 1 );
459 nSplitRow
+= nSplitSpan
+ 1;
464 rLeftOvers
[nCol
++] = 0;
471 // cope with outside cells, merge if needed
472 if( nRowSpan
< (rLeftOvers
[nCol
] + nNewRows
) )
473 mxTable
->merge( nCol
, nRow
, nColSpan
+ 1, (rLeftOvers
[nCol
] + nNewRows
) + 1 );
477 rLeftOvers
[nCol
++] = 0; // consumed
487 void CellCursor::split_vertical( sal_Int32 nRows
)
489 const sal_Int32 nColCount
= mxTable
->getColumnCount();
491 std::vector
< sal_Int32
> aLeftOvers( nColCount
);
493 for( sal_Int32 nRow
= mnBottom
; nRow
>= mnTop
; --nRow
)
494 split_row( nRow
, nRows
, aLeftOvers
);
498 void SAL_CALL
CellCursor::split( sal_Int32 nColumns
, sal_Int32 nRows
)
500 if( (nColumns
< 0) || (nRows
< 0) )
501 throw IllegalArgumentException();
503 if( !mxTable
.is() || (mxTable
->getSdrTableObj() == nullptr) )
504 throw DisposedException();
506 SdrModel
& rModel(mxTable
->getSdrTableObj()->getSdrModelFromSdrObject());
507 const bool bUndo(mxTable
->getSdrTableObj()->IsInserted() && rModel
.IsUndoEnabled());
510 rModel
.BegUndo( SvxResId(STR_TABLE_SPLIT
) );
515 split_horizontal( nColumns
);
518 split_vertical( nRows
);
520 if( nColumns
> 0 ||nRows
> 0 )
521 mxTable
->setModified(true);
525 OSL_FAIL("sdr::table::CellCursor::split(), exception caught!");
526 throw NoSupportException();
536 sal_Bool SAL_CALL
CellCursor::isMergeable( )
538 CellPos aStart
, aEnd
;
539 return GetMergedSelection( aStart
, aEnd
);
545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */