Bump version to 6.4-15
[LibreOffice.git] / svx / source / table / tablecontroller.cxx
blob232ed782414acfefdf5d7c62cec49fe588bdb53f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <algorithm>
22 #include <svx/sdr/table/tablecontroller.hxx>
23 #include <tablemodel.hxx>
25 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
26 #include <com/sun/star/container/XIndexAccess.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/table/XMergeableCellRange.hpp>
30 #include <com/sun/star/table/XMergeableCell.hpp>
32 #include <sal/config.h>
33 #include <sal/log.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
38 #include <svl/whiter.hxx>
39 #include <svl/stritem.hxx>
41 #include <sfx2/request.hxx>
43 #include <svx/svdotable.hxx>
44 #include <svx/sdr/overlay/overlayobjectcell.hxx>
45 #include <svx/sdr/overlay/overlaymanager.hxx>
46 #include <svx/svxids.hrc>
47 #include <editeng/outlobj.hxx>
48 #include <svx/svdoutl.hxx>
49 #include <svx/svdpagv.hxx>
50 #include <svx/svdetc.hxx>
51 #include <editeng/editobj.hxx>
52 #include <editeng/editstat.hxx>
53 #include <editeng/unolingu.hxx>
54 #include <svx/sdrpagewindow.hxx>
55 #include <svx/selectioncontroller.hxx>
56 #include <svx/svdmodel.hxx>
57 #include <svx/sdrpaintwindow.hxx>
58 #include <svx/svxdlg.hxx>
59 #include <editeng/boxitem.hxx>
60 #include <cell.hxx>
61 #include <editeng/borderline.hxx>
62 #include <editeng/colritem.hxx>
63 #include <editeng/lineitem.hxx>
64 #include <svx/strings.hrc>
65 #include <svx/dialmgr.hxx>
66 #include <svx/svdpage.hxx>
67 #include <svx/sdmetitm.hxx>
68 #include <svx/sdtditm.hxx>
69 #include "tableundo.hxx"
70 #include "tablelayouter.hxx"
71 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
72 #include <memory>
73 #include <o3tl/enumarray.hxx>
74 #include <o3tl/enumrange.hxx>
75 #include <cppuhelper/implbase.hxx>
76 #include <comphelper/lok.hxx>
77 #include <sfx2/viewsh.hxx>
78 #include <sfx2/lokhelper.hxx>
79 #include <editeng/editview.hxx>
81 using ::editeng::SvxBorderLine;
82 using namespace sdr::table;
83 using namespace ::com::sun::star;
84 using namespace ::com::sun::star::uno;
85 using namespace ::com::sun::star::table;
86 using namespace ::com::sun::star::beans;
87 using namespace ::com::sun::star::container;
88 using namespace ::com::sun::star::text;
89 using namespace ::com::sun::star::style;
91 enum class CellPosFlag // signals the relative position of a cell to a selection
93 NONE = 0x0000, // not set or inside
94 // row
95 Before = 0x0001,
96 Left = 0x0002,
97 Right = 0x0004,
98 After = 0x0008,
99 // column
100 Upper = 0x0010,
101 Top = 0x0020,
102 Bottom = 0x0040,
103 Lower = 0x0080
105 namespace o3tl
106 { template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
108 namespace sdr { namespace table {
110 class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
112 public:
113 explicit SvxTableControllerModifyListener( SvxTableController* pController )
114 : mpController( pController ) {}
116 // XModifyListener
117 virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
119 // XEventListener
120 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
122 SvxTableController* mpController;
126 // XModifyListener
129 void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
131 if( mpController )
132 mpController->onTableModified();
136 // XEventListener
139 void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
141 mpController = nullptr;
145 // class SvxTableController
148 rtl::Reference< sdr::SelectionController > CreateTableController(
149 SdrView& rView,
150 const SdrTableObj& rObj,
151 const rtl::Reference< sdr::SelectionController >& xRefController )
153 return SvxTableController::create(rView, rObj, xRefController);
157 rtl::Reference< sdr::SelectionController > SvxTableController::create(
158 SdrView& rView,
159 const SdrTableObj& rObj,
160 const rtl::Reference< sdr::SelectionController >& xRefController )
162 if( xRefController.is() )
164 SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
166 if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
168 return xRefController;
172 return new SvxTableController(rView, rObj);
176 SvxTableController::SvxTableController(
177 SdrView& rView,
178 const SdrTableObj& rObj)
179 : mbCellSelectionMode(false)
180 ,mbHasJustMerged(false)
181 ,mbLeftButtonDown(false)
182 ,mrView(rView)
183 ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
184 ,mnUpdateEvent( nullptr )
186 mxTableObj->getActiveCellPos( maCursorFirstPos );
187 maCursorLastPos = maCursorFirstPos;
189 Reference< XTable > xTable( mxTableObj->getTable() );
190 if( xTable.is() )
192 mxModifyListener = new SvxTableControllerModifyListener( this );
193 xTable->addModifyListener( mxModifyListener );
195 mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
199 SvxTableController::~SvxTableController()
201 if( mnUpdateEvent )
203 Application::RemoveUserEvent( mnUpdateEvent );
206 if( mxModifyListener.is() && mxTableObj.get() )
208 Reference< XTable > xTable( mxTableObj->getTable() );
209 if( xTable.is() )
211 xTable->removeModifyListener( mxModifyListener );
212 mxModifyListener.clear();
217 bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
219 if(!checkTableObject())
220 return false;
222 SdrTableObj& rTableObj(*mxTableObj.get());
223 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
225 // check if we are read only
226 if( rModel.IsReadOnly())
228 switch( rKEvt.GetKeyCode().GetCode() )
230 case awt::Key::DOWN:
231 case awt::Key::UP:
232 case awt::Key::LEFT:
233 case awt::Key::RIGHT:
234 case awt::Key::TAB:
235 case awt::Key::HOME:
236 case awt::Key::END:
237 case awt::Key::NUM2:
238 case awt::Key::NUM4:
239 case awt::Key::NUM6:
240 case awt::Key::NUM8:
241 case awt::Key::ESCAPE:
242 case awt::Key::F2:
243 break;
244 default:
245 // tell the view we eat the event, no further processing needed
246 return true;
250 TblAction nAction = getKeyboardAction(rKEvt);
252 return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
255 namespace {
257 Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
259 if (!pWindow)
260 return rPoint;
262 return pWindow->PixelToLogic(rPoint);
267 bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
269 if (comphelper::LibreOfficeKit::isActive() && !pWindow)
271 // Tiled rendering: get the window that has the disabled map mode.
272 if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
274 if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
275 pWindow = static_cast<vcl::Window*>(pOutputDevice);
279 if( !pWindow || !checkTableObject() )
280 return false;
282 SdrViewEvent aVEvt;
283 if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
284 return false;
286 TableHitKind eHit = mxTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
288 mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
290 if( eHit == TableHitKind::Cell )
292 StartSelection( maMouseDownPos );
293 return true;
296 if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
297 return true; // right click will become context menu
299 // for cell selection with the mouse remember our first hit
300 if( mbLeftButtonDown )
302 RemoveSelection();
304 SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
306 if( pHdl )
308 mbLeftButtonDown = false;
310 else
312 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
314 if (!pTableObj || eHit == TableHitKind::NONE)
316 mbLeftButtonDown = false;
321 if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
323 bool bEmptyOutliner = false;
324 if (Outliner* pOutliner = mrView.GetTextEditOutliner())
326 if (pOutliner->GetParagraphCount() == 1)
328 if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
329 bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
332 if (bEmptyOutliner)
334 // Tiled rendering: a left double-click in an empty cell: select it.
335 StartSelection(maMouseDownPos);
336 setSelectedCells(maMouseDownPos, maMouseDownPos);
337 // Update graphic selection, should be hidden now.
338 mrView.AdjustMarkHdl();
339 return true;
343 return false;
347 bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
349 if( !checkTableObject() )
350 return false;
352 mbLeftButtonDown = false;
354 return rMEvt.GetClicks() == 2;
358 bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
360 if( !checkTableObject() )
361 return false;
363 SdrTableObj* pTableObj = mxTableObj.get();
364 CellPos aPos;
365 if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
367 if(aPos != maMouseDownPos)
369 if( mbCellSelectionMode )
371 setSelectedCells( maMouseDownPos, aPos );
372 return true;
374 else
376 StartSelection( maMouseDownPos );
379 else if( mbCellSelectionMode )
381 UpdateSelection( aPos );
382 return true;
385 return false;
389 void SvxTableController::onSelectionHasChanged()
391 bool bSelected = false;
393 SdrTableObj* pTableObj = mxTableObj.get();
394 if( pTableObj && pTableObj->IsTextEditActive() )
396 pTableObj->getActiveCellPos( maCursorFirstPos );
397 maCursorLastPos = maCursorFirstPos;
398 mbCellSelectionMode = false;
400 else
402 const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
403 if( rMarkList.GetMarkCount() == 1 )
404 bSelected = mxTableObj.get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
405 /* fdo#46186 Selecting the table means selecting the entire cells */
406 if (!hasSelectedCells() && pTableObj)
408 maCursorFirstPos = SdrTableObj::getFirstCell();
409 maCursorLastPos = pTableObj->getLastCell();
410 mbCellSelectionMode=true;
414 if( bSelected )
416 updateSelectionOverlay();
418 else
420 destroySelectionOverlay();
425 void SvxTableController::GetState( SfxItemSet& rSet )
427 if(!mxTable.is() || !mxTableObj.is())
428 return;
430 SdrTableObj& rTableObj(*mxTableObj.get());
431 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
432 std::unique_ptr<SfxItemSet> xSet;
433 bool bVertDone(false);
435 // Iterate over all requested items in the set.
436 SfxWhichIter aIter( rSet );
437 sal_uInt16 nWhich = aIter.FirstWhich();
438 while (nWhich)
440 switch (nWhich)
442 case SID_TABLE_VERT_BOTTOM:
443 case SID_TABLE_VERT_CENTER:
444 case SID_TABLE_VERT_NONE:
446 if(!bVertDone)
448 if (!xSet)
450 xSet.reset(new SfxItemSet(rModel.GetItemPool()));
451 MergeAttrFromSelectedCells(*xSet, false);
454 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
456 if (xSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
457 eAdj = xSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
459 rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
460 rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
461 rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
462 bVertDone = true;
464 break;
466 case SID_TABLE_DELETE_ROW:
467 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
468 rSet.DisableItem(SID_TABLE_DELETE_ROW);
469 break;
470 case SID_TABLE_DELETE_COL:
471 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
472 rSet.DisableItem(SID_TABLE_DELETE_COL);
473 break;
474 case SID_TABLE_DELETE_TABLE:
475 if( !mxTable.is() )
476 rSet.DisableItem(SID_TABLE_DELETE_TABLE);
477 break;
478 case SID_TABLE_MERGE_CELLS:
479 if( !mxTable.is() || !hasSelectedCells() )
480 rSet.DisableItem(SID_TABLE_MERGE_CELLS);
481 break;
482 case SID_TABLE_SPLIT_CELLS:
483 if( !hasSelectedCells() || !mxTable.is() )
484 rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
485 break;
487 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
488 case SID_TABLE_DISTRIBUTE_COLUMNS:
489 case SID_TABLE_DISTRIBUTE_ROWS:
491 bool bDistributeColumns = false;
492 bool bDistributeRows = false;
493 if( mxTable.is() )
495 CellPos aStart, aEnd;
496 getSelectedCells( aStart, aEnd );
498 bDistributeColumns = aStart.mnCol != aEnd.mnCol;
499 bDistributeRows = aStart.mnRow != aEnd.mnRow;
501 if( !bDistributeColumns )
502 rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
503 if( !bDistributeRows )
505 rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
506 rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
508 break;
511 default:
512 break;
514 nWhich = aIter.NextWhich();
519 void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
521 if(!checkTableObject())
522 return;
524 SdrTableObj& rTableObj(*mxTableObj.get());
525 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
526 bool bInsertAfter = true;
527 sal_uInt16 nCount = 0;
529 if( pArgs )
531 const SfxPoolItem* pItem = nullptr;
532 pArgs->GetItemState(nSId, false, &pItem);
533 if (pItem)
535 nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
536 if(SfxItemState::SET == pArgs->GetItemState(SID_TABLE_PARAM_INSERT_AFTER, true, &pItem))
537 bInsertAfter = static_cast<const SfxBoolItem*>(pItem)->GetValue();
541 CellPos aStart, aEnd;
542 if( hasSelectedCells() )
544 getSelectedCells( aStart, aEnd );
546 else
548 if( bInsertAfter )
550 aStart.mnCol = mxTable->getColumnCount() - 1;
551 aStart.mnRow = mxTable->getRowCount() - 1;
552 aEnd = aStart;
556 if( rTableObj.IsTextEditActive() )
557 mrView.SdrEndTextEdit(true);
559 RemoveSelection();
561 const OUString sSize( "Size" );
562 const bool bUndo(rModel.IsUndoEnabled());
564 switch( nSId )
566 case SID_TABLE_INSERT_COL:
568 TableModelNotifyGuard aGuard( mxTable.get() );
570 if( bUndo )
572 rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
573 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
576 Reference< XTableColumns > xCols( mxTable->getColumns() );
577 const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
578 const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
579 xCols->insertByIndex( nNewStartColumn, nNewColumns );
581 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
583 // Resolves fdo#61540
584 // On Insert before, the reference column whose size is going to be
585 // used for newly created column(s) is wrong. As the new columns are
586 // inserted before the reference column, the reference column moved
587 // to the new position by no., of new columns i.e (earlier+newcolumns).
588 Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
589 setPropertyValue( sSize,
590 Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
591 getPropertyValue( sSize ) );
594 // Copy cell properties
595 sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
596 sal_Int32 nRowSpan = 0;
597 bool bNewSpan = false;
599 for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
601 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
603 // When we insert new COLUMNs, we want to copy ROW spans.
604 if (xSourceCell.is() && nRowSpan == 0)
606 // we are not in a span yet. Let's find out if the current cell is in a span.
607 sal_Int32 nColSpan = sal_Int32();
608 sal_Int32 nSpanInfoCol = sal_Int32();
610 if( xSourceCell->getRowSpan() > 1 )
612 // The current cell is the top-left cell in a span.
613 // Get the span info and propagate it to the target.
614 nRowSpan = xSourceCell->getRowSpan();
615 nColSpan = xSourceCell->getColumnSpan();
616 nSpanInfoCol = nPropSrcCol;
618 else if( xSourceCell->isMerged() )
620 // The current cell is a middle cell in a 2D span.
621 // Look for the top-left cell in the span.
622 for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
624 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
625 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
627 nRowSpan = xMergeInfoCell->getRowSpan();
628 nColSpan = xMergeInfoCell->getColumnSpan();
629 break;
632 if( nRowSpan == 1 )
633 nRowSpan = 0;
636 // The target columns are outside the span; Start a new span.
637 if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
638 bNewSpan = true;
641 // Now copy the properties from the source to the targets
642 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
644 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
645 if( xTargetCell.is() )
647 if( nRowSpan > 0 )
649 if( bNewSpan )
650 xTargetCell->merge( 1, nRowSpan );
651 else
652 xTargetCell->setMerged();
654 xTargetCell->copyFormatFrom( xSourceCell );
658 if( nRowSpan > 0 )
660 --nRowSpan;
661 bNewSpan = false;
665 if( bUndo )
666 rModel.EndUndo();
668 aStart.mnCol = nNewStartColumn;
669 aStart.mnRow = 0;
670 aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
671 aEnd.mnRow = mxTable->getRowCount() - 1;
672 break;
675 case SID_TABLE_INSERT_ROW:
677 TableModelNotifyGuard aGuard( mxTable.get() );
679 if( bUndo )
681 rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
682 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
685 Reference< XTableRows > xRows( mxTable->getRows() );
686 const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
687 const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
688 xRows->insertByIndex( nNewRowStart, nNewRows );
690 for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
692 Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
693 setPropertyValue( sSize,
694 Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
695 getPropertyValue( sSize ) );
698 // Copy the cell properties
699 sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
700 sal_Int32 nColSpan = 0;
701 bool bNewSpan = false;
703 for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
705 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
707 if (!xSourceCell.is())
708 continue;
710 // When we insert new ROWs, we want to copy COLUMN spans.
711 if( nColSpan == 0 )
713 // we are not in a span yet. Let's find out if the current cell is in a span.
714 sal_Int32 nRowSpan = sal_Int32();
715 sal_Int32 nSpanInfoRow = sal_Int32();
717 if( xSourceCell->getColumnSpan() > 1 )
719 // The current cell is the top-left cell in a span.
720 // Get the span info and propagate it to the target.
721 nColSpan = xSourceCell->getColumnSpan();
722 nRowSpan = xSourceCell->getRowSpan();
723 nSpanInfoRow = nPropSrcRow;
725 else if( xSourceCell->isMerged() )
727 // The current cell is a middle cell in a 2D span.
728 // Look for the top-left cell in the span.
729 for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
731 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
732 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
734 nColSpan = xMergeInfoCell->getColumnSpan();
735 nRowSpan = xMergeInfoCell->getRowSpan();
736 break;
739 if( nColSpan == 1 )
740 nColSpan = 0;
743 // Inserted rows are outside the span; Start a new span.
744 if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
745 bNewSpan = true;
748 // Now copy the properties from the source to the targets
749 for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
751 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
752 if( xTargetCell.is() )
754 if( nColSpan > 0 )
756 if( bNewSpan )
757 xTargetCell->merge( nColSpan, 1 );
758 else
759 xTargetCell->setMerged();
761 xTargetCell->copyFormatFrom( xSourceCell );
765 if( nColSpan > 0 )
767 --nColSpan;
768 bNewSpan = false;
772 if( bUndo )
773 rModel.EndUndo();
775 aStart.mnCol = 0;
776 aStart.mnRow = nNewRowStart;
777 aEnd.mnCol = mxTable->getColumnCount() - 1;
778 aEnd.mnRow = aStart.mnRow + nNewRows - 1;
779 break;
783 StartSelection( aStart );
784 UpdateSelection( aEnd );
788 void SvxTableController::onDelete( sal_uInt16 nSId )
790 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
791 if( !pTableObj || !mxTable.is() )
792 return;
794 if( nSId == SID_TABLE_DELETE_TABLE )
796 if( pTableObj->IsTextEditActive() )
797 mrView.SdrEndTextEdit(true);
799 mrView.DeleteMarkedObj();
801 else if( hasSelectedCells() )
803 CellPos aStart, aEnd;
804 getSelectedCells( aStart, aEnd );
806 if( pTableObj->IsTextEditActive() )
807 mrView.SdrEndTextEdit(true);
809 RemoveSelection();
811 bool bDeleteTable = false;
812 switch( nSId )
814 case SID_TABLE_DELETE_COL:
816 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
817 if( nRemovedColumns == mxTable->getColumnCount() )
819 bDeleteTable = true;
821 else
823 Reference< XTableColumns > xCols( mxTable->getColumns() );
824 xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
826 break;
829 case SID_TABLE_DELETE_ROW:
831 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
832 if( nRemovedRows == mxTable->getRowCount() )
834 bDeleteTable = true;
836 else
838 Reference< XTableRows > xRows( mxTable->getRows() );
839 xRows->removeByIndex( aStart.mnRow, nRemovedRows );
841 break;
845 if( bDeleteTable )
846 mrView.DeleteMarkedObj();
847 else
848 UpdateTableShape();
853 void SvxTableController::onSelect( sal_uInt16 nSId )
855 if( mxTable.is() )
857 const sal_Int32 nRowCount = mxTable->getRowCount();
858 const sal_Int32 nColCount = mxTable->getColumnCount();
859 if( nRowCount && nColCount )
861 CellPos aStart, aEnd;
862 getSelectedCells( aStart, aEnd );
864 switch( nSId )
866 case SID_TABLE_SELECT_ALL:
867 aEnd.mnCol = 0; aEnd.mnRow = 0;
868 aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
869 break;
870 case SID_TABLE_SELECT_COL:
871 aEnd.mnRow = nRowCount - 1;
872 aStart.mnRow = 0;
873 break;
874 case SID_TABLE_SELECT_ROW:
875 aEnd.mnCol = nColCount - 1;
876 aStart.mnCol = 0;
877 break;
880 StartSelection( aEnd );
881 gotoCell( aStart, true, nullptr );
886 namespace
888 SvxBoxItem mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet& rAttrSet)
890 // merge drawing layer text distance items into SvxBoxItem used by the dialog
891 SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
892 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
893 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
894 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
895 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
896 return aBoxItem;
900 void SvxTableController::onFormatTable(const SfxRequest& rReq)
902 if(!mxTableObj.is())
903 return;
905 SdrTableObj& rTableObj(*mxTableObj.get());
906 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
907 const SfxItemSet* pArgs = rReq.GetArgs();
909 if(!pArgs)
911 SfxItemSet aNewAttr(rModel.GetItemPool());
913 // merge drawing layer text distance items into SvxBoxItem used by the dialog
914 SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
916 SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
918 MergeAttrFromSelectedCells(aNewAttr, false);
919 FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
920 aNewAttr.Put( aBoxItem );
921 aNewAttr.Put( aBoxInfoItem );
923 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
924 std::shared_ptr<AbstractTabController> xDlg( pFact->CreateSvxFormatCellsDialog(
925 rReq.GetFrameWeld(),
926 &aNewAttr,
927 rModel) );
929 SvxTableController* pThis = this;
930 VclAbstractDialog::AsyncContext aContext;
931 aContext.maEndDialogFn = [xDlg, pThis, aBoxItem, aBoxInfoItem](int nResult){
932 if (nResult == RET_OK)
934 SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
936 //Only properties that were unchanged by the dialog appear in this
937 //itemset. We had constructed these two properties from other
938 //ones, so if they were not changed, then forcible set them back to
939 //their originals in the new result set so we can decompose that
940 //unchanged state back to their input properties
941 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
943 aNewSet.Put(aBoxItem);
945 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
947 aNewSet.Put(aBoxInfoItem);
950 SvxBoxItem aNewBoxItem( aNewSet.Get( SDRATTR_TABLE_BORDER ) );
952 if( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) != aBoxItem.GetDistance( SvxBoxItemLine::LEFT ) )
953 aNewSet.Put(makeSdrTextLeftDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) ) );
955 if( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) != aBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) )
956 aNewSet.Put(makeSdrTextRightDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) ) );
958 if( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) != aBoxItem.GetDistance( SvxBoxItemLine::TOP ) )
959 aNewSet.Put(makeSdrTextUpperDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) ) );
961 if( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) != aBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
962 aNewSet.Put(makeSdrTextLowerDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) ) );
964 pThis->SetAttrToSelectedCells(aNewSet, false);
968 xDlg->StartExecuteAsync(aContext);
972 void SvxTableController::Execute( SfxRequest& rReq )
974 const sal_uInt16 nSId = rReq.GetSlot();
975 switch( nSId )
977 case SID_TABLE_INSERT_ROW:
978 case SID_TABLE_INSERT_COL:
979 onInsert( nSId, rReq.GetArgs() );
980 break;
981 case SID_TABLE_DELETE_ROW:
982 case SID_TABLE_DELETE_COL:
983 case SID_TABLE_DELETE_TABLE:
984 onDelete( nSId );
985 break;
986 case SID_TABLE_SELECT_ALL:
987 case SID_TABLE_SELECT_COL:
988 case SID_TABLE_SELECT_ROW:
989 onSelect( nSId );
990 break;
991 case SID_FORMAT_TABLE_DLG:
992 onFormatTable( rReq );
993 break;
995 case SID_FRAME_LINESTYLE:
996 case SID_FRAME_LINECOLOR:
997 case SID_ATTR_BORDER:
999 const SfxItemSet* pArgs = rReq.GetArgs();
1000 if( pArgs )
1001 ApplyBorderAttr( *pArgs );
1003 break;
1005 case SID_ATTR_FILL_STYLE:
1007 const SfxItemSet* pArgs = rReq.GetArgs();
1008 if( pArgs )
1009 SetAttributes( *pArgs, false );
1011 break;
1013 case SID_TABLE_MERGE_CELLS:
1014 MergeMarkedCells();
1015 break;
1017 case SID_TABLE_SPLIT_CELLS:
1018 SplitMarkedCells(rReq);
1019 break;
1021 case SID_TABLE_MINIMAL_COLUMN_WIDTH:
1022 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
1023 break;
1025 case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
1026 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
1027 break;
1029 case SID_TABLE_DISTRIBUTE_COLUMNS:
1030 DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
1031 break;
1033 case SID_TABLE_MINIMAL_ROW_HEIGHT:
1034 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
1035 break;
1037 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
1038 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
1039 break;
1041 case SID_TABLE_DISTRIBUTE_ROWS:
1042 DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
1043 break;
1045 case SID_TABLE_VERT_BOTTOM:
1046 case SID_TABLE_VERT_CENTER:
1047 case SID_TABLE_VERT_NONE:
1048 SetVertical( nSId );
1049 break;
1051 case SID_AUTOFORMAT:
1052 case SID_TABLE_SORT_DIALOG:
1053 case SID_TABLE_AUTOSUM:
1054 default:
1055 break;
1057 case SID_TABLE_STYLE:
1058 SetTableStyle( rReq.GetArgs() );
1059 break;
1061 case SID_TABLE_STYLE_SETTINGS:
1062 SetTableStyleSettings( rReq.GetArgs() );
1063 break;
1064 case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
1065 changeTableEdge(rReq);
1066 break;
1070 void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
1072 if(!checkTableObject())
1073 return;
1075 SdrTableObj& rTableObj(*mxTableObj.get());
1076 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1078 if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
1079 return;
1081 const SfxStringItem* pArg = dynamic_cast< const SfxStringItem* >( &pArgs->Get( SID_TABLE_STYLE ) );
1082 if( pArg && mxTable.is() ) try
1084 Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
1085 Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
1086 const OUString sFamilyName( "table" );
1087 Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( sFamilyName ), UNO_QUERY_THROW );
1089 if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
1091 // found table style with the same name
1092 Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
1094 const bool bUndo = rModel.IsUndoEnabled();
1096 if( bUndo )
1098 rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
1099 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1102 rTableObj.setTableStyle( xNewTableStyle );
1104 const sal_Int32 nRowCount = mxTable->getRowCount();
1105 const sal_Int32 nColCount = mxTable->getColumnCount();
1106 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1108 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
1110 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1111 if( xCell.is() )
1113 SfxItemSet aSet( xCell->GetItemSet() );
1114 bool bChanges = false;
1115 SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
1116 SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
1117 if (pStyleSheet)
1119 const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
1121 for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
1123 if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
1125 aSet.ClearItem( nWhich );
1126 bChanges = true;
1131 if( bChanges )
1133 if( bUndo )
1134 xCell->AddUndo();
1136 xCell->SetMergedItemSetAndBroadcast( aSet, true );
1140 catch( Exception& )
1142 OSL_FAIL( "svx::SvxTableController::SetTableStyle(), exception caught!" );
1146 if( bUndo )
1147 rModel.EndUndo();
1150 catch( Exception& )
1152 OSL_FAIL( "svx::SvxTableController::SetTableStyle(), exception caught!" );
1156 void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
1158 if(!checkTableObject())
1159 return;
1161 SdrTableObj& rTableObj(*mxTableObj.get());
1162 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1164 TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
1165 const SfxPoolItem *pPoolItem=nullptr;
1167 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTROWSTYLE, false,&pPoolItem) )
1168 aSettings.mbUseFirstRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1170 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTROWSTYLE, false,&pPoolItem) )
1171 aSettings.mbUseLastRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1173 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGROWSTYLE, false,&pPoolItem) )
1174 aSettings.mbUseRowBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1176 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTCOLUMNSTYLE, false,&pPoolItem) )
1177 aSettings.mbUseFirstColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1179 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTCOLUMNSTYLE, false,&pPoolItem) )
1180 aSettings.mbUseLastColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1182 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGCOLUMNSTYLE, false,&pPoolItem) )
1183 aSettings.mbUseColumnBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1185 if( aSettings == rTableObj.getTableStyleSettings() )
1186 return;
1188 const bool bUndo(rModel.IsUndoEnabled());
1190 if( bUndo )
1192 rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
1193 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1196 rTableObj.setTableStyleSettings( aSettings );
1198 if( bUndo )
1199 rModel.EndUndo();
1202 void SvxTableController::SetVertical( sal_uInt16 nSId )
1204 if(!checkTableObject())
1205 return;
1207 SdrTableObj& rTableObj(*mxTableObj.get());
1208 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1210 TableModelNotifyGuard aGuard( mxTable.get() );
1211 const bool bUndo(rModel.IsUndoEnabled());
1213 if (bUndo)
1215 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
1216 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
1219 CellPos aStart, aEnd;
1220 getSelectedCells( aStart, aEnd );
1222 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
1224 switch( nSId )
1226 case SID_TABLE_VERT_BOTTOM:
1227 eAdj = SDRTEXTVERTADJUST_BOTTOM;
1228 break;
1229 case SID_TABLE_VERT_CENTER:
1230 eAdj = SDRTEXTVERTADJUST_CENTER;
1231 break;
1232 //case SID_TABLE_VERT_NONE:
1233 default:
1234 break;
1237 SdrTextVertAdjustItem aItem( eAdj );
1239 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1241 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1243 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1244 if( xCell.is() )
1246 if (bUndo)
1247 xCell->AddUndo();
1248 SfxItemSet aSet(xCell->GetItemSet());
1249 aSet.Put(aItem);
1250 xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
1255 UpdateTableShape();
1257 if (bUndo)
1258 rModel.EndUndo();
1261 void SvxTableController::MergeMarkedCells()
1263 CellPos aStart, aEnd;
1264 getSelectedCells( aStart, aEnd );
1265 SdrTableObj* pTableObj = mxTableObj.get();
1266 if( pTableObj )
1268 if( pTableObj->IsTextEditActive() )
1269 mrView.SdrEndTextEdit(true);
1271 TableModelNotifyGuard aGuard( mxTable.get() );
1272 MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
1276 void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
1278 if(!checkTableObject() || !mxTable.is())
1279 return;
1281 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1282 VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
1284 xDlg->StartExecuteAsync([xDlg, this](int) {
1285 const sal_Int32 nCount = xDlg->GetCount() - 1;
1287 if( nCount < 1 )
1288 return;
1290 CellPos aStart, aEnd;
1291 getSelectedCells( aStart, aEnd );
1292 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
1293 const sal_Int32 nRowCount = mxTable->getRowCount();
1294 const sal_Int32 nColCount = mxTable->getColumnCount();
1295 SdrTableObj& rTableObj(*mxTableObj.get());
1297 if( rTableObj.IsTextEditActive() )
1298 mrView.SdrEndTextEdit(true);
1300 TableModelNotifyGuard aGuard( mxTable.get() );
1301 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1302 const bool bUndo(rModel.IsUndoEnabled());
1304 if( bUndo )
1306 rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
1307 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1310 if( xDlg->IsHorizontal() )
1312 xRange->split( 0, nCount );
1314 else
1316 xRange->split( nCount, 0 );
1319 if( bUndo )
1320 rModel.EndUndo();
1322 aEnd.mnRow += mxTable->getRowCount() - nRowCount;
1323 aEnd.mnCol += mxTable->getColumnCount() - nColCount;
1325 setSelectedCells( aStart, aEnd );
1327 xDlg->disposeOnce();
1331 void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
1333 if(!checkTableObject())
1334 return;
1336 SdrTableObj& rTableObj(*mxTableObj.get());
1337 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1338 const bool bUndo(rModel.IsUndoEnabled());
1340 if( bUndo )
1342 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
1343 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1346 CellPos aStart, aEnd;
1347 getSelectedCells( aStart, aEnd );
1348 rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
1350 if( bUndo )
1351 rModel.EndUndo();
1354 void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
1356 if(!checkTableObject())
1357 return;
1359 SdrTableObj& rTableObj(*mxTableObj.get());
1360 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1361 const bool bUndo(rModel.IsUndoEnabled());
1363 if( bUndo )
1365 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
1366 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1369 CellPos aStart, aEnd;
1370 getSelectedCells( aStart, aEnd );
1371 rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
1373 if( bUndo )
1374 rModel.EndUndo();
1377 bool SvxTableController::HasMarked() const
1379 return mbCellSelectionMode && mxTable.is();
1382 bool SvxTableController::DeleteMarked()
1384 if(!checkTableObject() || !HasMarked())
1385 return false;
1387 SdrTableObj& rTableObj(*mxTableObj.get());
1388 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1389 const bool bUndo(rModel.IsUndoEnabled());
1391 if (bUndo)
1392 rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
1394 CellPos aStart, aEnd;
1395 getSelectedCells( aStart, aEnd );
1396 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1398 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1400 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1401 if (xCell.is() && xCell->hasText())
1403 if (bUndo)
1404 xCell->AddUndo();
1405 xCell->SetOutlinerParaObject(nullptr);
1410 if (bUndo)
1411 rModel.EndUndo();
1413 UpdateTableShape();
1414 return true;
1417 bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
1419 if( hasSelectedCells() )
1421 rpStyleSheet = nullptr;
1423 if( mxTable.is() )
1425 SfxStyleSheet* pRet=nullptr;
1426 bool b1st=true;
1428 CellPos aStart, aEnd;
1429 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
1431 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1433 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1435 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1436 if( xCell.is() )
1438 SfxStyleSheet* pSS=xCell->GetStyleSheet();
1439 if(b1st)
1441 pRet=pSS;
1443 else if(pRet != pSS)
1445 return true;
1447 b1st=false;
1451 rpStyleSheet = pRet;
1452 return true;
1455 return false;
1458 bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
1460 if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
1462 if( mxTable.is() )
1464 CellPos aStart, aEnd;
1465 getSelectedCells( aStart, aEnd );
1467 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1469 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1471 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1472 if( xCell.is() )
1473 xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
1477 UpdateTableShape();
1478 return true;
1481 return false;
1484 void SvxTableController::changeTableEdge(const SfxRequest& rReq)
1486 if (!checkTableObject())
1487 return;
1489 const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
1490 const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
1491 const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
1493 if (pType && pIndex && pOffset)
1495 const OUString sType = pType->GetValue();
1496 const sal_uInt16 nIndex = pIndex->GetValue();
1497 const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
1499 SdrTableObj& rTableObj(*mxTableObj.get());
1501 sal_Int32 nEdgeIndex = -1;
1502 bool bHorizontal = sType.startsWith("row");
1504 if (sType == "column-left" || sType == "row-left")
1506 nEdgeIndex = 0;
1508 else if (sType == "column-right")
1510 // Number of edges = number of columns + 1
1511 nEdgeIndex = rTableObj.getColumnCount();
1513 else if (sType == "row-right")
1515 // Number of edges = number of rows + 1
1516 nEdgeIndex = rTableObj.getRowCount();
1518 else if (sType == "column-middle" || sType == "row-middle")
1520 nEdgeIndex = nIndex + 1;
1523 if (nEdgeIndex >= 0)
1525 TableModelNotifyGuard aGuard(mxTable.get());
1526 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1527 const bool bUndo(rModel.IsUndoEnabled());
1528 if (bUndo)
1530 auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
1531 rModel.BegUndo(pUndoObject->GetComment());
1533 auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
1534 if (pGeoUndo)
1535 pGeoUndo->SetSkipChangeLayout(true);
1536 rModel.AddUndo(std::move(pUndoObject));
1538 tools::Rectangle aBoundRect;
1539 if (rTableObj.GetUserCall())
1540 aBoundRect = rTableObj.GetLastBoundRect();
1541 rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
1542 rTableObj.SetChanged();
1543 rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
1544 if (bUndo)
1545 rModel.EndUndo();
1550 // internals
1553 bool SvxTableController::checkTableObject()
1555 return mxTableObj.is();
1559 SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
1561 const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
1562 const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
1563 const bool bTextEdit = mrView.IsTextEdit();
1565 TblAction nAction = TblAction::HandledByView;
1567 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
1568 if( !pTableObj )
1569 return nAction;
1571 // handle special keys
1572 const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
1573 switch( nCode )
1575 case awt::Key::ESCAPE: // handle escape
1577 if( bTextEdit )
1579 // escape during text edit ends text edit
1580 nAction = TblAction::StopTextEdit;
1582 if( mbCellSelectionMode )
1584 // escape with selected cells removes selection
1585 nAction = TblAction::RemoveSelection;
1587 break;
1589 case awt::Key::RETURN: // handle return
1591 if( !bMod1 && !bMod2 && !bTextEdit )
1593 // when not already editing, return starts text edit
1594 setSelectionStart( SdrTableObj::getFirstCell() );
1595 nAction = TblAction::EditCell;
1597 break;
1599 case awt::Key::F2: // f2 toggles text edit
1601 if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
1604 else if( bTextEdit )
1606 // f2 during text edit stops text edit
1607 nAction = TblAction::StopTextEdit;
1609 else if( mbCellSelectionMode )
1611 // f2 with selected cells removes selection
1612 nAction = TblAction::RemoveSelection;
1614 else
1616 // f2 with no selection and no text edit starts text edit
1617 setSelectionStart( SdrTableObj::getFirstCell() );
1618 nAction = TblAction::EditCell;
1620 break;
1622 case awt::Key::HOME:
1623 case awt::Key::NUM7:
1625 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1627 if( bMod1 && !bMod2 )
1629 // ctrl + home jumps to first cell
1630 nAction = TblAction::GotoFirstCell;
1632 else if( !bMod1 && bMod2 )
1634 // alt + home jumps to first column
1635 nAction = TblAction::GotoFirstColumn;
1638 break;
1640 case awt::Key::END:
1641 case awt::Key::NUM1:
1643 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1645 if( bMod1 && !bMod2 )
1647 // ctrl + end jumps to last cell
1648 nAction = TblAction::GotoLastCell;
1650 else if( !bMod1 && bMod2 )
1652 // alt + home jumps to last column
1653 nAction = TblAction::GotoLastColumn;
1656 break;
1659 case awt::Key::TAB:
1661 if( bTextEdit || mbCellSelectionMode )
1662 nAction = TblAction::Tab;
1663 break;
1666 case awt::Key::UP:
1667 case awt::Key::NUM8:
1668 case awt::Key::DOWN:
1669 case awt::Key::NUM2:
1670 case awt::Key::LEFT:
1671 case awt::Key::NUM4:
1672 case awt::Key::RIGHT:
1673 case awt::Key::NUM6:
1676 if( !bMod1 && bMod2 )
1678 if(bTextEdit || mbCellSelectionMode)
1680 if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
1682 nAction = TblAction::GotoLeftCell;
1683 break;
1685 else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
1687 nAction = TblAction::GotoRightCell;
1688 break;
1693 bool bTextMove = false;
1694 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
1695 if( pOLV )
1697 RemoveSelection();
1698 // during text edit, check if we navigate out of the cell
1699 ESelection aOldSelection = pOLV->GetSelection();
1700 pOLV->PostKeyEvent(rKEvt);
1701 bTextMove = aOldSelection == pOLV->GetSelection();
1702 if( !bTextMove )
1704 nAction = TblAction::NONE;
1708 if( mbCellSelectionMode || bTextMove )
1710 // no text edit, navigate in cells if selection active
1711 switch( nCode )
1713 case awt::Key::LEFT:
1714 case awt::Key::NUM4:
1715 nAction = TblAction::GotoLeftCell;
1716 break;
1717 case awt::Key::RIGHT:
1718 case awt::Key::NUM6:
1719 nAction = TblAction::GotoRightCell;
1720 break;
1721 case awt::Key::DOWN:
1722 case awt::Key::NUM2:
1723 nAction = TblAction::GotoDownCell;
1724 break;
1725 case awt::Key::UP:
1726 case awt::Key::NUM8:
1727 nAction = TblAction::GotoUpCell;
1728 break;
1731 break;
1733 case awt::Key::PAGEUP:
1734 if( bMod2 )
1735 nAction = TblAction::GotoFirstRow;
1736 break;
1738 case awt::Key::PAGEDOWN:
1739 if( bMod2 )
1740 nAction = TblAction::GotoLastRow;
1741 break;
1743 return nAction;
1746 bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
1748 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
1749 if( !pTableObj )
1750 return false;
1752 switch( nAction )
1754 case TblAction::GotoFirstCell:
1756 gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
1757 break;
1760 case TblAction::GotoLeftCell:
1762 gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
1763 break;
1766 case TblAction::GotoRightCell:
1768 gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
1769 break;
1772 case TblAction::GotoLastCell:
1774 gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
1775 break;
1778 case TblAction::GotoFirstColumn:
1780 CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
1781 gotoCell( aPos, bSelect, pWindow, nAction );
1782 break;
1785 case TblAction::GotoLastColumn:
1787 CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
1788 gotoCell( aPos, bSelect, pWindow, nAction );
1789 break;
1792 case TblAction::GotoFirstRow:
1794 CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
1795 gotoCell( aPos, bSelect, pWindow, nAction );
1796 break;
1799 case TblAction::GotoUpCell:
1801 gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1802 break;
1805 case TblAction::GotoDownCell:
1807 gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1808 break;
1811 case TblAction::GotoLastRow:
1813 CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
1814 gotoCell( aPos, bSelect, pWindow, nAction );
1815 break;
1818 case TblAction::EditCell:
1819 EditCell( getSelectionStart(), pWindow, nAction );
1820 break;
1822 case TblAction::StopTextEdit:
1823 StopTextEdit();
1824 break;
1826 case TblAction::RemoveSelection:
1827 RemoveSelection();
1828 break;
1830 case TblAction::Tab:
1832 if( bSelect )
1833 gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
1834 else
1836 CellPos aSelectionEnd( getSelectionEnd() );
1837 CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
1838 if( aSelectionEnd == aNextCell )
1840 onInsert( SID_TABLE_INSERT_ROW );
1841 aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
1843 gotoCell( aNextCell, false, pWindow, nAction );
1845 break;
1847 default:
1848 break;
1851 return nAction != TblAction::HandledByView;
1855 void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
1857 if( mxTableObj.is() && mxTableObj->IsTextEditActive() )
1858 mrView.SdrEndTextEdit(true);
1860 if( bSelect )
1862 maCursorLastPos = rPos;
1863 if( mxTableObj.is() )
1864 mxTableObj->setActiveCell( rPos );
1866 if( !mbCellSelectionMode )
1868 setSelectedCells( maCursorFirstPos, rPos );
1870 else
1872 UpdateSelection( rPos );
1875 else
1877 RemoveSelection();
1878 EditCell( rPos, pWindow, nAction );
1883 const CellPos& SvxTableController::getSelectionStart()
1885 checkCell( maCursorFirstPos );
1886 return maCursorFirstPos;
1890 void SvxTableController::setSelectionStart( const CellPos& rPos )
1892 maCursorFirstPos = rPos;
1896 const CellPos& SvxTableController::getSelectionEnd()
1898 checkCell( maCursorLastPos );
1899 return maCursorLastPos;
1903 void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
1905 if(!checkTableObject() || !mxTable.is())
1906 return;
1910 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
1912 if( xRange->isMergeable() )
1914 SdrTableObj& rTableObj(*mxTableObj.get());
1915 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1916 const bool bUndo(rModel.IsUndoEnabled());
1918 if( bUndo )
1920 rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
1921 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1924 xRange->merge();
1925 mbHasJustMerged = true;
1926 setSelectedCells( maCursorFirstPos, maCursorFirstPos );
1928 if( bUndo )
1929 rModel.EndUndo();
1932 catch( Exception& )
1934 SAL_WARN( "svx", "sdr::table::SvxTableController::MergeRange(), exception caught!" );
1939 void SvxTableController::checkCell( CellPos& rPos )
1941 if( mxTable.is() ) try
1943 if( rPos.mnCol >= mxTable->getColumnCount() )
1944 rPos.mnCol = mxTable->getColumnCount()-1;
1946 if( rPos.mnRow >= mxTable->getRowCount() )
1947 rPos.mnRow = mxTable->getRowCount()-1;
1949 catch( Exception& )
1951 OSL_FAIL("sdr::table::SvxTableController::checkCell(), exception caught!" );
1956 void SvxTableController::findMergeOrigin( CellPos& rPos )
1958 if( mxTable.is() ) try
1960 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
1961 if( xCell->isMerged() )
1963 ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
1966 catch( Exception& )
1968 OSL_FAIL("sdr::table::SvxTableController::findMergeOrigin(), exception caught!" );
1973 void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
1975 SdrPageView* pPV(mrView.GetSdrPageView());
1977 if(nullptr == pPV || !checkTableObject())
1978 return;
1980 SdrTableObj& rTableObj(*mxTableObj.get());
1982 if(rTableObj.getSdrPageFromSdrObject() == pPV->GetPage())
1984 bool bEmptyOutliner = false;
1986 if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
1988 ::Outliner* pOutl = mrView.GetTextEditOutliner();
1989 sal_Int32 nParaCnt = pOutl->GetParagraphCount();
1990 Paragraph* p1stPara = pOutl->GetParagraph( 0 );
1992 if(nParaCnt==1 && p1stPara)
1994 // with only one paragraph
1995 if (pOutl->GetText(p1stPara).isEmpty())
1997 bEmptyOutliner = true;
2002 CellPos aPos( rPos );
2003 findMergeOrigin( aPos );
2005 if( &rTableObj != mrView.GetTextEditObject() || bEmptyOutliner || !rTableObj.IsTextEditActive( aPos ) )
2007 if( rTableObj.IsTextEditActive() )
2008 mrView.SdrEndTextEdit(true);
2010 rTableObj.setActiveCell( aPos );
2012 // create new outliner, owner will be the SdrObjEditView
2013 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2014 std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
2016 if (pOutl && rTableObj.IsVerticalWriting())
2017 pOutl->SetVertical( true );
2019 if (mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
2021 maCursorLastPos = maCursorFirstPos = rPos;
2023 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
2025 // Move cursor to end of text
2026 ESelection aNewSelection;
2028 const WritingMode eMode = rTableObj.GetWritingMode();
2029 if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
2031 const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
2032 ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
2034 if( bLast )
2035 aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
2037 pOLV->SetSelection(aNewSelection);
2044 void SvxTableController::StopTextEdit()
2046 if(mrView.IsTextEdit())
2048 mrView.SdrEndTextEdit();
2049 mrView.SetCurrentObj(OBJ_TABLE);
2050 mrView.SetEditMode(SdrViewEditMode::Edit);
2055 void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
2057 if( mbCellSelectionMode )
2059 checkCell( maCursorFirstPos );
2060 checkCell( maCursorLastPos );
2062 rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2063 rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2064 rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2065 rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2067 bool bExt = false;
2068 if( mxTable.is() ) do
2070 bExt = false;
2071 for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
2073 for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
2075 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
2076 if( !xCell.is() )
2077 continue;
2079 if( xCell->isMerged() )
2081 CellPos aPos( nCol, nRow );
2082 findMergeOrigin( aPos );
2083 if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
2085 rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
2086 rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
2087 bExt = true;
2090 else
2092 if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
2094 rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
2095 rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
2096 bExt = true;
2102 while(bExt);
2104 else if(mrView.IsTextEdit())
2106 rFirst = getSelectionStart();
2107 findMergeOrigin( rFirst );
2108 rLast = rFirst;
2110 if( mxTable.is() )
2112 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
2113 if( xCell.is() )
2115 rLast.mnCol += xCell->getColumnSpan() - 1;
2116 rLast.mnRow += xCell->getRowSpan() - 1;
2120 else
2122 rFirst.mnCol = 0;
2123 rFirst.mnRow = 0;
2124 if( mxTable.is() )
2126 rLast.mnRow = mxTable->getRowCount()-1;
2127 rLast.mnCol = mxTable->getColumnCount()-1;
2129 else
2131 rLast.mnRow = 0;
2132 rLast.mnCol = 0;
2138 void SvxTableController::StartSelection( const CellPos& rPos )
2140 StopTextEdit();
2141 mbCellSelectionMode = true;
2142 maCursorLastPos = maCursorFirstPos = rPos;
2143 mrView.MarkListHasChanged();
2147 void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
2149 StopTextEdit();
2150 mbCellSelectionMode = true;
2151 maCursorFirstPos = rStart;
2152 UpdateSelection( rEnd );
2156 bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
2158 if(!checkTableObject() || !mxTable.is())
2159 return false;
2161 SdrTableObj& rTableObj(*mxTableObj.get());
2162 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2164 if (mrView.IsTextEdit())
2165 return true;
2167 CellPos aStart, aEnd;
2169 if(hasSelectedCells())
2171 getSelectedCells(aStart, aEnd);
2173 else
2175 aStart.mnRow = 0;
2176 aStart.mnCol = 0;
2177 aEnd.mnRow = mxTable->getRowCount() - 1;
2178 aEnd.mnCol = mxTable->getColumnCount() - 1;
2181 for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
2183 for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
2185 CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
2186 if (xCell.is())
2188 if (rModel.IsUndoEnabled())
2189 xCell->AddUndo();
2191 SfxItemSet aCellSet(xCell->GetItemSet());
2192 if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
2194 xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
2200 UpdateTableShape();
2202 return true;
2206 void SvxTableController::UpdateSelection( const CellPos& rPos )
2208 maCursorLastPos = rPos;
2209 mrView.MarkListHasChanged();
2213 void SvxTableController::clearSelection()
2215 RemoveSelection();
2219 void SvxTableController::selectAll()
2221 if( mxTable.is() )
2223 CellPos aPos1, aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
2224 if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
2226 setSelectedCells( aPos1, aPos2 );
2232 void SvxTableController::RemoveSelection()
2234 if( mbCellSelectionMode )
2236 mbCellSelectionMode = false;
2237 mrView.MarkListHasChanged();
2242 void SvxTableController::onTableModified()
2244 if( mnUpdateEvent == nullptr )
2245 mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
2249 void SvxTableController::updateSelectionOverlay()
2251 // There is no need to update selection overlay after merging cells
2252 // since the selection overlay should remain the same
2253 if ( mbHasJustMerged )
2254 return;
2256 destroySelectionOverlay();
2257 if( mbCellSelectionMode )
2259 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
2260 if( pTableObj )
2262 sdr::overlay::OverlayObjectCell::RangeVector aRanges;
2264 tools::Rectangle aStartRect, aEndRect;
2265 CellPos aStart,aEnd;
2266 getSelectedCells( aStart, aEnd );
2267 pTableObj->getCellBounds( aStart, aStartRect );
2269 basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
2270 a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
2272 findMergeOrigin( aEnd );
2273 pTableObj->getCellBounds( aEnd, aEndRect );
2274 a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
2275 a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
2276 aRanges.push_back( a2DRange );
2278 ::Color aHighlight( COL_BLUE );
2279 OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
2280 if( pOutDev )
2281 aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
2283 const sal_uInt32 nCount = mrView.PaintWindowCount();
2284 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
2286 SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
2287 if( pPaintWindow )
2289 const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
2290 if( xOverlayManager.is() )
2292 std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, aRanges ));
2294 xOverlayManager->add(*pOverlay);
2295 mpSelectionOverlay.reset(new sdr::overlay::OverlayObjectList);
2296 mpSelectionOverlay->append(std::move(pOverlay));
2301 // If tiled rendering, emit callbacks for sdr table selection.
2302 if (pOutDev && comphelper::LibreOfficeKit::isActive())
2304 tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
2306 if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2307 aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
2309 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2311 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString().getStr());
2312 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString().getStr());
2320 void SvxTableController::destroySelectionOverlay()
2322 if( mpSelectionOverlay )
2324 mpSelectionOverlay.reset();
2326 if (comphelper::LibreOfficeKit::isActive())
2328 // Clear the LOK text selection so far provided by this table.
2329 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2331 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY");
2332 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
2333 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
2334 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
2341 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
2343 if( mxTable.is() )
2345 CellPos aStart, aEnd;
2346 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
2348 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2350 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2352 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2353 if( xCell.is() && !xCell->isMerged() )
2355 const SfxItemSet& rSet = xCell->GetItemSet();
2356 SfxWhichIter aIter(rSet);
2357 sal_uInt16 nWhich(aIter.FirstWhich());
2358 while(nWhich)
2360 if(!bOnlyHardAttr)
2362 if(SfxItemState::DONTCARE == rSet.GetItemState(nWhich, false))
2363 rAttr.InvalidateItem(nWhich);
2364 else
2365 rAttr.MergeValue(rSet.Get(nWhich), true);
2367 else if(SfxItemState::SET == rSet.GetItemState(nWhich, false))
2369 const SfxPoolItem& rItem = rSet.Get(nWhich);
2370 rAttr.MergeValue(rItem, true);
2373 nWhich = aIter.NextWhich();
2382 static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
2384 if( pNew )
2386 const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
2387 if( pOld )
2389 SvxBorderLine aNewLine( *pNew );
2390 aNewLine.SetColor( pOld->GetColor() );
2391 rNewFrame.SetLine( &aNewLine, nLine );
2392 return;
2395 rNewFrame.SetLine( pNew, nLine );
2399 static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
2401 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2403 // current cell is outside the selection
2405 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2407 if (nCellPosFlags & CellPosFlag::Upper)
2409 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
2410 rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
2412 else if (nCellPosFlags & CellPosFlag::Lower)
2414 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
2415 rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
2418 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2420 if (nCellPosFlags & CellPosFlag::Before)
2422 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
2423 rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
2425 else if (nCellPosFlags & CellPosFlag::After)
2427 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
2428 rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
2432 else
2434 // current cell is inside the selection
2436 if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
2437 : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
2438 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
2440 if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2441 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
2443 if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2444 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
2446 if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2447 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
2449 // apply distance to borders
2450 if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
2451 for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
2452 rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
2457 static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
2459 const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
2460 if( pSourceLine )
2462 SvxBorderLine aLine( *pSourceLine );
2463 aLine.SetColor( rColor );
2464 rNewFrame.SetLine( &aLine, nLine );
2469 static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
2471 const Color aColor( pLineColorItem->GetValue() );
2473 if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
2474 ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
2476 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
2477 ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
2479 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
2480 ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
2482 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
2483 ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
2487 static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
2489 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2491 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2493 if (nCellPosFlags & CellPosFlag::Upper)
2495 if( rNewFrame.GetBottom() )
2496 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2498 else if (nCellPosFlags & CellPosFlag::Lower)
2500 if( rNewFrame.GetTop() )
2501 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2504 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2506 if (nCellPosFlags & CellPosFlag::Before)
2508 if( rNewFrame.GetRight() )
2509 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2511 else if (nCellPosFlags & CellPosFlag::After)
2513 if( rNewFrame.GetLeft() )
2514 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2518 else
2520 if( rNewFrame.GetBottom() )
2521 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2522 if( rNewFrame.GetTop() )
2523 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2524 if( rNewFrame.GetRight() )
2525 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2526 if( rNewFrame.GetLeft() )
2527 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2532 void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
2534 if( mxTable.is() )
2536 const sal_Int32 nRowCount = mxTable->getRowCount();
2537 const sal_Int32 nColCount = mxTable->getColumnCount();
2538 if( nRowCount && nColCount )
2540 const SvxBoxItem* pBoxItem = nullptr;
2541 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
2542 pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
2544 const SvxBoxInfoItem* pBoxInfoItem = nullptr;
2545 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
2546 pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
2548 const SvxColorItem* pLineColorItem = nullptr;
2549 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
2550 pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
2552 const SvxBorderLine* pBorderLineItem = nullptr;
2553 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
2554 pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
2556 if( pBoxInfoItem && !pBoxItem )
2558 const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
2559 pBoxItem = &gaEmptyBoxItem;
2561 else if( pBoxItem && !pBoxInfoItem )
2563 const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
2564 pBoxInfoItem = &gaEmptyBoxInfoItem;
2567 CellPos aStart, aEnd;
2568 getSelectedCells( aStart, aEnd );
2570 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
2571 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
2573 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
2575 CellPosFlag nRowFlags = CellPosFlag::NONE;
2576 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
2577 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
2578 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
2579 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
2581 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
2583 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2584 if( !xCell.is() )
2585 continue;
2587 const SfxItemSet& rSet = xCell->GetItemSet();
2588 const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
2590 SvxBoxItem aNewFrame( *pOldOuter );
2592 CellPosFlag nCellPosFlags = nRowFlags;
2593 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
2594 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
2595 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
2596 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
2598 if( pBoxItem && pBoxInfoItem )
2599 ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
2601 if( pLineColorItem )
2602 ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
2604 if( pBorderLineItem )
2605 ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
2607 if (aNewFrame != *pOldOuter)
2609 SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
2610 aAttr.Put(aNewFrame);
2611 xCell->SetMergedItemSetAndBroadcast( aAttr, false );
2620 void SvxTableController::UpdateTableShape()
2622 SdrObject* pTableObj = mxTableObj.get();
2623 if( pTableObj )
2625 pTableObj->ActionChanged();
2626 pTableObj->BroadcastObjectChange();
2628 updateSelectionOverlay();
2632 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
2634 if(!checkTableObject() || !mxTable.is())
2635 return;
2637 SdrTableObj& rTableObj(*mxTableObj.get());
2638 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2639 const bool bUndo(rModel.IsUndoEnabled());
2641 if( bUndo )
2642 rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
2644 CellPos aStart, aEnd;
2645 getSelectedCells( aStart, aEnd );
2647 SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
2648 aAttr.Put(rAttr);
2650 const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2652 if( bFrame )
2654 aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2655 aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2658 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2660 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2662 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2663 if( xCell.is() )
2665 if( bUndo )
2666 xCell->AddUndo();
2667 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2672 if( bFrame )
2674 ApplyBorderAttr( rAttr );
2677 UpdateTableShape();
2679 if( bUndo )
2680 rModel.EndUndo();
2684 bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2686 if( mxTableObj.is() && hasSelectedCells() )
2688 MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
2690 if( mrView.IsTextEdit() )
2692 OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
2693 if(pTextEditOutlinerView)
2695 // FALSE= consider InvalidItems not as the default, but as "holes"
2696 rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
2700 return true;
2702 else
2704 return false;
2709 bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2711 if( mbCellSelectionMode || mrView.IsTextEdit() )
2713 SetAttrToSelectedCells( rSet, bReplaceAll );
2714 return true;
2716 return false;
2719 SdrObject* SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
2721 SdrTableObj* pRetval(nullptr);
2722 sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
2724 if(nullptr == pCurrentSdrTableObj)
2726 return pRetval;
2729 if(!mxTableObj.is())
2731 return pRetval;
2734 // get selection and create full selection
2735 CellPos aStart, aEnd;
2736 const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
2738 getSelectedCells(aStart, aEnd);
2740 // compare to see if we have a partial selection
2741 if(aStart != aFullStart || aEnd != aFullEnd)
2743 // create full clone
2744 pRetval = pCurrentSdrTableObj->CloneSdrObject(rTargetModel);
2746 // limit SdrObject's TableModel to partial selection
2747 pRetval->CropTableModelToSelection(aStart, aEnd);
2750 return pRetval;
2753 bool SvxTableController::PasteObjModel( const SdrModel& rModel )
2755 if( mxTableObj.is() && (rModel.GetPageCount() >= 1) )
2757 const SdrPage* pPastePage = rModel.GetPage(0);
2758 if( pPastePage && pPastePage->GetObjCount() == 1 )
2760 SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
2761 if( pPasteTableObj )
2763 return PasteObject( pPasteTableObj );
2768 return false;
2772 bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
2774 if( !pPasteTableObj )
2775 return false;
2777 Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
2778 if( !xPasteTable.is() )
2779 return false;
2781 if( !mxTable.is() )
2782 return false;
2784 sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
2785 sal_Int32 nPasteRows = xPasteTable->getRowCount();
2787 CellPos aStart, aEnd;
2788 getSelectedCells( aStart, aEnd );
2790 if( mrView.IsTextEdit() )
2791 mrView.SdrEndTextEdit(true);
2793 sal_Int32 nColumns = mxTable->getColumnCount();
2794 sal_Int32 nRows = mxTable->getRowCount();
2796 const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
2797 if( nMissing > 0 )
2799 Reference< XTableRows > xRows( mxTable->getRows() );
2800 xRows->insertByIndex( nRows, nMissing );
2801 nRows = mxTable->getRowCount();
2804 nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
2805 nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
2807 // copy cell contents
2808 for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
2810 for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
2812 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
2813 if( xTargetCell.is() && !xTargetCell->isMerged() )
2815 CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
2816 if (xSourceCell.is())
2818 xTargetCell->AddUndo();
2819 // Prevent cell span exceeding the pasting range.
2820 if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
2821 xTargetCell->replaceContentAndFormating(xSourceCell);
2822 else
2823 xTargetCell->cloneFrom(xSourceCell);
2825 nCol += xSourceCell->getColumnSpan() - 1;
2826 nTargetCol += xTargetCell->getColumnSpan();
2832 UpdateTableShape();
2834 return true;
2837 bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
2839 if(!mbCellSelectionMode)
2841 return false;
2844 if(!checkTableObject())
2845 return false;
2847 SdrTableObj& rTableObj(*mxTableObj.get());
2848 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2849 const bool bUndo(rModel.IsUndoEnabled());
2851 if( bUndo )
2852 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
2854 CellPos aStart, aEnd;
2855 getSelectedCells( aStart, aEnd );
2856 const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2858 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2860 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2862 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2863 if( xCell.is() )
2865 if (bUndo)
2866 xCell->AddUndo();
2867 SdrText* pText = xCell.get();
2868 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
2873 if( bFrame )
2875 ApplyBorderAttr( rFormatSet );
2878 UpdateTableShape();
2880 if( bUndo )
2881 rModel.EndUndo();
2883 return true;
2887 IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
2889 mnUpdateEvent = nullptr;
2891 if( mbCellSelectionMode )
2893 CellPos aStart( maCursorFirstPos );
2894 CellPos aEnd( maCursorLastPos );
2895 checkCell(aStart);
2896 checkCell(aEnd);
2897 if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
2899 setSelectedCells( aStart, aEnd );
2903 updateSelectionOverlay();
2904 mbHasJustMerged = false;
2907 namespace
2910 struct LinesState
2912 LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
2913 : rBoxItem(rBoxItem_)
2914 , rBoxInfoItem(rBoxInfoItem_)
2915 , bDistanceIndeterminate(false)
2917 aBorderSet.fill(false);
2918 aInnerLineSet.fill(false);
2919 aBorderIndeterminate.fill(false);
2920 aInnerLineIndeterminate.fill(false);
2921 aDistanceSet.fill(false);
2922 aDistance.fill(0);
2925 SvxBoxItem& rBoxItem;
2926 SvxBoxInfoItem& rBoxInfoItem;
2927 o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
2928 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
2929 o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
2930 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
2931 o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
2932 o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
2933 bool bDistanceIndeterminate;
2936 class BoxItemWrapper
2938 public:
2939 BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
2941 const SvxBorderLine* getLine() const;
2942 void setLine(const SvxBorderLine* pLine);
2944 private:
2945 SvxBoxItem& m_rBoxItem;
2946 SvxBoxInfoItem& m_rBoxInfoItem;
2947 const SvxBoxItemLine m_nBorderLine;
2948 const SvxBoxInfoItemLine m_nInnerLine;
2949 const bool m_bBorder;
2952 BoxItemWrapper::BoxItemWrapper(
2953 SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
2954 const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
2955 : m_rBoxItem(rBoxItem)
2956 , m_rBoxInfoItem(rBoxInfoItem)
2957 , m_nBorderLine(nBorderLine)
2958 , m_nInnerLine(nInnerLine)
2959 , m_bBorder(bBorder)
2963 const SvxBorderLine* BoxItemWrapper::getLine() const
2965 if (m_bBorder)
2966 return m_rBoxItem.GetLine(m_nBorderLine);
2967 else
2968 return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
2971 void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
2973 if (m_bBorder)
2974 m_rBoxItem.SetLine(pLine, m_nBorderLine);
2975 else
2976 m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
2979 void lcl_MergeBorderLine(
2980 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
2981 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
2983 const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
2984 BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
2985 bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
2987 if (rbSet)
2989 bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
2990 if (!rbIndeterminate)
2992 const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
2993 if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
2995 aBoxItem.setLine(nullptr);
2996 rbIndeterminate = true;
3000 else
3002 aBoxItem.setLine(pLine);
3003 rbSet = true;
3007 void lcl_MergeBorderOrInnerLine(
3008 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3009 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
3011 if (bBorder)
3012 lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
3013 else
3015 const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
3016 lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
3020 void lcl_MergeDistance(
3021 LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
3023 if (rLinesState.aDistanceSet[nIndex])
3025 if (!rLinesState.bDistanceIndeterminate)
3026 rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
3028 else
3030 rLinesState.aDistance[nIndex] = nDistance;
3031 rLinesState.aDistanceSet[nIndex] = true;
3035 void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
3037 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
3039 // current cell is outside the selection
3041 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
3043 if (nCellPosFlags & CellPosFlag::Upper)
3044 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
3045 else if (nCellPosFlags & CellPosFlag::Lower)
3046 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
3048 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
3050 if (nCellPosFlags & CellPosFlag::Before)
3051 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
3052 else if (nCellPosFlags & CellPosFlag::After)
3053 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
3056 // NOTE: inner distances for cells outside the selected range
3057 // are not relevant -> we ignore them.
3059 else
3061 // current cell is inside the selection
3063 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
3064 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
3065 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
3066 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
3068 lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
3069 lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
3070 lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
3071 lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
3077 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
3079 if( mxTable.is() )
3081 const sal_Int32 nRowCount = mxTable->getRowCount();
3082 const sal_Int32 nColCount = mxTable->getColumnCount();
3083 if( nRowCount && nColCount )
3085 CellPos aStart, aEnd;
3086 const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
3088 // We are adding one more row/column around the block of selected cells.
3089 // We will be checking the adjoining border of these too.
3090 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
3091 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
3093 rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
3094 LinesState aLinesState( rBoxItem, rBoxInfoItem );
3096 /* Here we go through all the selected cells (enhanced by
3097 * the adjoining row/column on each side) and determine the
3098 * lines for presentation. The algorithm is simple:
3099 * 1. if a border or inner line is set (or unset) in all
3100 * cells to the same value, it will be used.
3101 * 2. if a border or inner line is set only in some cells,
3102 * it will be set to indeterminate state (SetValid() on
3103 * rBoxInfoItem).
3105 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
3107 CellPosFlag nRowFlags = CellPosFlag::NONE;
3108 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
3109 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
3110 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
3111 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
3113 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
3115 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
3116 if( !xCell.is() )
3117 continue;
3119 CellPosFlag nCellPosFlags = nRowFlags;
3120 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
3121 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
3122 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
3123 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
3125 const SfxItemSet& rSet = xCell->GetItemSet();
3126 SvxBoxItem aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
3127 lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
3131 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
3132 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
3133 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
3134 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
3135 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
3136 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
3137 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
3138 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
3139 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
3140 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
3141 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
3142 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
3144 if (!aLinesState.bDistanceIndeterminate)
3146 if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
3147 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
3148 if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
3149 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
3150 if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
3151 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
3152 if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
3153 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
3154 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
3160 bool SvxTableController::selectRow( sal_Int32 row )
3162 if( !mxTable.is() )
3163 return false;
3164 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3165 StartSelection( aEnd );
3166 gotoCell( aStart, true, nullptr );
3167 return true;
3170 bool SvxTableController::selectColumn( sal_Int32 column )
3172 if( !mxTable.is() )
3173 return false;
3174 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3175 StartSelection( aEnd );
3176 gotoCell( aStart, true, nullptr );
3177 return true;
3180 bool SvxTableController::deselectRow( sal_Int32 row )
3182 if( !mxTable.is() )
3183 return false;
3184 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3185 StartSelection( aEnd );
3186 gotoCell( aStart, false, nullptr );
3187 return true;
3190 bool SvxTableController::deselectColumn( sal_Int32 column )
3192 if( !mxTable.is() )
3193 return false;
3194 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3195 StartSelection( aEnd );
3196 gotoCell( aStart, false, nullptr );
3197 return true;
3200 bool SvxTableController::isRowSelected( sal_Int32 nRow )
3202 if( hasSelectedCells() )
3204 CellPos aFirstPos, aLastPos;
3205 getSelectedCells( aFirstPos, aLastPos );
3206 if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
3207 return true;
3209 return false;
3212 bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
3214 if( hasSelectedCells() )
3216 CellPos aFirstPos, aLastPos;
3217 getSelectedCells( aFirstPos, aLastPos );
3218 if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
3219 return true;
3221 return false;
3224 bool SvxTableController::isRowHeader()
3226 if(!checkTableObject())
3227 return false;
3229 SdrTableObj& rTableObj(*mxTableObj.get());
3230 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3232 return aSettings.mbUseFirstRow;
3235 bool SvxTableController::isColumnHeader()
3237 if(!checkTableObject())
3238 return false;
3240 SdrTableObj& rTableObj(*mxTableObj.get());
3241 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3243 return aSettings.mbUseFirstColumn;
3246 bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
3248 if (mxTableObj->GetObjIdentifier() != OBJ_TABLE)
3249 return false;
3251 SdrTableObj* pTableObj = mxTableObj.get();
3252 CellPos aCellPos;
3253 if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
3255 // Position is a table cell.
3256 if (mbCellSelectionMode)
3258 // We have a table selection already: adjust the point or the mark.
3259 if (bPoint)
3260 setSelectedCells(maCursorFirstPos, aCellPos);
3261 else
3262 setSelectedCells(aCellPos, maCursorLastPos);
3263 return true;
3265 else if (aCellPos != maMouseDownPos)
3267 // No selection, but rPosition is at another cell: start table selection.
3268 StartSelection(maMouseDownPos);
3269 // Update graphic selection, should be hidden now.
3270 mrView.AdjustMarkHdl();
3274 return false;
3279 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */