Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / table / tablecontroller.cxx
blobbfb65d5674aadcbe5e6b1cf1000d513861ffb7de
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>
37 #include <vcl/window.hxx>
39 #include <svl/whiter.hxx>
40 #include <svl/stritem.hxx>
42 #include <sfx2/request.hxx>
44 #include <svx/svdotable.hxx>
45 #include <sdr/overlay/overlayobjectcell.hxx>
46 #include <svx/sdr/overlay/overlaymanager.hxx>
47 #include <svx/svxids.hrc>
48 #include <svx/svdoutl.hxx>
49 #include <svx/svdpagv.hxx>
50 #include <svx/svdetc.hxx>
51 #include <svx/selectioncontroller.hxx>
52 #include <svx/svdmodel.hxx>
53 #include <svx/sdrpaintwindow.hxx>
54 #include <svx/svxdlg.hxx>
55 #include <editeng/boxitem.hxx>
56 #include <cell.hxx>
57 #include <editeng/borderline.hxx>
58 #include <editeng/colritem.hxx>
59 #include <editeng/lineitem.hxx>
60 #include <svx/strings.hrc>
61 #include <svx/dialmgr.hxx>
62 #include <svx/svdpage.hxx>
63 #include <svx/sdmetitm.hxx>
64 #include <svx/sdtditm.hxx>
65 #include "tableundo.hxx"
66 #include "tablelayouter.hxx"
67 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
68 #include <memory>
69 #include <o3tl/enumarray.hxx>
70 #include <o3tl/enumrange.hxx>
71 #include <cppuhelper/implbase.hxx>
72 #include <comphelper/lok.hxx>
73 #include <sfx2/viewsh.hxx>
74 #include <editeng/editview.hxx>
75 #include <tools/UnitConversion.hxx>
76 #include <comphelper/diagnose_ex.hxx>
78 using ::editeng::SvxBorderLine;
79 using namespace sdr::table;
80 using namespace ::com::sun::star;
81 using namespace ::com::sun::star::uno;
82 using namespace ::com::sun::star::table;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::container;
85 using namespace ::com::sun::star::text;
86 using namespace ::com::sun::star::style;
88 namespace {
90 enum class CellPosFlag // signals the relative position of a cell to a selection
92 NONE = 0x0000, // not set or inside
93 // row
94 Before = 0x0001,
95 Left = 0x0002,
96 Right = 0x0004,
97 After = 0x0008,
98 // column
99 Upper = 0x0010,
100 Top = 0x0020,
101 Bottom = 0x0040,
102 Lower = 0x0080
107 namespace o3tl
108 { template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
110 namespace sdr::table {
112 namespace {
114 class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
116 public:
117 explicit SvxTableControllerModifyListener( SvxTableController* pController )
118 : mpController( pController ) {}
120 // XModifyListener
121 virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
123 // XEventListener
124 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
126 SvxTableController* mpController;
131 // XModifyListener
134 void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
136 if( mpController )
137 mpController->onTableModified();
141 // XEventListener
144 void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
146 mpController = nullptr;
152 rtl::Reference< sdr::SelectionController > CreateTableController(
153 SdrView& rView,
154 const SdrTableObj& rObj,
155 const rtl::Reference< sdr::SelectionController >& xRefController )
157 return SvxTableController::create(rView, rObj, xRefController);
161 rtl::Reference< sdr::SelectionController > SvxTableController::create(
162 SdrView& rView,
163 const SdrTableObj& rObj,
164 const rtl::Reference< sdr::SelectionController >& xRefController )
166 if( xRefController.is() )
168 SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
170 if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
172 return xRefController;
176 return new SvxTableController(rView, rObj);
180 SvxTableController::SvxTableController(
181 SdrView& rView,
182 const SdrTableObj& rObj)
183 : mbCellSelectionMode(false)
184 ,mbHasJustMerged(false)
185 ,mbLeftButtonDown(false)
186 ,mrView(rView)
187 ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
188 ,mnUpdateEvent( nullptr )
190 rObj.getActiveCellPos( maCursorFirstPos );
191 maCursorLastPos = maCursorFirstPos;
193 Reference< XTable > xTable( mxTableObj.get()->getTable() );
194 if( xTable.is() )
196 mxModifyListener = new SvxTableControllerModifyListener( this );
197 xTable->addModifyListener( mxModifyListener );
199 mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
203 SvxTableController::~SvxTableController()
205 if( mnUpdateEvent )
207 Application::RemoveUserEvent( mnUpdateEvent );
210 if( mxModifyListener.is() && mxTableObj.get() )
212 Reference< XTable > xTable( mxTableObj.get()->getTable() );
213 if( xTable.is() )
215 xTable->removeModifyListener( mxModifyListener );
216 mxModifyListener.clear();
221 bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
223 if(!checkTableObject())
224 return false;
226 SdrTableObj& rTableObj(*mxTableObj.get());
227 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
229 // check if we are read only
230 if( rModel.IsReadOnly())
232 switch( rKEvt.GetKeyCode().GetCode() )
234 case awt::Key::DOWN:
235 case awt::Key::UP:
236 case awt::Key::LEFT:
237 case awt::Key::RIGHT:
238 case awt::Key::TAB:
239 case awt::Key::HOME:
240 case awt::Key::END:
241 case awt::Key::NUM2:
242 case awt::Key::NUM4:
243 case awt::Key::NUM6:
244 case awt::Key::NUM8:
245 case awt::Key::ESCAPE:
246 case awt::Key::F2:
247 break;
248 default:
249 // tell the view we eat the event, no further processing needed
250 return true;
254 TblAction nAction = getKeyboardAction(rKEvt);
256 return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
259 namespace {
261 Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
263 if (!pWindow)
264 return rPoint;
266 return pWindow->PixelToLogic(rPoint);
271 bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
273 if (comphelper::LibreOfficeKit::isActive() && !pWindow)
275 // Tiled rendering: get the window that has the disabled map mode.
276 if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
278 if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
279 pWindow = pOutputDevice->GetOwnerWindow();
283 if( !pWindow || !checkTableObject() )
284 return false;
286 SdrViewEvent aVEvt;
287 if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
288 return false;
290 TableHitKind eHit = mxTableObj.get()->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
292 mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
294 if( eHit == TableHitKind::Cell )
296 StartSelection( maMouseDownPos );
297 return true;
300 if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
301 return true; // right click will become context menu
303 // for cell selection with the mouse remember our first hit
304 if( mbLeftButtonDown )
306 RemoveSelection();
308 SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
310 if( pHdl )
312 mbLeftButtonDown = false;
314 else
316 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
318 if (!pTableObj || eHit == TableHitKind::NONE)
320 mbLeftButtonDown = false;
325 if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
327 bool bEmptyOutliner = false;
328 if (Outliner* pOutliner = mrView.GetTextEditOutliner())
330 if (pOutliner->GetParagraphCount() == 1)
332 if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
333 bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
336 if (bEmptyOutliner)
338 // Tiled rendering: a left double-click in an empty cell: select it.
339 StartSelection(maMouseDownPos);
340 setSelectedCells(maMouseDownPos, maMouseDownPos);
341 // Update graphic selection, should be hidden now.
342 mrView.AdjustMarkHdl();
343 return true;
347 return false;
351 bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
353 if( !checkTableObject() )
354 return false;
356 mbLeftButtonDown = false;
358 return rMEvt.GetClicks() == 2;
362 bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
364 if( !checkTableObject() )
365 return false;
367 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
368 CellPos aPos;
369 if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
371 if(aPos != maMouseDownPos)
373 if( mbCellSelectionMode )
375 setSelectedCells( maMouseDownPos, aPos );
376 return true;
378 else
380 StartSelection( maMouseDownPos );
383 else if( mbCellSelectionMode )
385 UpdateSelection( aPos );
386 return true;
389 return false;
393 void SvxTableController::onSelectionHasChanged()
395 bool bSelected = false;
397 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
398 if( pTableObj && pTableObj->IsTextEditActive() )
400 pTableObj->getActiveCellPos( maCursorFirstPos );
401 maCursorLastPos = maCursorFirstPos;
402 mbCellSelectionMode = false;
404 else
406 const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
407 if( rMarkList.GetMarkCount() == 1 )
408 bSelected = mxTableObj.get().get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
411 if( bSelected )
413 updateSelectionOverlay();
415 else
417 destroySelectionOverlay();
420 void SvxTableController::onSelectAll()
422 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
423 if ( pTableObj && !pTableObj->IsTextEditActive())
425 selectAll();
430 void SvxTableController::GetState( SfxItemSet& rSet )
432 if(!mxTable.is() || !mxTableObj.get().is())
433 return;
435 SdrTableObj& rTableObj(*mxTableObj.get());
436 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
437 std::optional<SfxItemSet> oSet;
438 bool bVertDone(false);
440 // Iterate over all requested items in the set.
441 SfxWhichIter aIter( rSet );
442 sal_uInt16 nWhich = aIter.FirstWhich();
443 while (nWhich)
445 switch (nWhich)
447 case SID_TABLE_VERT_BOTTOM:
448 case SID_TABLE_VERT_CENTER:
449 case SID_TABLE_VERT_NONE:
451 if(!bVertDone)
453 if (!oSet)
455 oSet.emplace(rModel.GetItemPool());
456 MergeAttrFromSelectedCells(*oSet, false);
459 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
461 if (oSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
462 eAdj = oSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
464 rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
465 rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
466 rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
467 bVertDone = true;
469 break;
471 case SID_TABLE_DELETE_ROW:
472 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
473 rSet.DisableItem(SID_TABLE_DELETE_ROW);
474 break;
475 case SID_TABLE_DELETE_COL:
476 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
477 rSet.DisableItem(SID_TABLE_DELETE_COL);
478 break;
479 case SID_TABLE_DELETE_TABLE:
480 if( !mxTable.is() )
481 rSet.DisableItem(SID_TABLE_DELETE_TABLE);
482 break;
483 case SID_TABLE_MERGE_CELLS:
484 if( !mxTable.is() || !hasSelectedCells() )
485 rSet.DisableItem(SID_TABLE_MERGE_CELLS);
486 break;
487 case SID_TABLE_SPLIT_CELLS:
488 if( !hasSelectedCells() || !mxTable.is() )
489 rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
490 break;
492 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
493 case SID_TABLE_DISTRIBUTE_COLUMNS:
494 case SID_TABLE_DISTRIBUTE_ROWS:
496 bool bDistributeColumns = false;
497 bool bDistributeRows = false;
498 if( mxTable.is() )
500 CellPos aStart, aEnd;
501 getSelectedCells( aStart, aEnd );
503 bDistributeColumns = aStart.mnCol != aEnd.mnCol;
504 bDistributeRows = aStart.mnRow != aEnd.mnRow;
506 if( !bDistributeColumns )
507 rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
508 if( !bDistributeRows )
510 rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
511 rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
513 break;
516 default:
517 break;
519 nWhich = aIter.NextWhich();
524 void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
526 if(!checkTableObject())
527 return;
529 SdrTableObj& rTableObj(*mxTableObj.get());
530 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
531 bool bInsertAfter = true;
532 sal_uInt16 nCount = 0;
534 if( pArgs )
536 const SfxPoolItem* pItem = nullptr;
537 pArgs->GetItemState(nSId, false, &pItem);
538 if (pItem)
540 nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
541 if(const SfxBoolItem* pItem2 = pArgs->GetItemIfSet(SID_TABLE_PARAM_INSERT_AFTER))
542 bInsertAfter = pItem2->GetValue();
546 CellPos aStart, aEnd;
547 if( hasSelectedCells() )
549 getSelectedCells( aStart, aEnd );
551 else
553 if( bInsertAfter )
555 aStart.mnCol = mxTable->getColumnCount() - 1;
556 aStart.mnRow = mxTable->getRowCount() - 1;
557 aEnd = aStart;
561 if( rTableObj.IsTextEditActive() )
562 mrView.SdrEndTextEdit(true);
564 RemoveSelection();
566 static const OUStringLiteral sSize( u"Size" );
567 const bool bUndo(rModel.IsUndoEnabled());
569 switch( nSId )
571 case SID_TABLE_INSERT_COL:
573 TableModelNotifyGuard aGuard( mxTable.get() );
575 if( bUndo )
577 rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
578 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
581 Reference< XTableColumns > xCols( mxTable->getColumns() );
582 const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
583 const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
584 xCols->insertByIndex( nNewStartColumn, nNewColumns );
586 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
588 // Resolves fdo#61540
589 // On Insert before, the reference column whose size is going to be
590 // used for newly created column(s) is wrong. As the new columns are
591 // inserted before the reference column, the reference column moved
592 // to the new position by no., of new columns i.e (earlier+newcolumns).
593 Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
594 setPropertyValue( sSize,
595 Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
596 getPropertyValue( sSize ) );
599 // Copy cell properties
600 sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
601 sal_Int32 nRowSpan = 0;
602 bool bNewSpan = false;
604 for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
606 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
608 // When we insert new COLUMNs, we want to copy ROW spans.
609 if (xSourceCell.is() && nRowSpan == 0)
611 // we are not in a span yet. Let's find out if the current cell is in a span.
612 sal_Int32 nColSpan = sal_Int32();
613 sal_Int32 nSpanInfoCol = sal_Int32();
615 if( xSourceCell->getRowSpan() > 1 )
617 // The current cell is the top-left cell in a span.
618 // Get the span info and propagate it to the target.
619 nRowSpan = xSourceCell->getRowSpan();
620 nColSpan = xSourceCell->getColumnSpan();
621 nSpanInfoCol = nPropSrcCol;
623 else if( xSourceCell->isMerged() )
625 // The current cell is a middle cell in a 2D span.
626 // Look for the top-left cell in the span.
627 for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
629 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
630 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
632 nRowSpan = xMergeInfoCell->getRowSpan();
633 nColSpan = xMergeInfoCell->getColumnSpan();
634 break;
637 if( nRowSpan == 1 )
638 nRowSpan = 0;
641 // The target columns are outside the span; Start a new span.
642 if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
643 bNewSpan = true;
646 // Now copy the properties from the source to the targets
647 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
649 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
650 if( xTargetCell.is() )
652 if( nRowSpan > 0 )
654 if( bNewSpan )
655 xTargetCell->merge( 1, nRowSpan );
656 else
657 xTargetCell->setMerged();
659 xTargetCell->copyFormatFrom( xSourceCell );
663 if( nRowSpan > 0 )
665 --nRowSpan;
666 bNewSpan = false;
670 if( bUndo )
671 rModel.EndUndo();
673 aStart.mnCol = nNewStartColumn;
674 aStart.mnRow = 0;
675 aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
676 aEnd.mnRow = mxTable->getRowCount() - 1;
677 break;
680 case SID_TABLE_INSERT_ROW:
682 TableModelNotifyGuard aGuard( mxTable.get() );
684 if( bUndo )
686 rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
687 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
690 Reference< XTableRows > xRows( mxTable->getRows() );
691 const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
692 const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
693 xRows->insertByIndex( nNewRowStart, nNewRows );
695 for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
697 Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
698 setPropertyValue( sSize,
699 Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
700 getPropertyValue( sSize ) );
703 // Copy the cell properties
704 sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
705 sal_Int32 nColSpan = 0;
706 bool bNewSpan = false;
708 for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
710 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
712 if (!xSourceCell.is())
713 continue;
715 // When we insert new ROWs, we want to copy COLUMN spans.
716 if( nColSpan == 0 )
718 // we are not in a span yet. Let's find out if the current cell is in a span.
719 sal_Int32 nRowSpan = sal_Int32();
720 sal_Int32 nSpanInfoRow = sal_Int32();
722 if( xSourceCell->getColumnSpan() > 1 )
724 // The current cell is the top-left cell in a span.
725 // Get the span info and propagate it to the target.
726 nColSpan = xSourceCell->getColumnSpan();
727 nRowSpan = xSourceCell->getRowSpan();
728 nSpanInfoRow = nPropSrcRow;
730 else if( xSourceCell->isMerged() )
732 // The current cell is a middle cell in a 2D span.
733 // Look for the top-left cell in the span.
734 for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
736 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
737 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
739 nColSpan = xMergeInfoCell->getColumnSpan();
740 nRowSpan = xMergeInfoCell->getRowSpan();
741 break;
744 if( nColSpan == 1 )
745 nColSpan = 0;
748 // Inserted rows are outside the span; Start a new span.
749 if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
750 bNewSpan = true;
753 // Now copy the properties from the source to the targets
754 for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
756 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
757 if( xTargetCell.is() )
759 if( nColSpan > 0 )
761 if( bNewSpan )
762 xTargetCell->merge( nColSpan, 1 );
763 else
764 xTargetCell->setMerged();
766 xTargetCell->copyFormatFrom( xSourceCell );
770 if( nColSpan > 0 )
772 --nColSpan;
773 bNewSpan = false;
777 if( bUndo )
778 rModel.EndUndo();
780 aStart.mnCol = 0;
781 aStart.mnRow = nNewRowStart;
782 aEnd.mnCol = mxTable->getColumnCount() - 1;
783 aEnd.mnRow = aStart.mnRow + nNewRows - 1;
784 break;
788 StartSelection( aStart );
789 UpdateSelection( aEnd );
793 void SvxTableController::onDelete( sal_uInt16 nSId )
795 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
796 if( !pTableObj || !mxTable.is() )
797 return;
799 if( nSId == SID_TABLE_DELETE_TABLE )
801 if( pTableObj->IsTextEditActive() )
802 mrView.SdrEndTextEdit(true);
804 mrView.DeleteMarkedObj();
806 else if( hasSelectedCells() )
808 CellPos aStart, aEnd;
809 getSelectedCells( aStart, aEnd );
811 if( pTableObj->IsTextEditActive() )
812 mrView.SdrEndTextEdit(true);
814 RemoveSelection();
816 bool bDeleteTable = false;
817 switch( nSId )
819 case SID_TABLE_DELETE_COL:
821 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
822 if( nRemovedColumns == mxTable->getColumnCount() )
824 bDeleteTable = true;
826 else
828 Reference< XTableColumns > xCols( mxTable->getColumns() );
829 xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
830 EditCell(aStart, nullptr, TblAction::NONE);
832 break;
835 case SID_TABLE_DELETE_ROW:
837 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
838 if( nRemovedRows == mxTable->getRowCount() )
840 bDeleteTable = true;
842 else
844 Reference< XTableRows > xRows( mxTable->getRows() );
845 xRows->removeByIndex( aStart.mnRow, nRemovedRows );
846 EditCell(aStart, nullptr, TblAction::NONE);
848 break;
852 if( bDeleteTable )
853 mrView.DeleteMarkedObj();
854 else
855 UpdateTableShape();
860 void SvxTableController::onSelect( sal_uInt16 nSId )
862 if( !mxTable.is() )
863 return;
865 const sal_Int32 nRowCount = mxTable->getRowCount();
866 const sal_Int32 nColCount = mxTable->getColumnCount();
867 if( !(nRowCount && nColCount) )
868 return;
870 CellPos aStart, aEnd;
871 getSelectedCells( aStart, aEnd );
873 switch( nSId )
875 case SID_TABLE_SELECT_ALL:
876 aEnd.mnCol = 0; aEnd.mnRow = 0;
877 aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
878 break;
879 case SID_TABLE_SELECT_COL:
880 aEnd.mnRow = nRowCount - 1;
881 aStart.mnRow = 0;
882 break;
883 case SID_TABLE_SELECT_ROW:
884 aEnd.mnCol = nColCount - 1;
885 aStart.mnCol = 0;
886 break;
889 StartSelection( aEnd );
890 gotoCell( aStart, true, nullptr );
893 SvxBoxItem SvxTableController::TextDistancesToSvxBoxItem(const SfxItemSet& rAttrSet)
895 // merge drawing layer text distance items into SvxBoxItem used by the dialog
896 SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
897 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
898 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
899 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
900 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
901 return aBoxItem;
904 void SvxTableController::SvxBoxItemToTextDistances(const SvxBoxItem& pOriginalItem, SfxItemSet& rAttrSet)
906 const SvxBoxItem* pNewItem( rAttrSet.GetItemIfSet( SDRATTR_TABLE_BORDER ) );
907 if ( !pNewItem )
908 return;
910 if( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) != pOriginalItem.GetDistance( SvxBoxItemLine::LEFT ) )
911 rAttrSet.Put(makeSdrTextLeftDistItem( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) ) );
913 if( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) != pOriginalItem.GetDistance( SvxBoxItemLine::RIGHT ) )
914 rAttrSet.Put(makeSdrTextRightDistItem( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) ) );
916 if( pNewItem->GetDistance( SvxBoxItemLine::TOP ) != pOriginalItem.GetDistance( SvxBoxItemLine::TOP ) )
917 rAttrSet.Put(makeSdrTextUpperDistItem( pNewItem->GetDistance( SvxBoxItemLine::TOP ) ) );
919 if( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) != pOriginalItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
920 rAttrSet.Put(makeSdrTextLowerDistItem( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) ) );
923 void SvxTableController::onFormatTable(const SfxRequest& rReq)
925 if(!mxTableObj.get().is())
926 return;
928 SdrTableObj& rTableObj(*mxTableObj.get());
929 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
930 const SfxItemSet* pArgs = rReq.GetArgs();
932 if(pArgs)
933 return;
935 SfxItemSet aNewAttr(rModel.GetItemPool());
937 // merge drawing layer text distance items into SvxBoxItem used by the dialog
938 SvxBoxItem aBoxItem(TextDistancesToSvxBoxItem(aNewAttr));
940 SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
942 MergeAttrFromSelectedCells(aNewAttr, false);
943 FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
944 aNewAttr.Put( aBoxItem );
945 aNewAttr.Put( aBoxInfoItem );
947 // Fill in shadow properties.
948 const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
949 for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
951 if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
953 continue;
956 aNewAttr.Put(rTableItemSet.Get(nWhich));
959 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
960 VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
961 rReq.GetFrameWeld(),
962 &aNewAttr,
963 rModel, false) );
965 // Even Cancel Button is returning positive(101) value,
966 xDlg->StartExecuteAsync([xDlg, this, aBoxItem, aBoxInfoItem](int nResult){
967 if (nResult == RET_OK)
969 SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
971 //Only properties that were unchanged by the dialog appear in this
972 //itemset. We had constructed these two properties from other
973 //ones, so if they were not changed, then forcible set them back to
974 //their originals in the new result set so we can decompose that
975 //unchanged state back to their input properties
976 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
978 aNewSet.Put(aBoxItem);
980 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
982 aNewSet.Put(aBoxInfoItem);
985 SvxBoxItemToTextDistances(aBoxItem, aNewSet);
987 if (checkTableObject() && mxTable.is())
989 // Create a single undo action when applying the result of the dialog.
990 SdrTableObj& rTableObject(*mxTableObj.get());
991 SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
992 bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
993 if (bUndo)
995 rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
998 this->SetAttrToSelectedCells(aNewSet, false);
1000 this->SetAttrToSelectedShape(aNewSet);
1002 if (bUndo)
1004 rSdrModel.EndUndo();
1009 xDlg->disposeOnce();
1013 void SvxTableController::Execute( SfxRequest& rReq )
1015 const sal_uInt16 nSId = rReq.GetSlot();
1016 switch( nSId )
1018 case SID_TABLE_INSERT_ROW:
1019 case SID_TABLE_INSERT_COL:
1020 onInsert( nSId, rReq.GetArgs() );
1021 break;
1022 case SID_TABLE_DELETE_ROW:
1023 case SID_TABLE_DELETE_COL:
1024 case SID_TABLE_DELETE_TABLE:
1025 onDelete( nSId );
1026 break;
1027 case SID_TABLE_SELECT_ALL:
1028 case SID_TABLE_SELECT_COL:
1029 case SID_TABLE_SELECT_ROW:
1030 onSelect( nSId );
1031 break;
1032 case SID_FORMAT_TABLE_DLG:
1033 onFormatTable( rReq );
1034 break;
1036 case SID_FRAME_LINESTYLE:
1037 case SID_FRAME_LINECOLOR:
1038 case SID_ATTR_BORDER:
1040 const SfxItemSet* pArgs = rReq.GetArgs();
1041 if( pArgs )
1042 ApplyBorderAttr( *pArgs );
1044 break;
1046 case SID_ATTR_FILL_STYLE:
1048 const SfxItemSet* pArgs = rReq.GetArgs();
1049 if( pArgs )
1050 SetAttributes( *pArgs, false );
1052 break;
1054 case SID_TABLE_MERGE_CELLS:
1055 MergeMarkedCells();
1056 break;
1058 case SID_TABLE_SPLIT_CELLS:
1059 SplitMarkedCells(rReq);
1060 break;
1062 case SID_TABLE_MINIMAL_COLUMN_WIDTH:
1063 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
1064 break;
1066 case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
1067 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
1068 break;
1070 case SID_TABLE_DISTRIBUTE_COLUMNS:
1071 DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
1072 break;
1074 case SID_TABLE_MINIMAL_ROW_HEIGHT:
1075 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
1076 break;
1078 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
1079 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
1080 break;
1082 case SID_TABLE_DISTRIBUTE_ROWS:
1083 DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
1084 break;
1086 case SID_TABLE_VERT_BOTTOM:
1087 case SID_TABLE_VERT_CENTER:
1088 case SID_TABLE_VERT_NONE:
1089 SetVertical( nSId );
1090 break;
1092 case SID_AUTOFORMAT:
1093 case SID_TABLE_SORT_DIALOG:
1094 case SID_TABLE_AUTOSUM:
1095 default:
1096 break;
1098 case SID_TABLE_STYLE:
1099 SetTableStyle( rReq.GetArgs() );
1100 break;
1102 case SID_TABLE_STYLE_SETTINGS:
1103 SetTableStyleSettings( rReq.GetArgs() );
1104 break;
1105 case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
1106 changeTableEdge(rReq);
1107 break;
1111 void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
1113 if(!checkTableObject())
1114 return;
1116 SdrTableObj& rTableObj(*mxTableObj.get());
1117 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1119 if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
1120 return;
1122 const SfxStringItem* pArg = &pArgs->Get( SID_TABLE_STYLE );
1123 if( !(pArg && mxTable.is()) )
1124 return;
1128 Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
1129 Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
1130 Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
1132 if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
1134 // found table style with the same name
1135 Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
1137 const bool bUndo = rModel.IsUndoEnabled();
1139 if( bUndo )
1141 rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
1142 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1145 rTableObj.setTableStyle( xNewTableStyle );
1147 const sal_Int32 nRowCount = mxTable->getRowCount();
1148 const sal_Int32 nColCount = mxTable->getColumnCount();
1149 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1151 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
1153 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1154 if( xCell.is() )
1156 SfxItemSet aSet( xCell->GetItemSet() );
1157 bool bChanges = false;
1158 SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
1159 SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
1160 if (pStyleSheet)
1162 const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
1164 for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
1166 if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
1168 aSet.ClearItem( nWhich );
1169 bChanges = true;
1174 if( bChanges )
1176 if( bUndo )
1177 xCell->AddUndo();
1179 xCell->SetMergedItemSetAndBroadcast( aSet, true );
1183 catch( Exception& )
1185 TOOLS_WARN_EXCEPTION("svx.table", "");
1189 if( bUndo )
1190 rModel.EndUndo();
1193 catch( Exception& )
1195 TOOLS_WARN_EXCEPTION("svx.table", "");
1199 void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
1201 if(!checkTableObject())
1202 return;
1204 SdrTableObj& rTableObj(*mxTableObj.get());
1205 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1207 TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
1208 const SfxBoolItem *pPoolItem=nullptr;
1210 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTROWSTYLE, false)) )
1211 aSettings.mbUseFirstRow = pPoolItem->GetValue();
1213 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTROWSTYLE, false)) )
1214 aSettings.mbUseLastRow = pPoolItem->GetValue();
1216 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGROWSTYLE, false)) )
1217 aSettings.mbUseRowBanding = pPoolItem->GetValue();
1219 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTCOLUMNSTYLE, false)) )
1220 aSettings.mbUseFirstColumn = pPoolItem->GetValue();
1222 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTCOLUMNSTYLE, false)) )
1223 aSettings.mbUseLastColumn = pPoolItem->GetValue();
1225 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGCOLUMNSTYLE, false)) )
1226 aSettings.mbUseColumnBanding = pPoolItem->GetValue();
1228 if( aSettings == rTableObj.getTableStyleSettings() )
1229 return;
1231 const bool bUndo(rModel.IsUndoEnabled());
1233 if( bUndo )
1235 rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
1236 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1239 rTableObj.setTableStyleSettings( aSettings );
1241 if( bUndo )
1242 rModel.EndUndo();
1245 void SvxTableController::SetVertical( sal_uInt16 nSId )
1247 if(!checkTableObject())
1248 return;
1250 SdrTableObj& rTableObj(*mxTableObj.get());
1251 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1253 TableModelNotifyGuard aGuard( mxTable.get() );
1254 const bool bUndo(rModel.IsUndoEnabled());
1256 if (bUndo)
1258 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
1259 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
1262 CellPos aStart, aEnd;
1263 getSelectedCells( aStart, aEnd );
1265 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
1267 switch( nSId )
1269 case SID_TABLE_VERT_BOTTOM:
1270 eAdj = SDRTEXTVERTADJUST_BOTTOM;
1271 break;
1272 case SID_TABLE_VERT_CENTER:
1273 eAdj = SDRTEXTVERTADJUST_CENTER;
1274 break;
1275 //case SID_TABLE_VERT_NONE:
1276 default:
1277 break;
1280 SdrTextVertAdjustItem aItem( eAdj );
1282 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1284 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1286 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1287 if( xCell.is() )
1289 if (bUndo)
1290 xCell->AddUndo();
1291 SfxItemSet aSet(xCell->GetItemSet());
1292 aSet.Put(aItem);
1293 xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
1298 UpdateTableShape();
1300 if (bUndo)
1301 rModel.EndUndo();
1304 void SvxTableController::MergeMarkedCells()
1306 CellPos aStart, aEnd;
1307 getSelectedCells( aStart, aEnd );
1308 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
1309 if( pTableObj )
1311 if( pTableObj->IsTextEditActive() )
1312 mrView.SdrEndTextEdit(true);
1314 TableModelNotifyGuard aGuard( mxTable.get() );
1315 MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
1319 void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
1321 if(!checkTableObject() || !mxTable.is())
1322 return;
1324 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1325 VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
1327 xDlg->StartExecuteAsync([xDlg, this](int) {
1328 const sal_Int32 nCount = xDlg->GetCount() - 1;
1330 if( nCount < 1 )
1331 return;
1333 CellPos aStart, aEnd;
1334 getSelectedCells( aStart, aEnd );
1335 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
1336 const sal_Int32 nRowCount = mxTable->getRowCount();
1337 const sal_Int32 nColCount = mxTable->getColumnCount();
1338 SdrTableObj& rTableObj(*mxTableObj.get());
1340 if( rTableObj.IsTextEditActive() )
1341 mrView.SdrEndTextEdit(true);
1343 TableModelNotifyGuard aGuard( mxTable.get() );
1344 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1345 const bool bUndo(rModel.IsUndoEnabled());
1347 if( bUndo )
1349 rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
1350 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1353 if( xDlg->IsHorizontal() )
1355 xRange->split( 0, nCount );
1357 else
1359 xRange->split( nCount, 0 );
1362 if( bUndo )
1363 rModel.EndUndo();
1365 aEnd.mnRow += mxTable->getRowCount() - nRowCount;
1366 aEnd.mnCol += mxTable->getColumnCount() - nColCount;
1368 setSelectedCells( aStart, aEnd );
1370 xDlg->disposeOnce();
1374 void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
1376 if(!checkTableObject())
1377 return;
1379 SdrTableObj& rTableObj(*mxTableObj.get());
1380 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1381 const bool bUndo(rModel.IsUndoEnabled());
1383 if( bUndo )
1385 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
1386 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1389 CellPos aStart, aEnd;
1390 getSelectedCells( aStart, aEnd );
1391 rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
1393 if( bUndo )
1394 rModel.EndUndo();
1397 void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
1399 if(!checkTableObject())
1400 return;
1402 SdrTableObj& rTableObj(*mxTableObj.get());
1403 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1404 const bool bUndo(rModel.IsUndoEnabled());
1406 if( bUndo )
1408 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
1409 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1412 CellPos aStart, aEnd;
1413 getSelectedCells( aStart, aEnd );
1414 rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
1416 if( bUndo )
1417 rModel.EndUndo();
1420 bool SvxTableController::HasMarked() const
1422 return mbCellSelectionMode && mxTable.is();
1425 bool SvxTableController::DeleteMarked()
1427 if(!checkTableObject() || !HasMarked())
1428 return false;
1430 SdrTableObj& rTableObj(*mxTableObj.get());
1431 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1432 const bool bUndo(rModel.IsUndoEnabled());
1433 bool bDeleteTable = false;
1435 if (bUndo)
1436 rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
1438 CellPos aStart, aEnd;
1439 getSelectedCells( aStart, aEnd );
1440 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
1441 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
1442 if( nRemovedColumns == mxTable->getColumnCount() && nRemovedRows == mxTable->getRowCount())
1444 bDeleteTable = true;
1446 else
1448 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1450 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1452 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1453 if (xCell.is() && xCell->hasText())
1455 if (bUndo)
1456 xCell->AddUndo();
1457 xCell->SetOutlinerParaObject(std::nullopt);
1463 if (bDeleteTable)
1464 mrView.DeleteMarkedObj();
1466 if (bUndo)
1467 rModel.EndUndo();
1469 if (!bDeleteTable)
1470 UpdateTableShape();
1471 return true;
1474 bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
1476 if( hasSelectedCells() )
1478 rpStyleSheet = nullptr;
1480 if( mxTable.is() )
1482 SfxStyleSheet* pRet=nullptr;
1483 bool b1st=true;
1485 CellPos aStart, aEnd;
1486 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
1488 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1490 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1492 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1493 if( xCell.is() )
1495 SfxStyleSheet* pSS=xCell->GetStyleSheet();
1496 if(b1st)
1498 pRet=pSS;
1500 else if(pRet != pSS)
1502 return true;
1504 b1st=false;
1508 rpStyleSheet = pRet;
1509 return true;
1512 return false;
1515 bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
1517 if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
1519 if( mxTable.is() )
1521 CellPos aStart, aEnd;
1522 getSelectedCells( aStart, aEnd );
1524 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1526 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1528 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1529 if( xCell.is() )
1530 xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
1534 UpdateTableShape();
1535 return true;
1538 return false;
1541 void SvxTableController::changeTableEdge(const SfxRequest& rReq)
1543 if (!checkTableObject())
1544 return;
1546 const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
1547 const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
1548 const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
1550 if (!(pType && pIndex && pOffset))
1551 return;
1553 const OUString sType = pType->GetValue();
1554 const sal_uInt16 nIndex = pIndex->GetValue();
1555 const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
1557 SdrTableObj& rTableObj(*mxTableObj.get());
1559 sal_Int32 nEdgeIndex = -1;
1560 bool bHorizontal = sType.startsWith("row");
1562 if (sType == "column-left" || sType == "row-left")
1564 nEdgeIndex = 0;
1566 else if (sType == "column-right")
1568 // Number of edges = number of columns + 1
1569 nEdgeIndex = rTableObj.getColumnCount();
1571 else if (sType == "row-right")
1573 // Number of edges = number of rows + 1
1574 nEdgeIndex = rTableObj.getRowCount();
1576 else if (sType == "column-middle" || sType == "row-middle")
1578 nEdgeIndex = nIndex + 1;
1581 if (nEdgeIndex < 0)
1582 return;
1584 TableModelNotifyGuard aGuard(mxTable.get());
1585 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1586 const bool bUndo(rModel.IsUndoEnabled());
1587 if (bUndo)
1589 auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
1590 rModel.BegUndo(pUndoObject->GetComment());
1592 auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
1593 if (pGeoUndo)
1594 pGeoUndo->SetSkipChangeLayout(true);
1596 rModel.AddUndo(std::move(pUndoObject));
1598 tools::Rectangle aBoundRect;
1599 if (rTableObj.GetUserCall())
1600 aBoundRect = rTableObj.GetLastBoundRect();
1601 rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
1602 rTableObj.SetChanged();
1603 rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
1604 if (bUndo)
1605 rModel.EndUndo();
1608 // internals
1611 bool SvxTableController::checkTableObject()
1613 return mxTableObj.get().is();
1617 SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
1619 const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
1620 const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
1621 const bool bTextEdit = mrView.IsTextEdit();
1623 TblAction nAction = TblAction::HandledByView;
1625 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
1626 if( !pTableObj )
1627 return nAction;
1629 // handle special keys
1630 const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
1631 switch( nCode )
1633 case awt::Key::ESCAPE: // handle escape
1635 if( bTextEdit )
1637 // escape during text edit ends text edit
1638 nAction = TblAction::StopTextEdit;
1640 if( mbCellSelectionMode )
1642 // escape with selected cells removes selection
1643 nAction = TblAction::RemoveSelection;
1645 break;
1647 case awt::Key::RETURN: // handle return
1649 if( !bMod1 && !bMod2 && !bTextEdit )
1651 // when not already editing, return starts text edit
1652 setSelectionStart( SdrTableObj::getFirstCell() );
1653 nAction = TblAction::EditCell;
1655 break;
1657 case awt::Key::F2: // f2 toggles text edit
1659 if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
1662 else if( bTextEdit )
1664 // f2 during text edit stops text edit
1665 nAction = TblAction::StopTextEdit;
1667 else if( mbCellSelectionMode )
1669 // f2 with selected cells removes selection
1670 nAction = TblAction::RemoveSelection;
1672 else
1674 // f2 with no selection and no text edit starts text edit
1675 setSelectionStart( SdrTableObj::getFirstCell() );
1676 nAction = TblAction::EditCell;
1678 break;
1680 case awt::Key::HOME:
1681 case awt::Key::NUM7:
1683 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1685 if( bMod1 && !bMod2 )
1687 // ctrl + home jumps to first cell
1688 nAction = TblAction::GotoFirstCell;
1690 else if( !bMod1 && bMod2 )
1692 // alt + home jumps to first column
1693 nAction = TblAction::GotoFirstColumn;
1696 break;
1698 case awt::Key::END:
1699 case awt::Key::NUM1:
1701 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1703 if( bMod1 && !bMod2 )
1705 // ctrl + end jumps to last cell
1706 nAction = TblAction::GotoLastCell;
1708 else if( !bMod1 && bMod2 )
1710 // alt + home jumps to last column
1711 nAction = TblAction::GotoLastColumn;
1714 break;
1717 case awt::Key::TAB:
1719 if( bTextEdit || mbCellSelectionMode )
1720 nAction = TblAction::Tab;
1721 break;
1724 case awt::Key::UP:
1725 case awt::Key::NUM8:
1726 case awt::Key::DOWN:
1727 case awt::Key::NUM2:
1728 case awt::Key::LEFT:
1729 case awt::Key::NUM4:
1730 case awt::Key::RIGHT:
1731 case awt::Key::NUM6:
1734 if( !bMod1 && bMod2 )
1736 if(bTextEdit || mbCellSelectionMode)
1738 if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
1740 nAction = TblAction::GotoLeftCell;
1741 break;
1743 else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
1745 nAction = TblAction::GotoRightCell;
1746 break;
1751 bool bTextMove = false;
1752 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
1753 if( pOLV )
1755 RemoveSelection();
1756 // during text edit, check if we navigate out of the cell
1757 ESelection aOldSelection = pOLV->GetSelection();
1758 pOLV->PostKeyEvent(rKEvt);
1759 bTextMove = aOldSelection == pOLV->GetSelection();
1760 if( !bTextMove )
1762 nAction = TblAction::NONE;
1766 if( mbCellSelectionMode || bTextMove )
1768 // no text edit, navigate in cells if selection active
1769 switch( nCode )
1771 case awt::Key::LEFT:
1772 case awt::Key::NUM4:
1773 nAction = TblAction::GotoLeftCell;
1774 break;
1775 case awt::Key::RIGHT:
1776 case awt::Key::NUM6:
1777 nAction = TblAction::GotoRightCell;
1778 break;
1779 case awt::Key::DOWN:
1780 case awt::Key::NUM2:
1781 nAction = TblAction::GotoDownCell;
1782 break;
1783 case awt::Key::UP:
1784 case awt::Key::NUM8:
1785 nAction = TblAction::GotoUpCell;
1786 break;
1789 break;
1791 case awt::Key::PAGEUP:
1792 if( bMod2 )
1793 nAction = TblAction::GotoFirstRow;
1794 break;
1796 case awt::Key::PAGEDOWN:
1797 if( bMod2 )
1798 nAction = TblAction::GotoLastRow;
1799 break;
1801 return nAction;
1804 bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
1806 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
1807 if( !pTableObj )
1808 return false;
1810 switch( nAction )
1812 case TblAction::GotoFirstCell:
1814 gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
1815 break;
1818 case TblAction::GotoLeftCell:
1820 gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
1821 break;
1824 case TblAction::GotoRightCell:
1826 gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
1827 break;
1830 case TblAction::GotoLastCell:
1832 gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
1833 break;
1836 case TblAction::GotoFirstColumn:
1838 CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
1839 gotoCell( aPos, bSelect, pWindow, nAction );
1840 break;
1843 case TblAction::GotoLastColumn:
1845 CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
1846 gotoCell( aPos, bSelect, pWindow, nAction );
1847 break;
1850 case TblAction::GotoFirstRow:
1852 CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
1853 gotoCell( aPos, bSelect, pWindow, nAction );
1854 break;
1857 case TblAction::GotoUpCell:
1859 gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1860 break;
1863 case TblAction::GotoDownCell:
1865 gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1866 break;
1869 case TblAction::GotoLastRow:
1871 CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
1872 gotoCell( aPos, bSelect, pWindow, nAction );
1873 break;
1876 case TblAction::EditCell:
1877 EditCell( getSelectionStart(), pWindow, nAction );
1878 break;
1880 case TblAction::StopTextEdit:
1881 StopTextEdit();
1882 break;
1884 case TblAction::RemoveSelection:
1885 RemoveSelection();
1886 break;
1888 case TblAction::Tab:
1890 if( bSelect )
1891 gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
1892 else
1894 CellPos aSelectionEnd( getSelectionEnd() );
1895 CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
1896 if( aSelectionEnd == aNextCell )
1898 onInsert( SID_TABLE_INSERT_ROW );
1899 aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
1901 gotoCell( aNextCell, false, pWindow, nAction );
1903 break;
1905 default:
1906 break;
1909 return nAction != TblAction::HandledByView;
1913 void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
1915 auto pTable = mxTableObj.get();
1916 if( pTable && pTable->IsTextEditActive() )
1917 mrView.SdrEndTextEdit(true);
1919 if( bSelect )
1921 maCursorLastPos = rPos;
1922 if( pTable )
1923 pTable->setActiveCell( rPos );
1925 if( !mbCellSelectionMode )
1927 setSelectedCells( maCursorFirstPos, rPos );
1929 else
1931 UpdateSelection( rPos );
1934 else
1936 RemoveSelection();
1937 EditCell( rPos, pWindow, nAction );
1942 const CellPos& SvxTableController::getSelectionStart()
1944 checkCell( maCursorFirstPos );
1945 return maCursorFirstPos;
1949 void SvxTableController::setSelectionStart( const CellPos& rPos )
1951 maCursorFirstPos = rPos;
1955 const CellPos& SvxTableController::getSelectionEnd()
1957 checkCell( maCursorLastPos );
1958 return maCursorLastPos;
1962 void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
1964 if(!checkTableObject() || !mxTable.is())
1965 return;
1969 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
1971 if( xRange->isMergeable() )
1973 SdrTableObj& rTableObj(*mxTableObj.get());
1974 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1975 const bool bUndo(rModel.IsUndoEnabled());
1977 if( bUndo )
1979 rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
1980 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1983 xRange->merge();
1984 mbHasJustMerged = true;
1985 setSelectedCells( maCursorFirstPos, maCursorFirstPos );
1987 if( bUndo )
1988 rModel.EndUndo();
1991 catch( Exception& )
1993 TOOLS_WARN_EXCEPTION( "svx.table", "" );
1998 void SvxTableController::checkCell( CellPos& rPos ) const
2000 if( !mxTable.is() )
2001 return;
2005 if( rPos.mnCol >= mxTable->getColumnCount() )
2006 rPos.mnCol = mxTable->getColumnCount()-1;
2008 if( rPos.mnRow >= mxTable->getRowCount() )
2009 rPos.mnRow = mxTable->getRowCount()-1;
2011 catch( Exception& )
2013 TOOLS_WARN_EXCEPTION("svx.table", "");
2018 void SvxTableController::findMergeOrigin( CellPos& rPos )
2020 if( !mxTable.is() )
2021 return;
2025 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
2026 if( xCell->isMerged() )
2028 ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
2031 catch( Exception& )
2033 TOOLS_WARN_EXCEPTION("svx.table", "");
2038 void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
2040 SdrPageView* pPV(mrView.GetSdrPageView());
2042 if(nullptr == pPV || !checkTableObject())
2043 return;
2045 SdrTableObj& rTableObj(*mxTableObj.get());
2047 if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
2048 return;
2050 bool bEmptyOutliner = false;
2052 if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
2054 ::Outliner* pOutl = mrView.GetTextEditOutliner();
2055 sal_Int32 nParaCnt = pOutl->GetParagraphCount();
2056 Paragraph* p1stPara = pOutl->GetParagraph( 0 );
2058 if(nParaCnt==1 && p1stPara)
2060 // with only one paragraph
2061 if (pOutl->GetText(p1stPara).isEmpty())
2063 bEmptyOutliner = true;
2068 CellPos aPos( rPos );
2069 findMergeOrigin( aPos );
2071 if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
2072 return;
2074 if( rTableObj.IsTextEditActive() )
2075 mrView.SdrEndTextEdit(true);
2077 rTableObj.setActiveCell( aPos );
2079 // create new outliner, owner will be the SdrObjEditView
2080 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2081 std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
2083 if (pOutl && rTableObj.IsVerticalWriting())
2084 pOutl->SetVertical( true );
2086 if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
2087 return;
2089 maCursorLastPos = maCursorFirstPos = rPos;
2091 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
2093 // Move cursor to end of text
2094 ESelection aNewSelection;
2096 const WritingMode eMode = rTableObj.GetWritingMode();
2097 if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
2099 const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
2100 ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
2102 if( bLast )
2103 aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
2105 pOLV->SetSelection(aNewSelection);
2109 void SvxTableController::StopTextEdit()
2111 if(mrView.IsTextEdit())
2113 mrView.SdrEndTextEdit();
2114 mrView.SetCurrentObj(SdrObjKind::Table);
2115 mrView.SetEditMode(SdrViewEditMode::Edit);
2120 void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
2122 if( mbCellSelectionMode )
2124 checkCell( maCursorFirstPos );
2125 checkCell( maCursorLastPos );
2127 rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2128 rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2129 rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2130 rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2132 if( !mxTable.is() )
2133 return;
2135 bool bExt = false;
2138 bExt = false;
2139 for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
2141 for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
2143 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
2144 if( !xCell.is() )
2145 continue;
2147 if( xCell->isMerged() )
2149 CellPos aPos( nCol, nRow );
2150 findMergeOrigin( aPos );
2151 if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
2153 rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
2154 rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
2155 bExt = true;
2158 else
2160 if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
2162 rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
2163 rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
2164 bExt = true;
2170 while(bExt);
2172 else if(mrView.IsTextEdit())
2174 rFirst = getSelectionStart();
2175 findMergeOrigin( rFirst );
2176 rLast = rFirst;
2178 if( mxTable.is() )
2180 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
2181 if( xCell.is() )
2183 rLast.mnCol += xCell->getColumnSpan() - 1;
2184 rLast.mnRow += xCell->getRowSpan() - 1;
2188 else
2190 rFirst.mnCol = 0;
2191 rFirst.mnRow = 0;
2192 if( mxTable.is() )
2194 rLast.mnRow = mxTable->getRowCount()-1;
2195 rLast.mnCol = mxTable->getColumnCount()-1;
2197 else
2199 rLast.mnRow = 0;
2200 rLast.mnCol = 0;
2206 void SvxTableController::StartSelection( const CellPos& rPos )
2208 StopTextEdit();
2209 mbCellSelectionMode = true;
2210 maCursorLastPos = maCursorFirstPos = rPos;
2211 mrView.MarkListHasChanged();
2215 void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
2217 StopTextEdit();
2218 mbCellSelectionMode = true;
2219 maCursorFirstPos = rStart;
2220 UpdateSelection( rEnd );
2224 bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
2226 if(!checkTableObject() || !mxTable.is())
2227 return false;
2229 SdrTableObj& rTableObj(*mxTableObj.get());
2230 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2232 if (mrView.IsTextEdit())
2233 return true;
2235 CellPos aStart, aEnd;
2237 if(hasSelectedCells())
2239 getSelectedCells(aStart, aEnd);
2241 else
2243 aStart.mnRow = 0;
2244 aStart.mnCol = 0;
2245 aEnd.mnRow = mxTable->getRowCount() - 1;
2246 aEnd.mnCol = mxTable->getColumnCount() - 1;
2249 for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
2251 for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
2253 CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
2254 if (xCell.is())
2256 if (rModel.IsUndoEnabled())
2257 xCell->AddUndo();
2259 SfxItemSet aCellSet(xCell->GetItemSet());
2260 if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
2262 xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
2268 UpdateTableShape();
2270 return true;
2274 void SvxTableController::UpdateSelection( const CellPos& rPos )
2276 maCursorLastPos = rPos;
2277 mrView.MarkListHasChanged();
2281 void SvxTableController::clearSelection()
2283 RemoveSelection();
2287 void SvxTableController::selectAll()
2289 if( mxTable.is() )
2291 CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
2292 if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
2294 CellPos aPos1;
2295 setSelectedCells( aPos1, aPos2 );
2301 void SvxTableController::RemoveSelection()
2303 if( mbCellSelectionMode )
2305 mbCellSelectionMode = false;
2306 mrView.MarkListHasChanged();
2311 void SvxTableController::onTableModified()
2313 if( mnUpdateEvent == nullptr )
2314 mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
2318 void SvxTableController::updateSelectionOverlay()
2320 // There is no need to update selection overlay after merging cells
2321 // since the selection overlay should remain the same
2322 if ( mbHasJustMerged )
2323 return;
2325 destroySelectionOverlay();
2326 if( !mbCellSelectionMode )
2327 return;
2329 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
2330 if( !pTableObj )
2331 return;
2333 sdr::overlay::OverlayObjectCell::RangeVector aRanges;
2335 tools::Rectangle aStartRect, aEndRect;
2336 CellPos aStart,aEnd;
2337 getSelectedCells( aStart, aEnd );
2338 pTableObj->getCellBounds( aStart, aStartRect );
2340 basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
2341 a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
2343 findMergeOrigin( aEnd );
2344 pTableObj->getCellBounds( aEnd, aEndRect );
2345 a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
2346 a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
2347 aRanges.push_back( a2DRange );
2349 ::Color aHighlight( COL_BLUE );
2350 OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
2351 if( pOutDev )
2352 aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
2354 const sal_uInt32 nCount = mrView.PaintWindowCount();
2355 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
2357 SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
2358 if( pPaintWindow )
2360 const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
2361 if( xOverlayManager.is() )
2363 std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, std::vector(aRanges) ));
2365 xOverlayManager->add(*pOverlay);
2366 mpSelectionOverlay.emplace();
2367 mpSelectionOverlay->append(std::move(pOverlay));
2372 // If tiled rendering, emit callbacks for sdr table selection.
2373 if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
2374 return;
2376 tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
2378 if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2379 aSelection = o3tl::toTwips(aSelection, o3tl::Length::mm100);
2381 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2383 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString());
2384 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString());
2389 void SvxTableController::destroySelectionOverlay()
2391 if( !mpSelectionOverlay )
2392 return;
2394 mpSelectionOverlay.reset();
2396 if (comphelper::LibreOfficeKit::isActive())
2398 // Clear the LOK text selection so far provided by this table.
2399 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2401 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY");
2402 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
2403 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
2404 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
2410 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
2412 if( !mxTable.is() )
2413 return;
2415 CellPos aStart, aEnd;
2416 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
2418 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2420 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2422 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2423 if( xCell.is() && !xCell->isMerged() )
2425 const SfxItemSet& rSet = xCell->GetItemSet();
2426 SfxWhichIter aIter(rSet);
2427 sal_uInt16 nWhich(aIter.FirstWhich());
2428 while(nWhich)
2430 SfxItemState nState = aIter.GetItemState(false);
2431 if(!bOnlyHardAttr)
2433 if(SfxItemState::DONTCARE == nState)
2434 rAttr.InvalidateItem(nWhich);
2435 else
2436 rAttr.MergeValue(rSet.Get(nWhich), true);
2438 else if(SfxItemState::SET == nState)
2440 const SfxPoolItem& rItem = rSet.Get(nWhich);
2441 rAttr.MergeValue(rItem, true);
2444 nWhich = aIter.NextWhich();
2452 static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
2454 if( pNew )
2456 const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
2457 if( pOld )
2459 SvxBorderLine aNewLine( *pNew );
2460 aNewLine.SetColor( pOld->GetColor() );
2461 rNewFrame.SetLine( &aNewLine, nLine );
2462 return;
2465 rNewFrame.SetLine( pNew, nLine );
2469 static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
2471 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2473 // current cell is outside the selection
2475 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2477 if (nCellPosFlags & CellPosFlag::Upper)
2479 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
2480 rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
2482 else if (nCellPosFlags & CellPosFlag::Lower)
2484 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
2485 rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
2488 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2490 if (nCellPosFlags & CellPosFlag::Before)
2492 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
2493 rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
2495 else if (nCellPosFlags & CellPosFlag::After)
2497 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
2498 rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
2502 else
2504 // current cell is inside the selection
2506 if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
2507 : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
2508 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
2510 if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2511 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
2513 if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2514 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
2516 if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2517 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
2519 // apply distance to borders
2520 if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
2521 for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
2522 rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
2527 static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
2529 const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
2530 if( pSourceLine )
2532 SvxBorderLine aLine( *pSourceLine );
2533 aLine.SetColor( rColor );
2534 rNewFrame.SetLine( &aLine, nLine );
2539 static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
2541 const Color aColor( pLineColorItem->GetValue() );
2543 if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
2544 ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
2546 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
2547 ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
2549 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
2550 ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
2552 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
2553 ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
2557 static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
2559 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2561 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2563 if (nCellPosFlags & CellPosFlag::Upper)
2565 if( rNewFrame.GetBottom() )
2566 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2568 else if (nCellPosFlags & CellPosFlag::Lower)
2570 if( rNewFrame.GetTop() )
2571 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2574 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2576 if (nCellPosFlags & CellPosFlag::Before)
2578 if( rNewFrame.GetRight() )
2579 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2581 else if (nCellPosFlags & CellPosFlag::After)
2583 if( rNewFrame.GetLeft() )
2584 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2588 else
2590 if( rNewFrame.GetBottom() )
2591 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2592 if( rNewFrame.GetTop() )
2593 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2594 if( rNewFrame.GetRight() )
2595 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2596 if( rNewFrame.GetLeft() )
2597 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2602 void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
2604 if( !mxTable.is() )
2605 return;
2607 const sal_Int32 nRowCount = mxTable->getRowCount();
2608 const sal_Int32 nColCount = mxTable->getColumnCount();
2609 if( !(nRowCount && nColCount) )
2610 return;
2612 const SvxBoxItem* pBoxItem = nullptr;
2613 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
2614 pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
2616 const SvxBoxInfoItem* pBoxInfoItem = nullptr;
2617 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
2618 pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
2620 const SvxColorItem* pLineColorItem = nullptr;
2621 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
2622 pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
2624 const SvxBorderLine* pBorderLineItem = nullptr;
2625 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
2626 pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
2628 if( pBoxInfoItem && !pBoxItem )
2630 const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
2631 pBoxItem = &gaEmptyBoxItem;
2633 else if( pBoxItem && !pBoxInfoItem )
2635 const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
2636 pBoxInfoItem = &gaEmptyBoxInfoItem;
2639 CellPos aStart, aEnd;
2640 getSelectedCells( aStart, aEnd );
2642 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
2643 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
2645 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
2647 CellPosFlag nRowFlags = CellPosFlag::NONE;
2648 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
2649 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
2650 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
2651 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
2653 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
2655 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2656 if( !xCell.is() )
2657 continue;
2659 const SfxItemSet& rSet = xCell->GetItemSet();
2660 const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
2662 SvxBoxItem aNewFrame( *pOldOuter );
2664 CellPosFlag nCellPosFlags = nRowFlags;
2665 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
2666 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
2667 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
2668 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
2670 if( pBoxItem && pBoxInfoItem )
2671 ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
2673 if( pLineColorItem )
2674 ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
2676 if( pBorderLineItem )
2677 ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
2679 if (aNewFrame != *pOldOuter)
2681 SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
2682 aAttr.Put(aNewFrame);
2683 xCell->SetMergedItemSetAndBroadcast( aAttr, false );
2690 void SvxTableController::UpdateTableShape()
2692 rtl::Reference<SdrObject> pTableObj = mxTableObj.get();
2693 if( pTableObj )
2695 pTableObj->ActionChanged();
2696 pTableObj->BroadcastObjectChange();
2698 updateSelectionOverlay();
2702 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
2704 if(!checkTableObject() || !mxTable.is())
2705 return;
2707 SdrTableObj& rTableObj(*mxTableObj.get());
2708 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2709 const bool bUndo(rModel.IsUndoEnabled());
2711 if( bUndo )
2712 rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
2714 CellPos aStart, aEnd;
2715 getSelectedCells( aStart, aEnd );
2717 SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
2718 aAttr.Put(rAttr);
2720 const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2722 if( bFrame )
2724 aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2725 aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2728 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2730 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2732 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2733 if( xCell.is() )
2735 if( bUndo )
2736 xCell->AddUndo();
2737 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2742 if( bFrame )
2744 ApplyBorderAttr( rAttr );
2747 UpdateTableShape();
2749 if( bUndo )
2750 rModel.EndUndo();
2753 void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
2755 if (!checkTableObject() || !mxTable.is())
2756 return;
2758 // Filter out non-shadow items from rAttr.
2759 SfxItemSetFixed<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST> aSet(*rAttr.GetPool());
2760 aSet.Put(rAttr);
2762 if (!aSet.Count())
2764 // If there are no items to be applied on the shape, then don't set anything, it would
2765 // terminate text edit without a good reason, which affects undo/redo.
2766 return;
2769 // Set shadow items on the marked shape.
2770 mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
2773 bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2775 if( mxTableObj.get().is() && hasSelectedCells() )
2777 MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
2779 if( mrView.IsTextEdit() )
2781 OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
2782 if(pTextEditOutlinerView)
2784 // FALSE= consider InvalidItems not as the default, but as "holes"
2785 rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
2789 return true;
2791 else
2793 return false;
2798 bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2800 if( mbCellSelectionMode || mrView.IsTextEdit() )
2802 SetAttrToSelectedCells( rSet, bReplaceAll );
2803 return true;
2805 return false;
2808 rtl::Reference<SdrObject> SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
2810 rtl::Reference<SdrTableObj> pRetval;
2811 sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
2813 if(nullptr == pCurrentSdrTableObj)
2815 return pRetval;
2818 if(!mxTableObj.get().is())
2820 return pRetval;
2823 // get selection and create full selection
2824 CellPos aStart, aEnd;
2825 const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
2827 getSelectedCells(aStart, aEnd);
2829 // compare to see if we have a partial selection
2830 if(aStart != aFullStart || aEnd != aFullEnd)
2832 // create full clone
2833 pRetval = SdrObject::Clone(*pCurrentSdrTableObj, rTargetModel);
2835 // limit SdrObject's TableModel to partial selection
2836 pRetval->CropTableModelToSelection(aStart, aEnd);
2839 return pRetval;
2842 bool SvxTableController::PasteObjModel( const SdrModel& rModel )
2844 if( mxTableObj.get().is() && (rModel.GetPageCount() >= 1) )
2846 const SdrPage* pPastePage = rModel.GetPage(0);
2847 if( pPastePage && pPastePage->GetObjCount() == 1 )
2849 SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
2850 if( pPasteTableObj )
2852 return PasteObject( pPasteTableObj );
2857 return false;
2861 bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
2863 if( !pPasteTableObj )
2864 return false;
2866 Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
2867 if( !xPasteTable.is() )
2868 return false;
2870 if( !mxTable.is() )
2871 return false;
2873 sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
2874 sal_Int32 nPasteRows = xPasteTable->getRowCount();
2876 CellPos aStart, aEnd;
2877 getSelectedCells( aStart, aEnd );
2879 if( mrView.IsTextEdit() )
2880 mrView.SdrEndTextEdit(true);
2882 sal_Int32 nColumns = mxTable->getColumnCount();
2883 sal_Int32 nRows = mxTable->getRowCount();
2885 const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
2886 if( nMissing > 0 )
2888 Reference< XTableRows > xRows( mxTable->getRows() );
2889 xRows->insertByIndex( nRows, nMissing );
2890 nRows = mxTable->getRowCount();
2893 nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
2894 nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
2896 // copy cell contents
2897 for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
2899 for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
2901 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
2902 if( xTargetCell.is() && !xTargetCell->isMerged() )
2904 CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
2905 if (xSourceCell.is())
2907 xTargetCell->AddUndo();
2908 // Prevent cell span exceeding the pasting range.
2909 if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
2910 xTargetCell->replaceContentAndFormatting(xSourceCell);
2911 else
2912 xTargetCell->cloneFrom(xSourceCell);
2914 nCol += xSourceCell->getColumnSpan() - 1;
2915 nTargetCol += xTargetCell->getColumnSpan();
2921 UpdateTableShape();
2923 return true;
2926 bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
2928 if(!mbCellSelectionMode)
2930 return false;
2933 if(!checkTableObject())
2934 return false;
2936 SdrTableObj& rTableObj(*mxTableObj.get());
2937 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2938 const bool bUndo(rModel.IsUndoEnabled());
2940 if( bUndo )
2941 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
2943 CellPos aStart, aEnd;
2944 getSelectedCells( aStart, aEnd );
2945 const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2947 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2949 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2951 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2952 if( xCell.is() )
2954 if (bUndo)
2955 xCell->AddUndo();
2956 SdrText* pText = xCell.get();
2957 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
2962 if( bFrame )
2964 ApplyBorderAttr( rFormatSet );
2967 UpdateTableShape();
2969 if( bUndo )
2970 rModel.EndUndo();
2972 return true;
2976 IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
2978 mnUpdateEvent = nullptr;
2980 if( mbCellSelectionMode )
2982 CellPos aStart( maCursorFirstPos );
2983 CellPos aEnd( maCursorLastPos );
2984 checkCell(aStart);
2985 checkCell(aEnd);
2986 if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
2988 setSelectedCells( aStart, aEnd );
2992 updateSelectionOverlay();
2993 mbHasJustMerged = false;
2996 namespace
2999 struct LinesState
3001 LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
3002 : rBoxItem(rBoxItem_)
3003 , rBoxInfoItem(rBoxInfoItem_)
3004 , bDistanceIndeterminate(false)
3006 aBorderSet.fill(false);
3007 aInnerLineSet.fill(false);
3008 aBorderIndeterminate.fill(false);
3009 aInnerLineIndeterminate.fill(false);
3010 aDistanceSet.fill(false);
3011 aDistance.fill(0);
3014 SvxBoxItem& rBoxItem;
3015 SvxBoxInfoItem& rBoxInfoItem;
3016 o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
3017 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
3018 o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
3019 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
3020 o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
3021 o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
3022 bool bDistanceIndeterminate;
3025 class BoxItemWrapper
3027 public:
3028 BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
3030 const SvxBorderLine* getLine() const;
3031 void setLine(const SvxBorderLine* pLine);
3033 private:
3034 SvxBoxItem& m_rBoxItem;
3035 SvxBoxInfoItem& m_rBoxInfoItem;
3036 const SvxBoxItemLine m_nBorderLine;
3037 const SvxBoxInfoItemLine m_nInnerLine;
3038 const bool m_bBorder;
3041 BoxItemWrapper::BoxItemWrapper(
3042 SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
3043 const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
3044 : m_rBoxItem(rBoxItem)
3045 , m_rBoxInfoItem(rBoxInfoItem)
3046 , m_nBorderLine(nBorderLine)
3047 , m_nInnerLine(nInnerLine)
3048 , m_bBorder(bBorder)
3052 const SvxBorderLine* BoxItemWrapper::getLine() const
3054 if (m_bBorder)
3055 return m_rBoxItem.GetLine(m_nBorderLine);
3056 else
3057 return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
3060 void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
3062 if (m_bBorder)
3063 m_rBoxItem.SetLine(pLine, m_nBorderLine);
3064 else
3065 m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
3068 void lcl_MergeBorderLine(
3069 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3070 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
3072 const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
3073 BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
3074 bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
3076 if (rbSet)
3078 bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
3079 if (!rbIndeterminate)
3081 const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
3082 if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
3084 aBoxItem.setLine(nullptr);
3085 rbIndeterminate = true;
3089 else
3091 aBoxItem.setLine(pLine);
3092 rbSet = true;
3096 void lcl_MergeBorderOrInnerLine(
3097 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3098 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
3100 if (bBorder)
3101 lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
3102 else
3104 const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
3105 lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
3109 void lcl_MergeDistance(
3110 LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
3112 if (rLinesState.aDistanceSet[nIndex])
3114 if (!rLinesState.bDistanceIndeterminate)
3115 rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
3117 else
3119 rLinesState.aDistance[nIndex] = nDistance;
3120 rLinesState.aDistanceSet[nIndex] = true;
3124 void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
3126 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
3128 // current cell is outside the selection
3130 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
3132 if (nCellPosFlags & CellPosFlag::Upper)
3133 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
3134 else if (nCellPosFlags & CellPosFlag::Lower)
3135 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
3137 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
3139 if (nCellPosFlags & CellPosFlag::Before)
3140 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
3141 else if (nCellPosFlags & CellPosFlag::After)
3142 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
3145 // NOTE: inner distances for cells outside the selected range
3146 // are not relevant -> we ignore them.
3148 else
3150 // current cell is inside the selection
3152 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
3153 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
3154 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
3155 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
3157 lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
3158 lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
3159 lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
3160 lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
3166 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
3168 if( !mxTable.is() )
3169 return;
3171 const sal_Int32 nRowCount = mxTable->getRowCount();
3172 const sal_Int32 nColCount = mxTable->getColumnCount();
3173 if( !(nRowCount && nColCount) )
3174 return;
3176 CellPos aStart, aEnd;
3177 const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
3179 // We are adding one more row/column around the block of selected cells.
3180 // We will be checking the adjoining border of these too.
3181 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
3182 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
3184 rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
3185 LinesState aLinesState( rBoxItem, rBoxInfoItem );
3187 /* Here we go through all the selected cells (enhanced by
3188 * the adjoining row/column on each side) and determine the
3189 * lines for presentation. The algorithm is simple:
3190 * 1. if a border or inner line is set (or unset) in all
3191 * cells to the same value, it will be used.
3192 * 2. if a border or inner line is set only in some cells,
3193 * it will be set to indeterminate state (SetValid() on
3194 * rBoxInfoItem).
3196 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
3198 CellPosFlag nRowFlags = CellPosFlag::NONE;
3199 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
3200 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
3201 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
3202 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
3204 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
3206 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
3207 if( !xCell.is() )
3208 continue;
3210 CellPosFlag nCellPosFlags = nRowFlags;
3211 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
3212 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
3213 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
3214 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
3216 const SfxItemSet& rSet = xCell->GetItemSet();
3217 SvxBoxItem aCellBoxItem(TextDistancesToSvxBoxItem(rSet));
3218 lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
3222 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
3223 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
3224 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
3225 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
3226 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
3227 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
3228 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
3229 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
3230 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
3231 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
3232 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
3233 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
3235 if (!aLinesState.bDistanceIndeterminate)
3237 if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
3238 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
3239 if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
3240 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
3241 if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
3242 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
3243 if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
3244 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
3245 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
3249 bool SvxTableController::selectRow( sal_Int32 row )
3251 if( !mxTable.is() )
3252 return false;
3253 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3254 StartSelection( aEnd );
3255 gotoCell( aStart, true, nullptr );
3256 return true;
3259 bool SvxTableController::selectColumn( sal_Int32 column )
3261 if( !mxTable.is() )
3262 return false;
3263 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3264 StartSelection( aEnd );
3265 gotoCell( aStart, true, nullptr );
3266 return true;
3269 bool SvxTableController::deselectRow( sal_Int32 row )
3271 if( !mxTable.is() )
3272 return false;
3273 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3274 StartSelection( aEnd );
3275 gotoCell( aStart, false, nullptr );
3276 return true;
3279 bool SvxTableController::deselectColumn( sal_Int32 column )
3281 if( !mxTable.is() )
3282 return false;
3283 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3284 StartSelection( aEnd );
3285 gotoCell( aStart, false, nullptr );
3286 return true;
3289 bool SvxTableController::isRowSelected( sal_Int32 nRow )
3291 if( hasSelectedCells() )
3293 CellPos aFirstPos, aLastPos;
3294 getSelectedCells( aFirstPos, aLastPos );
3295 if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
3296 return true;
3298 return false;
3301 bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
3303 if( hasSelectedCells() )
3305 CellPos aFirstPos, aLastPos;
3306 getSelectedCells( aFirstPos, aLastPos );
3307 if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
3308 return true;
3310 return false;
3313 bool SvxTableController::isRowHeader()
3315 if(!checkTableObject())
3316 return false;
3318 SdrTableObj& rTableObj(*mxTableObj.get());
3319 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3321 return aSettings.mbUseFirstRow;
3324 bool SvxTableController::isColumnHeader()
3326 if(!checkTableObject())
3327 return false;
3329 SdrTableObj& rTableObj(*mxTableObj.get());
3330 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3332 return aSettings.mbUseFirstColumn;
3335 bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
3337 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
3338 if (pTableObj->GetObjIdentifier() != SdrObjKind::Table)
3339 return false;
3341 CellPos aCellPos;
3342 if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
3344 // Position is a table cell.
3345 if (mbCellSelectionMode)
3347 // We have a table selection already: adjust the point or the mark.
3348 if (bPoint)
3349 setSelectedCells(maCursorFirstPos, aCellPos);
3350 else
3351 setSelectedCells(aCellPos, maCursorLastPos);
3352 return true;
3354 else if (aCellPos != maMouseDownPos)
3356 // No selection, but rPosition is at another cell: start table selection.
3357 StartSelection(maMouseDownPos);
3358 // Update graphic selection, should be hidden now.
3359 mrView.AdjustMarkHdl();
3363 return false;
3368 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */