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>
32
33
34using namespace ::com::sun::star::uno;
35using namespace ::com::sun::star::lang;
36using namespace ::com::sun::star::container;
37using namespace ::com::sun::star::beans;
38using namespace ::com::sun::star::table;
39
40
41namespace sdr::table {
42
43CellCursor::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
57Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
58{
59 return CellRange::getCellByPosition( nColumn, nRow );
60}
61
62
63Reference< 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
69Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange )
70{
71 return CellRange::getCellRangeByName( aRange );
72}
73
74
75// XCellCursor
76
77
78void SAL_CALL CellCursor::gotoStart( )
79{
82}
83
84
85void SAL_CALL CellCursor::gotoEnd( )
86{
89}
90
91
92void 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
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
140void 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
238void 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
271void 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 constexpr 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
374void 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
385void 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 constexpr 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
488void 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
499void 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: */
sal_Int32 mnTop
sal_Int32 mnRight
sal_Int32 mnBottom
sal_Int32 mnLeft
void BegUndo()
Definition: svdmodel.cxx:382
virtual void SetChanged(bool bFlg=true)
Definition: svdmodel.cxx:1143
bool IsUndoEnabled() const
returns true if undo is currently enabled This returns false if undo was disabled using EnableUndo( f...
Definition: svdmodel.cxx:547
void EndUndo()
Definition: svdmodel.cxx:453
virtual void SAL_CALL gotoStart() override
Definition: cellcursor.cxx:78
virtual void SAL_CALL gotoOffset(::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset) override
Definition: cellcursor.cxx:140
virtual void SAL_CALL split(::sal_Int32 Columns, ::sal_Int32 Rows) override
Definition: cellcursor.cxx:499
virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName(const OUString &aRange) override
Definition: cellcursor.cxx:69
virtual ~CellCursor() override
Definition: cellcursor.cxx:49
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: cellcursor.cxx:57
virtual void SAL_CALL gotoEnd() override
Definition: cellcursor.cxx:85
void split_horizontal(sal_Int32 nColumns)
Definition: cellcursor.cxx:374
virtual void SAL_CALL gotoPrevious() override
Definition: cellcursor.cxx:120
CellCursor(const TableModelRef &xTableModel, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom)
Definition: cellcursor.cxx:43
virtual sal_Bool SAL_CALL isMergeable() override
Definition: cellcursor.cxx:537
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
void split_row(sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 > &rLeftOvers)
Definition: cellcursor.cxx:385
void split_column(sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 > &rLeftOvers)
Definition: cellcursor.cxx:271
virtual void SAL_CALL gotoNext() override
Definition: cellcursor.cxx:92
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
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: cellrange.cxx:109
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:89
virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow) override
Definition: cellrange.cxx:83
#define DBG_ASSERT(sCon, aError)
#define TOOLS_WARN_EXCEPTION(area, stream)
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
float u
@ Exception
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.
::cppu::ImplInheritanceHelper< CellRange, css::table::XCellCursor, css::table::XMergeableCellRange > CellCursorBase
Definition: cellcursor.hxx:31
unsigned char sal_Bool