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