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