LibreOffice Module svx (master)  1
cellcursor.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <com/sun/star/lang/NoSupportException.hpp>
23 #include <svx/svdotable.hxx>
24 #include "cellcursor.hxx"
25 #include "tablelayouter.hxx"
26 #include <cell.hxx>
27 #include <svx/svdmodel.hxx>
28 #include <svx/strings.hrc>
29 #include <svx/dialmgr.hxx>
30 #include <tools/debug.hxx>
31 
32 
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;
38 
39 
40 namespace sdr::table {
41 
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 )
44 {
45 }
46 
47 
49 {
50 }
51 
52 
53 // XCellCursor
54 
55 
56 Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
57 {
58  return CellRange::getCellByPosition( nColumn, nRow );
59 }
60 
61 
62 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
63 {
64  return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
65 }
66 
67 
68 Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange )
69 {
70  return CellRange::getCellRangeByName( aRange );
71 }
72 
73 
74 // XCellCursor
75 
76 
77 void SAL_CALL CellCursor::gotoStart( )
78 {
79  mnRight = mnLeft;
80  mnBottom = mnTop;
81 }
82 
83 
84 void SAL_CALL CellCursor::gotoEnd( )
85 {
86  mnLeft = mnRight;
87  mnTop = mnBottom;
88 }
89 
90 
91 void SAL_CALL CellCursor::gotoNext( )
92 {
93  if( mxTable.is() )
94  {
95  mnRight++;
96  if( mnRight >= mxTable->getColumnCount() )
97  {
98  // if we past the last column, try skip to the row line
99  mnTop++;
100  if( mnTop >= mxTable->getRowCount() )
101  {
102  // if we past the last row, do not move cursor at all
103  mnTop--;
104  mnRight--;
105  }
106  else
107  {
108  // restart at the first column on the next row
109  mnRight = 0;
110  }
111  }
112  }
113 
114  mnLeft = mnRight;
115  mnTop = mnBottom;
116 }
117 
118 
119 void SAL_CALL CellCursor::gotoPrevious( )
120 {
121  if( mxTable.is() )
122  {
123  if( mnLeft > 0 )
124  {
125  --mnLeft;
126  }
127  else if( mnTop > 0 )
128  {
129  --mnTop;
130  mnLeft = mxTable->getColumnCount() - 1;
131  }
132  }
133 
134  mnRight = mnLeft;
135  mnBottom = mnTop;
136 }
137 
138 
139 void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset )
140 {
141  if( mxTable.is() )
142  {
143  const sal_Int32 nLeft = mnLeft + nColumnOffset;
144  if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
145  mnRight = mnLeft = nLeft;
146 
147  const sal_Int32 nTop = mnTop + nRowOffset;
148  if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
149  mnTop = mnBottom = nTop;
150  }
151 }
152 
153 
154 // XMergeableCellCursor
155 
156 
160 {
161  rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
162  rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
163 
164  // single cell merge is never valid
165  if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
166  {
167  CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
168 
169  // check if first cell is merged
170  if( xCell.is() && xCell->isMerged() )
171  findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
172 
173  // check if last cell is merged
174  xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
175  if( xCell.is() )
176  {
177  if( xCell->isMerged() )
178  {
179  findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
180  // merge not possible if selection is only one cell and all its merges
181  if( rEnd == rStart )
182  return false;
183  xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
184  }
185  }
186  if( xCell.is() )
187  {
188  rEnd.mnCol += xCell->getColumnSpan()-1;
189  rEnd.mnRow += xCell->getRowSpan()-1;
190  }
191 
192  // now check if everything is inside the given bounds
193  sal_Int32 nRow, nCol;
194  for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
195  {
196  for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
197  {
198  xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
199  if( !xCell.is() )
200  continue;
201 
202  if( xCell->isMerged() )
203  {
204  sal_Int32 nOriginCol, nOriginRow;
205  if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
206  {
207  if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
208  return false;
209 
210  xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
211  if( xCell.is() )
212  {
213  nOriginCol += xCell->getColumnSpan()-1;
214  nOriginRow += xCell->getRowSpan()-1;
215 
216  if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
217  return false;
218  }
219  }
220  }
221  else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
222  {
223  return false;
224  }
225  }
226  }
227  return true;
228  }
229  catch( Exception& )
230  {
231  OSL_FAIL("sdr::table::SvmxTableController::GetMergedSelection(), exception caught!");
232  }
233  return false;
234 }
235 
236 
237 void SAL_CALL CellCursor::merge( )
238 {
239  CellPos aStart, aEnd;
240  if( !GetMergedSelection( aStart, aEnd ) )
241  throw NoSupportException();
242 
243  if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
244  throw DisposedException();
245 
246  SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
247  const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
248 
249  if( bUndo )
250  rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
251 
252  try
253  {
254  mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
255  mxTable->optimize();
256  mxTable->setModified(true);
257  }
258  catch( Exception& )
259  {
260  OSL_FAIL("sdr::table::CellCursor::merge(), exception caught!");
261  }
262 
263  if( bUndo )
264  rModel.EndUndo();
265 
266  rModel.SetChanged();
267 }
268 
269 
270 void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
271 {
272  const sal_Int32 nRowCount = mxTable->getRowCount();
273 
274  sal_Int32 nNewCols = 0, nRow;
275 
276  // first check how many columns we need to add
277  for( nRow = mnTop; nRow <= mnBottom; ++nRow )
278  {
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] );
282  }
283 
284  if( nNewCols > 0 )
285  {
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);
292 
293  // reference column gets new width + rounding errors
294  xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
295 
296  xCols->insertByIndex( nCol + 1, nNewCols );
297  mnRight += nNewCols;
298 
299  // distribute new width
300  for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
301  {
302  Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
303  xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
304  }
305  }
306 
307  for( nRow = 0; nRow < nRowCount; ++nRow )
308  {
309  CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
310  if( !xCell.is() || xCell->isMerged() )
311  {
312  if( nNewCols > 0 )
313  {
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;
318  }
319  }
320  else
321  {
322  sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
323  sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
324 
325  if( (nRow >= mnTop) && (nRow <= mnBottom) )
326  {
327  sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
328  if( nColSpan == 0 )
329  nCellsAvailable += nNewCols;
330 
331  DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
332 
333  sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
334 
335  sal_Int32 nSplitCol = nCol;
336  sal_Int32 nSplits = nColumns + 1;
337  while( nSplits-- )
338  {
339  // last split eats rounding cells
340  if( nSplits == 0 )
341  nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
342 
343  mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
344  if( nSplits > 0 )
345  nSplitCol += nSplitSpan + 1;
346  }
347 
348  do
349  {
350  rLeftOvers[nRow++] = 0;
351  }
352  while( nRowSpan-- );
353  --nRow;
354  }
355  else
356  {
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 );
360 
361  do
362  {
363  rLeftOvers[nRow++] = 0; // consumed
364  }
365  while( nRowSpan-- );
366  --nRow;
367  }
368  }
369  }
370 }
371 
372 
373 void CellCursor::split_horizontal( sal_Int32 nColumns )
374 {
375  const sal_Int32 nRowCount = mxTable->getRowCount();
376 
377  std::vector< sal_Int32 > aLeftOvers( nRowCount );
378 
379  for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
380  split_column( nCol, nColumns, aLeftOvers );
381 }
382 
383 
384 void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
385 {
386  const sal_Int32 nColCount = mxTable->getColumnCount();
387 
388  sal_Int32 nNewRows = 0, nCol;
389 
390  // first check how many columns we need to add
391  for( nCol = mnLeft; nCol <= mnRight; ++nCol )
392  {
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] );
396  }
397 
398  if( nNewRows > 0 )
399  {
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);
406 
407  // reference row gets new height + rounding errors
408  xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
409 
410  xRows->insertByIndex( nRow + 1, nNewRows );
411  mnBottom += nNewRows;
412 
413  // distribute new width
414  for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
415  {
416  Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
417  xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
418  }
419  }
420 
421  for( nCol = 0; nCol < nColCount; ++nCol )
422  {
423  CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
424  if( !xCell.is() || xCell->isMerged() )
425  {
426  if( nNewRows )
427  {
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;
432  }
433  }
434  else
435  {
436  sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
437  sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
438 
439  if( (nCol >= mnLeft) && (nCol <= mnRight) )
440  {
441  sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
442  if( nRowSpan == 0 )
443  nCellsAvailable += nNewRows;
444 
445  DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
446 
447  sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
448 
449  sal_Int32 nSplitRow = nRow;
450  sal_Int32 nSplits = nRows + 1;
451  while( nSplits-- )
452  {
453  // last split eats rounding cells
454  if( nSplits == 0 )
455  nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
456 
457  mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
458  if( nSplits > 0 )
459  nSplitRow += nSplitSpan + 1;
460  }
461 
462  do
463  {
464  rLeftOvers[nCol++] = 0;
465  }
466  while( nColSpan-- );
467  --nCol;
468  }
469  else
470  {
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 );
474 
475  do
476  {
477  rLeftOvers[nCol++] = 0; // consumed
478  }
479  while( nColSpan-- );
480  --nCol;
481  }
482  }
483  }
484 }
485 
486 
487 void CellCursor::split_vertical( sal_Int32 nRows )
488 {
489  const sal_Int32 nColCount = mxTable->getColumnCount();
490 
491  std::vector< sal_Int32 > aLeftOvers( nColCount );
492 
493  for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
494  split_row( nRow, nRows, aLeftOvers );
495 }
496 
497 
498 void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows )
499 {
500  if( (nColumns < 0) || (nRows < 0) )
501  throw IllegalArgumentException();
502 
503  if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
504  throw DisposedException();
505 
506  SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
507  const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
508 
509  if( bUndo )
510  rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
511 
512  try
513  {
514  if( nColumns > 0 )
515  split_horizontal( nColumns );
516 
517  if( nRows > 0 )
518  split_vertical( nRows );
519 
520  if( nColumns > 0 ||nRows > 0 )
521  mxTable->setModified(true);
522  }
523  catch( Exception& )
524  {
525  OSL_FAIL("sdr::table::CellCursor::split(), exception caught!");
526  throw NoSupportException();
527  }
528 
529  if( bUndo )
530  rModel.EndUndo();
531 
532  rModel.SetChanged();
533 }
534 
535 
537 {
538  CellPos aStart, aEnd;
539  return GetMergedSelection( aStart, aEnd );
540 }
541 
542 
543 }
544 
545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool findMergeOrigin(const TableModelRef &xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 &rOriginX, sal_Int32 &rOriginY)
returns true if the cell(nMergedX,nMergedY) is merged with other cells.
const int nColCount
sal_Int32 mnRight
sal_Int32 mnLeft
virtual sal_Bool SAL_CALL isMergeable() override
Definition: cellcursor.cxx:536
void split_horizontal(sal_Int32 nColumns)
Definition: cellcursor.cxx:373
bool GetMergedSelection(CellPos &rStart, CellPos &rEnd)
returns true and the merged cell positions if a merge is valid or false if a merge is not valid for t...
Definition: cellcursor.cxx:159
virtual void SAL_CALL split(::sal_Int32 Columns,::sal_Int32 Rows) override
Definition: cellcursor.cxx:498
void BegUndo()
Definition: svdmodel.cxx:425
::cppu::ImplInheritanceHelper< CellRange, css::table::XCellCursor, css::table::XMergeableCellRange > CellCursorBase
Definition: cellcursor.hxx:31
virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow) override
Definition: cellcursor.cxx:56
sal_Int32 mnBottom
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:28
virtual void SAL_CALL gotoNext() override
Definition: cellcursor.cxx:91
virtual void SAL_CALL gotoEnd() override
Definition: cellcursor.cxx:84
virtual void SAL_CALL gotoOffset(::sal_Int32 nColumnOffset,::sal_Int32 nRowOffset) override
Definition: cellcursor.cxx:139
virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition(sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom) override
Definition: cellrange.cxx:88
virtual void SAL_CALL gotoPrevious() override
Definition: cellcursor.cxx:119
#define DBG_ASSERT(sCon, aError)
void split_column(sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 > &rLeftOvers)
Definition: cellcursor.cxx:270
virtual void SAL_CALL gotoStart() override
Definition: cellcursor.cxx:77
unsigned char sal_Bool
virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition(sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom) override
Definition: cellcursor.cxx:62
virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName(const OUString &aRange) override
Definition: cellrange.cxx:108
sal_Int32 mnTop
CellCursor(const TableModelRef &xTableModel, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom)
Definition: cellcursor.cxx:42
virtual ~CellCursor() override
Definition: cellcursor.cxx:48
void split_vertical(sal_Int32 nRows)
Definition: cellcursor.cxx:487
virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName(const OUString &aRange) override
Definition: cellcursor.cxx:68
virtual void SAL_CALL merge() override
Definition: cellcursor.cxx:237
virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow) override
Definition: cellrange.cxx:82
void split_row(sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 > &rLeftOvers)
Definition: cellcursor.cxx:384