1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
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>
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
;
90 enum class CellPosFlag
// signals the relative position of a cell to a selection
92 NONE
= 0x0000, // not set or inside
108 { template<> struct typed_flags
<CellPosFlag
> : is_typed_flags
<CellPosFlag
, 0xff> {}; }
110 namespace sdr::table
{
112 class SvxTableControllerModifyListener
: public ::cppu::WeakImplHelper
< css::util::XModifyListener
>
115 explicit SvxTableControllerModifyListener( SvxTableController
* pController
)
116 : mpController( pController
) {}
119 virtual void SAL_CALL
modified( const css::lang::EventObject
& aEvent
) override
;
122 virtual void SAL_CALL
disposing( const css::lang::EventObject
& Source
) override
;
124 SvxTableController
* mpController
;
130 void SAL_CALL
SvxTableControllerModifyListener::modified( const css::lang::EventObject
& )
133 mpController
->onTableModified();
140 void SAL_CALL
SvxTableControllerModifyListener::disposing( const css::lang::EventObject
& )
142 mpController
= nullptr;
148 rtl::Reference
< sdr::SelectionController
> CreateTableController(
150 const SdrTableObj
& rObj
,
151 const rtl::Reference
< sdr::SelectionController
>& xRefController
)
153 return SvxTableController::create(rView
, rObj
, xRefController
);
157 rtl::Reference
< sdr::SelectionController
> SvxTableController::create(
159 const SdrTableObj
& rObj
,
160 const rtl::Reference
< sdr::SelectionController
>& xRefController
)
162 if( xRefController
.is() )
164 SvxTableController
* pController
= dynamic_cast< SvxTableController
* >( xRefController
.get() );
166 if(pController
&& (pController
->mxTableObj
.get() == &rObj
) && (&pController
->mrView
== &rView
))
168 return xRefController
;
172 return new SvxTableController(rView
, rObj
);
176 SvxTableController::SvxTableController(
178 const SdrTableObj
& rObj
)
179 : mbCellSelectionMode(false)
180 ,mbHasJustMerged(false)
181 ,mbLeftButtonDown(false)
183 ,mxTableObj(const_cast< SdrTableObj
* >(&rObj
))
184 ,mnUpdateEvent( nullptr )
186 rObj
.getActiveCellPos( maCursorFirstPos
);
187 maCursorLastPos
= maCursorFirstPos
;
189 mxTable
= mxTableObj
.get()->getUnoTable();
192 mxModifyListener
= new SvxTableControllerModifyListener( this );
193 mxTable
->addModifyListener( mxModifyListener
);
197 SvxTableController::~SvxTableController()
201 Application::RemoveUserEvent( mnUpdateEvent
);
204 if( mxModifyListener
.is() && mxTableObj
.get() )
206 rtl::Reference
< TableModel
> xTable( mxTableObj
.get()->getUnoTable() );
209 xTable
->removeModifyListener( mxModifyListener
);
210 mxModifyListener
.clear();
215 bool SvxTableController::onKeyInput(const KeyEvent
& rKEvt
, vcl::Window
* pWindow
)
217 if(!checkTableObject())
220 SdrTableObj
& rTableObj(*mxTableObj
.get());
221 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
223 // check if we are read only
224 if( rModel
.IsReadOnly())
226 switch( rKEvt
.GetKeyCode().GetCode() )
231 case awt::Key::RIGHT
:
239 case awt::Key::ESCAPE
:
243 // tell the view we eat the event, no further processing needed
248 TblAction nAction
= getKeyboardAction(rKEvt
);
250 return executeAction( nAction
, rKEvt
.GetKeyCode().IsShift(), pWindow
);
255 Point
pixelToLogic(const Point
& rPoint
, vcl::Window
const * pWindow
)
260 return pWindow
->PixelToLogic(rPoint
);
265 bool SvxTableController::onMouseButtonDown(const MouseEvent
& rMEvt
, vcl::Window
* pWindow
)
267 if (comphelper::LibreOfficeKit::isActive() && !pWindow
)
269 // Tiled rendering: get the window that has the disabled map mode.
270 if (OutputDevice
* pOutputDevice
= mrView
.GetFirstOutputDevice())
272 if (pOutputDevice
->GetOutDevType() == OUTDEV_WINDOW
)
273 pWindow
= pOutputDevice
->GetOwnerWindow();
277 if( !pWindow
|| !checkTableObject() )
281 if( !rMEvt
.IsRight() && mrView
.PickAnything(rMEvt
,SdrMouseEventKind::BUTTONDOWN
, aVEvt
) == SdrHitKind::Handle
)
284 TableHitKind eHit
= mxTableObj
.get()->CheckTableHit(pixelToLogic(rMEvt
.GetPosPixel(), pWindow
), maMouseDownPos
.mnCol
, maMouseDownPos
.mnRow
);
286 mbLeftButtonDown
= (rMEvt
.GetClicks() == 1) && rMEvt
.IsLeft();
288 if( eHit
== TableHitKind::Cell
)
290 StartSelection( maMouseDownPos
);
294 if( rMEvt
.IsRight() && eHit
!= TableHitKind::NONE
)
295 return true; // right click will become context menu
297 // for cell selection with the mouse remember our first hit
298 if( mbLeftButtonDown
)
302 SdrHdl
* pHdl
= mrView
.PickHandle(pixelToLogic(rMEvt
.GetPosPixel(), pWindow
));
306 mbLeftButtonDown
= false;
310 rtl::Reference
<sdr::table::SdrTableObj
> pTableObj
= mxTableObj
.get();
312 if (!pTableObj
|| eHit
== TableHitKind::NONE
)
314 mbLeftButtonDown
= false;
319 if (comphelper::LibreOfficeKit::isActive() && rMEvt
.GetClicks() == 2 && rMEvt
.IsLeft() && eHit
== TableHitKind::CellTextArea
)
321 bool bEmptyOutliner
= false;
322 if (Outliner
* pOutliner
= mrView
.GetTextEditOutliner())
324 if (pOutliner
->GetParagraphCount() == 1)
326 if (Paragraph
* pParagraph
= pOutliner
->GetParagraph(0))
327 bEmptyOutliner
= pOutliner
->GetText(pParagraph
).isEmpty();
332 // Tiled rendering: a left double-click in an empty cell: select it.
333 StartSelection(maMouseDownPos
);
334 setSelectedCells(maMouseDownPos
, maMouseDownPos
);
335 // Update graphic selection, should be hidden now.
336 mrView
.AdjustMarkHdl();
345 bool SvxTableController::onMouseButtonUp(const MouseEvent
& rMEvt
, vcl::Window
* /*pWin*/)
347 if( !checkTableObject() )
350 mbLeftButtonDown
= false;
352 return rMEvt
.GetClicks() == 2;
356 bool SvxTableController::onMouseMove(const MouseEvent
& rMEvt
, vcl::Window
* pWindow
)
358 if( !checkTableObject() )
361 rtl::Reference
<SdrTableObj
> pTableObj
= mxTableObj
.get();
363 if (mbLeftButtonDown
&& pTableObj
&& pTableObj
->CheckTableHit(pixelToLogic(rMEvt
.GetPosPixel(), pWindow
), aPos
.mnCol
, aPos
.mnRow
) != TableHitKind::NONE
)
365 if(aPos
!= maMouseDownPos
)
367 if( mbCellSelectionMode
)
369 setSelectedCells( maMouseDownPos
, aPos
);
374 StartSelection( maMouseDownPos
);
377 else if( mbCellSelectionMode
)
379 UpdateSelection( aPos
);
387 void SvxTableController::onSelectionHasChanged()
389 bool bSelected
= false;
391 rtl::Reference
<SdrTableObj
> pTableObj
= mxTableObj
.get();
392 if( pTableObj
&& pTableObj
->IsTextEditActive() )
394 pTableObj
->getActiveCellPos( maCursorFirstPos
);
395 maCursorLastPos
= maCursorFirstPos
;
396 mbCellSelectionMode
= false;
400 const SdrMarkList
& rMarkList
= mrView
.GetMarkedObjectList();
401 if( rMarkList
.GetMarkCount() == 1 )
402 bSelected
= mxTableObj
.get().get() == rMarkList
.GetMark(0)->GetMarkedSdrObj();
407 updateSelectionOverlay();
411 destroySelectionOverlay();
414 void SvxTableController::onSelectAll()
416 rtl::Reference
<sdr::table::SdrTableObj
> pTableObj
= mxTableObj
.get();
417 if ( pTableObj
&& !pTableObj
->IsTextEditActive())
424 void SvxTableController::GetState( SfxItemSet
& rSet
)
426 if(!mxTable
.is() || !mxTableObj
.get().is())
429 SdrTableObj
& rTableObj(*mxTableObj
.get());
430 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
431 std::optional
<SfxItemSet
> oSet
;
432 bool bVertDone(false);
434 // Iterate over all requested items in the set.
435 SfxWhichIter
aIter( rSet
);
436 sal_uInt16 nWhich
= aIter
.FirstWhich();
441 case SID_TABLE_VERT_BOTTOM
:
442 case SID_TABLE_VERT_CENTER
:
443 case SID_TABLE_VERT_NONE
:
449 oSet
.emplace(rModel
.GetItemPool());
450 MergeAttrFromSelectedCells(*oSet
, false);
453 SdrTextVertAdjust eAdj
= SDRTEXTVERTADJUST_BLOCK
;
455 if (oSet
->GetItemState( SDRATTR_TEXT_VERTADJUST
) != SfxItemState::INVALID
)
456 eAdj
= oSet
->Get(SDRATTR_TEXT_VERTADJUST
).GetValue();
458 rSet
.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM
, eAdj
== SDRTEXTVERTADJUST_BOTTOM
));
459 rSet
.Put(SfxBoolItem(SID_TABLE_VERT_CENTER
, eAdj
== SDRTEXTVERTADJUST_CENTER
));
460 rSet
.Put(SfxBoolItem(SID_TABLE_VERT_NONE
, eAdj
== SDRTEXTVERTADJUST_TOP
));
465 case SID_TABLE_DELETE_ROW
:
466 if( !mxTable
.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable
->getRowCount() <= 1) )
467 rSet
.DisableItem(SID_TABLE_DELETE_ROW
);
469 case SID_TABLE_DELETE_COL
:
470 if( !mxTable
.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable
->getColumnCount() <= 1) )
471 rSet
.DisableItem(SID_TABLE_DELETE_COL
);
473 case SID_TABLE_DELETE_TABLE
:
475 rSet
.DisableItem(SID_TABLE_DELETE_TABLE
);
477 case SID_TABLE_MERGE_CELLS
:
478 if( !mxTable
.is() || !hasSelectedCells() )
479 rSet
.DisableItem(SID_TABLE_MERGE_CELLS
);
481 case SID_TABLE_SPLIT_CELLS
:
482 if( !hasSelectedCells() || !mxTable
.is() )
483 rSet
.DisableItem(SID_TABLE_SPLIT_CELLS
);
486 case SID_TABLE_OPTIMAL_ROW_HEIGHT
:
487 case SID_TABLE_DISTRIBUTE_COLUMNS
:
488 case SID_TABLE_DISTRIBUTE_ROWS
:
490 bool bDistributeColumns
= false;
491 bool bDistributeRows
= false;
494 CellPos aStart
, aEnd
;
495 getSelectedCells( aStart
, aEnd
);
497 bDistributeColumns
= aStart
.mnCol
!= aEnd
.mnCol
;
498 bDistributeRows
= aStart
.mnRow
!= aEnd
.mnRow
;
500 if( !bDistributeColumns
)
501 rSet
.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS
);
502 if( !bDistributeRows
)
504 rSet
.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT
);
505 rSet
.DisableItem(SID_TABLE_DISTRIBUTE_ROWS
);
513 nWhich
= aIter
.NextWhich();
518 void SvxTableController::onInsert( sal_uInt16 nSId
, const SfxItemSet
* pArgs
)
520 if(!checkTableObject())
523 SdrTableObj
& rTableObj(*mxTableObj
.get());
524 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
525 bool bInsertAfter
= true;
526 sal_uInt16 nCount
= 0;
530 const SfxPoolItem
* pItem
= nullptr;
531 pArgs
->GetItemState(nSId
, false, &pItem
);
534 nCount
= static_cast<const SfxInt16Item
*>(pItem
)->GetValue();
535 if(const SfxBoolItem
* pItem2
= pArgs
->GetItemIfSet(SID_TABLE_PARAM_INSERT_AFTER
))
536 bInsertAfter
= pItem2
->GetValue();
540 CellPos aStart
, aEnd
;
541 if( hasSelectedCells() )
543 getSelectedCells( aStart
, aEnd
);
549 aStart
.mnCol
= mxTable
->getColumnCount() - 1;
550 aStart
.mnRow
= mxTable
->getRowCount() - 1;
555 if( rTableObj
.IsTextEditActive() )
556 mrView
.SdrEndTextEdit(true);
560 static constexpr OUString
sSize( u
"Size"_ustr
);
561 const bool bUndo(rModel
.IsUndoEnabled());
565 case SID_TABLE_INSERT_COL
:
567 TableModelNotifyGuard
aGuard( mxTable
.get() );
571 rModel
.BegUndo( SvxResId(STR_TABLE_INSCOL
) );
572 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
));
575 Reference
< XTableColumns
> xCols( mxTable
->getColumns() );
576 const sal_Int32 nNewColumns
= (nCount
== 0) ? (aEnd
.mnCol
- aStart
.mnCol
+ 1) : nCount
;
577 const sal_Int32 nNewStartColumn
= aEnd
.mnCol
+ (bInsertAfter
? 1 : 0);
578 xCols
->insertByIndex( nNewStartColumn
, nNewColumns
);
580 for( sal_Int32 nOffset
= 0; nOffset
< nNewColumns
; nOffset
++ )
582 // Resolves fdo#61540
583 // On Insert before, the reference column whose size is going to be
584 // used for newly created column(s) is wrong. As the new columns are
585 // inserted before the reference column, the reference column moved
586 // to the new position by no., of new columns i.e (earlier+newcolumns).
587 Reference
< XPropertySet
>(xCols
->getByIndex(nNewStartColumn
+nOffset
), UNO_QUERY_THROW
)->
588 setPropertyValue( sSize
,
589 Reference
< XPropertySet
>(xCols
->getByIndex( bInsertAfter
?nNewStartColumn
-1:nNewStartColumn
+nNewColumns
), UNO_QUERY_THROW
)->
590 getPropertyValue( sSize
) );
593 // Copy cell properties
594 sal_Int32 nPropSrcCol
= (bInsertAfter
? aEnd
.mnCol
: aStart
.mnCol
+ nNewColumns
);
595 sal_Int32 nRowSpan
= 0;
596 bool bNewSpan
= false;
598 for( sal_Int32 nRow
= 0; nRow
< mxTable
->getRowCount(); ++nRow
)
600 CellRef
xSourceCell( mxTable
->getCell( nPropSrcCol
, nRow
) );
602 // When we insert new COLUMNs, we want to copy ROW spans.
603 if (xSourceCell
.is() && nRowSpan
== 0)
605 // we are not in a span yet. Let's find out if the current cell is in a span.
606 sal_Int32 nColSpan
= sal_Int32();
607 sal_Int32 nSpanInfoCol
= sal_Int32();
609 if( xSourceCell
->getRowSpan() > 1 )
611 // The current cell is the top-left cell in a span.
612 // Get the span info and propagate it to the target.
613 nRowSpan
= xSourceCell
->getRowSpan();
614 nColSpan
= xSourceCell
->getColumnSpan();
615 nSpanInfoCol
= nPropSrcCol
;
617 else if( xSourceCell
->isMerged() )
619 // The current cell is a middle cell in a 2D span.
620 // Look for the top-left cell in the span.
621 for( nSpanInfoCol
= nPropSrcCol
- 1; nSpanInfoCol
>= 0; --nSpanInfoCol
)
623 CellRef
xMergeInfoCell( mxTable
->getCell( nSpanInfoCol
, nRow
) );
624 if (xMergeInfoCell
.is() && !xMergeInfoCell
->isMerged())
626 nRowSpan
= xMergeInfoCell
->getRowSpan();
627 nColSpan
= xMergeInfoCell
->getColumnSpan();
635 // The target columns are outside the span; Start a new span.
636 if( nRowSpan
> 0 && ( nNewStartColumn
< nSpanInfoCol
|| nSpanInfoCol
+ nColSpan
<= nNewStartColumn
) )
640 // Now copy the properties from the source to the targets
641 for( sal_Int32 nOffset
= 0; nOffset
< nNewColumns
; nOffset
++ )
643 CellRef
xTargetCell( mxTable
->getCell( nNewStartColumn
+ nOffset
, nRow
) );
644 if( xTargetCell
.is() )
649 xTargetCell
->merge( 1, nRowSpan
);
651 xTargetCell
->setMerged();
653 xTargetCell
->copyFormatFrom( xSourceCell
);
667 aStart
.mnCol
= nNewStartColumn
;
669 aEnd
.mnCol
= aStart
.mnCol
+ nNewColumns
- 1;
670 aEnd
.mnRow
= mxTable
->getRowCount() - 1;
674 case SID_TABLE_INSERT_ROW
:
676 TableModelNotifyGuard
aGuard( mxTable
.get() );
680 rModel
.BegUndo( SvxResId(STR_TABLE_INSROW
) );
681 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
));
684 Reference
< XTableRows
> xRows( mxTable
->getRows() );
685 const sal_Int32 nNewRows
= (nCount
== 0) ? (aEnd
.mnRow
- aStart
.mnRow
+ 1) : nCount
;
686 const sal_Int32 nNewRowStart
= aEnd
.mnRow
+ (bInsertAfter
? 1 : 0);
687 xRows
->insertByIndex( nNewRowStart
, nNewRows
);
689 for( sal_Int32 nOffset
= 0; nOffset
< nNewRows
; nOffset
++ )
691 Reference
< XPropertySet
>( xRows
->getByIndex( aEnd
.mnRow
+ nOffset
+ 1 ), UNO_QUERY_THROW
)->
692 setPropertyValue( sSize
,
693 Reference
< XPropertySet
>( xRows
->getByIndex( aStart
.mnRow
+ nOffset
), UNO_QUERY_THROW
)->
694 getPropertyValue( sSize
) );
697 // Copy the cell properties
698 sal_Int32 nPropSrcRow
= (bInsertAfter
? aEnd
.mnRow
: aStart
.mnRow
+ nNewRows
);
699 sal_Int32 nColSpan
= 0;
700 bool bNewSpan
= false;
702 for( sal_Int32 nCol
= 0; nCol
< mxTable
->getColumnCount(); ++nCol
)
704 CellRef
xSourceCell( mxTable
->getCell( nCol
, nPropSrcRow
) );
706 if (!xSourceCell
.is())
709 // When we insert new ROWs, we want to copy COLUMN spans.
712 // we are not in a span yet. Let's find out if the current cell is in a span.
713 sal_Int32 nRowSpan
= sal_Int32();
714 sal_Int32 nSpanInfoRow
= sal_Int32();
716 if( xSourceCell
->getColumnSpan() > 1 )
718 // The current cell is the top-left cell in a span.
719 // Get the span info and propagate it to the target.
720 nColSpan
= xSourceCell
->getColumnSpan();
721 nRowSpan
= xSourceCell
->getRowSpan();
722 nSpanInfoRow
= nPropSrcRow
;
724 else if( xSourceCell
->isMerged() )
726 // The current cell is a middle cell in a 2D span.
727 // Look for the top-left cell in the span.
728 for( nSpanInfoRow
= nPropSrcRow
- 1; nSpanInfoRow
>= 0; --nSpanInfoRow
)
730 CellRef
xMergeInfoCell( mxTable
->getCell( nCol
, nSpanInfoRow
) );
731 if (xMergeInfoCell
.is() && !xMergeInfoCell
->isMerged())
733 nColSpan
= xMergeInfoCell
->getColumnSpan();
734 nRowSpan
= xMergeInfoCell
->getRowSpan();
742 // Inserted rows are outside the span; Start a new span.
743 if( nColSpan
> 0 && ( nNewRowStart
< nSpanInfoRow
|| nSpanInfoRow
+ nRowSpan
<= nNewRowStart
) )
747 // Now copy the properties from the source to the targets
748 for( sal_Int32 nOffset
= 0; nOffset
< nNewRows
; ++nOffset
)
750 CellRef
xTargetCell( mxTable
->getCell( nCol
, nNewRowStart
+ nOffset
) );
751 if( xTargetCell
.is() )
756 xTargetCell
->merge( nColSpan
, 1 );
758 xTargetCell
->setMerged();
760 xTargetCell
->copyFormatFrom( xSourceCell
);
775 aStart
.mnRow
= nNewRowStart
;
776 aEnd
.mnCol
= mxTable
->getColumnCount() - 1;
777 aEnd
.mnRow
= aStart
.mnRow
+ nNewRows
- 1;
782 StartSelection( aStart
);
783 UpdateSelection( aEnd
);
787 void SvxTableController::onDelete( sal_uInt16 nSId
)
789 rtl::Reference
<sdr::table::SdrTableObj
> pTableObj
= mxTableObj
.get();
790 if( !pTableObj
|| !mxTable
.is() )
793 if( nSId
== SID_TABLE_DELETE_TABLE
)
795 if( pTableObj
->IsTextEditActive() )
796 mrView
.SdrEndTextEdit(true);
798 mrView
.DeleteMarkedObj();
800 else if( hasSelectedCells() )
802 CellPos aStart
, aEnd
;
803 getSelectedCells( aStart
, aEnd
);
805 if( pTableObj
->IsTextEditActive() )
806 mrView
.SdrEndTextEdit(true);
810 bool bDeleteTable
= false;
813 case SID_TABLE_DELETE_COL
:
815 const sal_Int32 nRemovedColumns
= aEnd
.mnCol
- aStart
.mnCol
+ 1;
816 if( nRemovedColumns
== mxTable
->getColumnCount() )
822 Reference
< XTableColumns
> xCols( mxTable
->getColumns() );
823 xCols
->removeByIndex( aStart
.mnCol
, nRemovedColumns
);
824 EditCell(aStart
, nullptr, TblAction::NONE
);
829 case SID_TABLE_DELETE_ROW
:
831 const sal_Int32 nRemovedRows
= aEnd
.mnRow
- aStart
.mnRow
+ 1;
832 if( nRemovedRows
== mxTable
->getRowCount() )
838 Reference
< XTableRows
> xRows( mxTable
->getRows() );
839 xRows
->removeByIndex( aStart
.mnRow
, nRemovedRows
);
840 EditCell(aStart
, nullptr, TblAction::NONE
);
847 mrView
.DeleteMarkedObj();
854 void SvxTableController::onSelect( sal_uInt16 nSId
)
859 const sal_Int32 nRowCount
= mxTable
->getRowCount();
860 const sal_Int32 nColCount
= mxTable
->getColumnCount();
861 if( !(nRowCount
&& nColCount
) )
864 CellPos aStart
, aEnd
;
865 getSelectedCells( aStart
, aEnd
);
869 case SID_TABLE_SELECT_ALL
:
870 aEnd
.mnCol
= 0; aEnd
.mnRow
= 0;
871 aStart
.mnCol
= nColCount
- 1; aStart
.mnRow
= nRowCount
- 1;
873 case SID_TABLE_SELECT_COL
:
874 aEnd
.mnRow
= nRowCount
- 1;
877 case SID_TABLE_SELECT_ROW
:
878 aEnd
.mnCol
= nColCount
- 1;
883 StartSelection( aEnd
);
884 gotoCell( aStart
, true, nullptr );
887 SvxBoxItem
SvxTableController::TextDistancesToSvxBoxItem(const SfxItemSet
& rAttrSet
)
889 // merge drawing layer text distance items into SvxBoxItem used by the dialog
890 SvxBoxItem
aBoxItem( rAttrSet
.Get( SDRATTR_TABLE_BORDER
) );
891 aBoxItem
.SetDistance( sal::static_int_cast
< sal_uInt16
>( rAttrSet
.Get(SDRATTR_TEXT_LEFTDIST
).GetValue()), SvxBoxItemLine::LEFT
);
892 aBoxItem
.SetDistance( sal::static_int_cast
< sal_uInt16
>( rAttrSet
.Get(SDRATTR_TEXT_RIGHTDIST
).GetValue()), SvxBoxItemLine::RIGHT
);
893 aBoxItem
.SetDistance( sal::static_int_cast
< sal_uInt16
>( rAttrSet
.Get(SDRATTR_TEXT_UPPERDIST
).GetValue()), SvxBoxItemLine::TOP
);
894 aBoxItem
.SetDistance( sal::static_int_cast
< sal_uInt16
>( rAttrSet
.Get(SDRATTR_TEXT_LOWERDIST
).GetValue()), SvxBoxItemLine::BOTTOM
);
898 void SvxTableController::SvxBoxItemToTextDistances(const SvxBoxItem
& pOriginalItem
, SfxItemSet
& rAttrSet
)
900 const SvxBoxItem
* pNewItem( rAttrSet
.GetItemIfSet( SDRATTR_TABLE_BORDER
) );
904 if( pNewItem
->GetDistance( SvxBoxItemLine::LEFT
) != pOriginalItem
.GetDistance( SvxBoxItemLine::LEFT
) )
905 rAttrSet
.Put(makeSdrTextLeftDistItem( pNewItem
->GetDistance( SvxBoxItemLine::LEFT
) ) );
907 if( pNewItem
->GetDistance( SvxBoxItemLine::RIGHT
) != pOriginalItem
.GetDistance( SvxBoxItemLine::RIGHT
) )
908 rAttrSet
.Put(makeSdrTextRightDistItem( pNewItem
->GetDistance( SvxBoxItemLine::RIGHT
) ) );
910 if( pNewItem
->GetDistance( SvxBoxItemLine::TOP
) != pOriginalItem
.GetDistance( SvxBoxItemLine::TOP
) )
911 rAttrSet
.Put(makeSdrTextUpperDistItem( pNewItem
->GetDistance( SvxBoxItemLine::TOP
) ) );
913 if( pNewItem
->GetDistance( SvxBoxItemLine::BOTTOM
) != pOriginalItem
.GetDistance( SvxBoxItemLine::BOTTOM
) )
914 rAttrSet
.Put(makeSdrTextLowerDistItem( pNewItem
->GetDistance( SvxBoxItemLine::BOTTOM
) ) );
917 void SvxTableController::onFormatTable(const SfxRequest
& rReq
)
919 if(!mxTableObj
.get().is())
922 SdrTableObj
& rTableObj(*mxTableObj
.get());
923 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
924 const SfxItemSet
* pArgs
= rReq
.GetArgs();
929 SfxItemSet
aNewAttr(rModel
.GetItemPool());
931 // merge drawing layer text distance items into SvxBoxItem used by the dialog
932 auto xBoxItem(std::make_shared
<SvxBoxItem
>(TextDistancesToSvxBoxItem(aNewAttr
)));
933 auto xBoxInfoItem(std::make_shared
<SvxBoxInfoItem
>(aNewAttr
.Get(SDRATTR_TABLE_BORDER_INNER
)));
935 MergeAttrFromSelectedCells(aNewAttr
, false);
936 FillCommonBorderAttrFromSelectedCells(*xBoxItem
, *xBoxInfoItem
);
937 aNewAttr
.Put(*xBoxItem
);
938 aNewAttr
.Put(*xBoxInfoItem
);
940 // Fill in shadow properties.
941 const SfxItemSet
& rTableItemSet
= rTableObj
.GetMergedItemSet();
942 for (sal_uInt16 nWhich
= SDRATTR_SHADOW_FIRST
; nWhich
<= SDRATTR_SHADOW_LAST
; ++nWhich
)
944 if (rTableItemSet
.GetItemState(nWhich
, false) != SfxItemState::SET
)
949 aNewAttr
.Put(rTableItemSet
.Get(nWhich
));
952 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
953 VclPtr
<SfxAbstractTabDialog
> xDlg( pFact
->CreateSvxFormatCellsDialog(
958 // Even Cancel Button is returning positive(101) value,
959 xDlg
->StartExecuteAsync([xDlg
, this, xBoxItem
=std::move(xBoxItem
),
960 xBoxInfoItem
=std::move(xBoxInfoItem
)](int nResult
){
961 if (nResult
== RET_OK
)
963 SfxItemSet
aNewSet(*(xDlg
->GetOutputItemSet()));
965 //Only properties that were unchanged by the dialog appear in this
966 //itemset. We had constructed these two properties from other
967 //ones, so if they were not changed, then forcible set them back to
968 //their originals in the new result set so we can decompose that
969 //unchanged state back to their input properties
970 if (aNewSet
.GetItemState(SDRATTR_TABLE_BORDER
, false) != SfxItemState::SET
)
972 aNewSet
.Put(*xBoxItem
);
974 if (aNewSet
.GetItemState(SDRATTR_TABLE_BORDER_INNER
, false) != SfxItemState::SET
)
976 aNewSet
.Put(*xBoxInfoItem
);
979 SvxBoxItemToTextDistances(*xBoxItem
, aNewSet
);
981 if (checkTableObject() && mxTable
.is())
983 // Create a single undo action when applying the result of the dialog.
984 SdrTableObj
& rTableObject(*mxTableObj
.get());
985 SdrModel
& rSdrModel(rTableObject
.getSdrModelFromSdrObject());
986 bool bUndo
= rSdrModel
.IsUndoEnabled() && !mrView
.IsTextEdit();
989 rSdrModel
.BegUndo(SvxResId(STR_TABLE_NUMFORMAT
));
992 this->SetAttrToSelectedCells(aNewSet
, false);
994 this->SetAttrToSelectedShape(aNewSet
);
1003 xDlg
->disposeOnce();
1007 void SvxTableController::Execute( SfxRequest
& rReq
)
1009 const sal_uInt16 nSId
= rReq
.GetSlot();
1012 case SID_TABLE_INSERT_ROW
:
1013 case SID_TABLE_INSERT_COL
:
1014 onInsert( nSId
, rReq
.GetArgs() );
1016 case SID_TABLE_DELETE_ROW
:
1017 case SID_TABLE_DELETE_COL
:
1018 case SID_TABLE_DELETE_TABLE
:
1021 case SID_TABLE_SELECT_ALL
:
1022 case SID_TABLE_SELECT_COL
:
1023 case SID_TABLE_SELECT_ROW
:
1026 case SID_FORMAT_TABLE_DLG
:
1027 onFormatTable( rReq
);
1030 case SID_FRAME_LINESTYLE
:
1031 case SID_FRAME_LINECOLOR
:
1032 case SID_ATTR_BORDER
:
1034 const SfxItemSet
* pArgs
= rReq
.GetArgs();
1036 ApplyBorderAttr( *pArgs
);
1040 case SID_ATTR_FILL_STYLE
:
1042 const SfxItemSet
* pArgs
= rReq
.GetArgs();
1044 SetAttributes( *pArgs
, false );
1048 case SID_TABLE_MERGE_CELLS
:
1052 case SID_TABLE_SPLIT_CELLS
:
1053 SplitMarkedCells(rReq
);
1056 case SID_TABLE_MINIMAL_COLUMN_WIDTH
:
1057 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
1060 case SID_TABLE_OPTIMAL_COLUMN_WIDTH
:
1061 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
1064 case SID_TABLE_DISTRIBUTE_COLUMNS
:
1065 DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
1068 case SID_TABLE_MINIMAL_ROW_HEIGHT
:
1069 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
1072 case SID_TABLE_OPTIMAL_ROW_HEIGHT
:
1073 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
1076 case SID_TABLE_DISTRIBUTE_ROWS
:
1077 DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
1080 case SID_TABLE_VERT_BOTTOM
:
1081 case SID_TABLE_VERT_CENTER
:
1082 case SID_TABLE_VERT_NONE
:
1083 SetVertical( nSId
);
1086 case SID_AUTOFORMAT
:
1087 case SID_TABLE_SORT_DIALOG
:
1088 case SID_TABLE_AUTOSUM
:
1092 case SID_TABLE_STYLE
:
1093 SetTableStyle( rReq
.GetArgs() );
1096 case SID_TABLE_STYLE_SETTINGS
:
1097 SetTableStyleSettings( rReq
.GetArgs() );
1099 case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION
:
1100 changeTableEdge(rReq
);
1105 void SvxTableController::SetTableStyle( const SfxItemSet
* pArgs
)
1107 if(!checkTableObject())
1110 SdrTableObj
& rTableObj(*mxTableObj
.get());
1111 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1113 if(!pArgs
|| (SfxItemState::SET
!= pArgs
->GetItemState(SID_TABLE_STYLE
, false)))
1116 const SfxStringItem
* pArg
= &pArgs
->Get( SID_TABLE_STYLE
);
1117 if( !(pArg
&& mxTable
.is()) )
1122 Reference
< XStyleFamiliesSupplier
> xSFS( rModel
.getUnoModel(), UNO_QUERY_THROW
);
1123 Reference
< XNameAccess
> xFamilyNameAccess( xSFS
->getStyleFamilies(), UNO_SET_THROW
);
1124 Reference
< XNameAccess
> xTableFamilyAccess( xFamilyNameAccess
->getByName( u
"table"_ustr
), UNO_QUERY_THROW
);
1126 if( xTableFamilyAccess
->hasByName( pArg
->GetValue() ) )
1128 // found table style with the same name
1129 Reference
< XIndexAccess
> xNewTableStyle( xTableFamilyAccess
->getByName( pArg
->GetValue() ), UNO_QUERY_THROW
);
1131 const bool bUndo
= rModel
.IsUndoEnabled();
1135 rModel
.BegUndo(SvxResId(STR_TABLE_STYLE
));
1136 rModel
.AddUndo(std::make_unique
<TableStyleUndo
>(rTableObj
));
1139 rTableObj
.setTableStyle( xNewTableStyle
);
1141 const sal_Int32 nRowCount
= mxTable
->getRowCount();
1142 const sal_Int32 nColCount
= mxTable
->getColumnCount();
1143 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; nRow
++ )
1145 for( sal_Int32 nCol
= 0; nCol
< nColCount
; nCol
++ ) try
1147 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
1150 SfxItemSet
aSet( xCell
->GetItemSet() );
1151 bool bChanges
= false;
1152 SfxStyleSheet
*pStyleSheet
= xCell
->GetStyleSheet();
1153 SAL_WARN_IF(!pStyleSheet
, "svx", "no stylesheet for table cell?");
1156 const SfxItemSet
& rStyleAttribs
= pStyleSheet
->GetItemSet();
1158 for ( sal_uInt16 nWhich
= SDRATTR_START
; nWhich
<= SDRATTR_TABLE_LAST
; nWhich
++ )
1160 if( (rStyleAttribs
.GetItemState( nWhich
) == SfxItemState::SET
) && (aSet
.GetItemState( nWhich
) == SfxItemState::SET
) )
1162 aSet
.ClearItem( nWhich
);
1173 xCell
->SetMergedItemSetAndBroadcast( aSet
, true );
1179 TOOLS_WARN_EXCEPTION("svx.table", "");
1189 TOOLS_WARN_EXCEPTION("svx.table", "");
1193 void SvxTableController::SetTableStyleSettings( const SfxItemSet
* pArgs
)
1195 if(!checkTableObject())
1198 SdrTableObj
& rTableObj(*mxTableObj
.get());
1199 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1201 TableStyleSettings
aSettings(rTableObj
.getTableStyleSettings() );
1202 const SfxBoolItem
*pPoolItem
=nullptr;
1204 if( (pPoolItem
= pArgs
->GetItemIfSet(ID_VAL_USEFIRSTROWSTYLE
, false)) )
1205 aSettings
.mbUseFirstRow
= pPoolItem
->GetValue();
1207 if( (pPoolItem
= pArgs
->GetItemIfSet(ID_VAL_USELASTROWSTYLE
, false)) )
1208 aSettings
.mbUseLastRow
= pPoolItem
->GetValue();
1210 if( (pPoolItem
= pArgs
->GetItemIfSet(ID_VAL_USEBANDINGROWSTYLE
, false)) )
1211 aSettings
.mbUseRowBanding
= pPoolItem
->GetValue();
1213 if( (pPoolItem
= pArgs
->GetItemIfSet(ID_VAL_USEFIRSTCOLUMNSTYLE
, false)) )
1214 aSettings
.mbUseFirstColumn
= pPoolItem
->GetValue();
1216 if( (pPoolItem
= pArgs
->GetItemIfSet(ID_VAL_USELASTCOLUMNSTYLE
, false)) )
1217 aSettings
.mbUseLastColumn
= pPoolItem
->GetValue();
1219 if( (pPoolItem
= pArgs
->GetItemIfSet(ID_VAL_USEBANDINGCOLUMNSTYLE
, false)) )
1220 aSettings
.mbUseColumnBanding
= pPoolItem
->GetValue();
1222 if( aSettings
== rTableObj
.getTableStyleSettings() )
1225 const bool bUndo(rModel
.IsUndoEnabled());
1229 rModel
.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS
) );
1230 rModel
.AddUndo(std::make_unique
<TableStyleUndo
>(rTableObj
));
1233 rTableObj
.setTableStyleSettings( aSettings
);
1239 void SvxTableController::SetVertical( sal_uInt16 nSId
)
1241 if(!checkTableObject())
1244 SdrTableObj
& rTableObj(*mxTableObj
.get());
1245 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1247 TableModelNotifyGuard
aGuard( mxTable
.get() );
1248 const bool bUndo(rModel
.IsUndoEnabled());
1252 rModel
.BegUndo(SvxResId(STR_TABLE_NUMFORMAT
));
1253 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj
));
1256 CellPos aStart
, aEnd
;
1257 getSelectedCells( aStart
, aEnd
);
1259 SdrTextVertAdjust eAdj
= SDRTEXTVERTADJUST_TOP
;
1263 case SID_TABLE_VERT_BOTTOM
:
1264 eAdj
= SDRTEXTVERTADJUST_BOTTOM
;
1266 case SID_TABLE_VERT_CENTER
:
1267 eAdj
= SDRTEXTVERTADJUST_CENTER
;
1269 //case SID_TABLE_VERT_NONE:
1274 SdrTextVertAdjustItem
aItem( eAdj
);
1276 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
1278 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
1280 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
1285 SfxItemSet
aSet(xCell
->GetItemSet());
1287 xCell
->SetMergedItemSetAndBroadcast(aSet
, /*bClearAllItems=*/false);
1298 void SvxTableController::MergeMarkedCells()
1300 CellPos aStart
, aEnd
;
1301 getSelectedCells( aStart
, aEnd
);
1302 rtl::Reference
<SdrTableObj
> pTableObj
= mxTableObj
.get();
1305 if( pTableObj
->IsTextEditActive() )
1306 mrView
.SdrEndTextEdit(true);
1308 TableModelNotifyGuard
aGuard( mxTable
.get() );
1309 MergeRange( aStart
.mnCol
, aStart
.mnRow
, aEnd
.mnCol
, aEnd
.mnRow
);
1313 void SvxTableController::SplitMarkedCells(const SfxRequest
& rReq
)
1315 if(!checkTableObject() || !mxTable
.is())
1318 SvxAbstractDialogFactory
* pFact
= SvxAbstractDialogFactory::Create();
1319 VclPtr
<SvxAbstractSplitTableDialog
> xDlg(pFact
->CreateSvxSplitTableDialog(rReq
.GetFrameWeld(), false, 99));
1321 xDlg
->StartExecuteAsync([xDlg
, this](int) {
1322 const sal_Int32 nCount
= xDlg
->GetCount() - 1;
1327 CellPos aStart
, aEnd
;
1328 getSelectedCells( aStart
, aEnd
);
1329 Reference
< XMergeableCellRange
> xRange( mxTable
->createCursorByRange( mxTable
->getCellRangeByPosition( aStart
.mnCol
, aStart
.mnRow
, aEnd
.mnCol
, aEnd
.mnRow
) ), UNO_QUERY_THROW
);
1330 const sal_Int32 nRowCount
= mxTable
->getRowCount();
1331 const sal_Int32 nColCount
= mxTable
->getColumnCount();
1332 SdrTableObj
& rTableObj(*mxTableObj
.get());
1334 if( rTableObj
.IsTextEditActive() )
1335 mrView
.SdrEndTextEdit(true);
1337 TableModelNotifyGuard
aGuard( mxTable
.get() );
1338 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1339 const bool bUndo(rModel
.IsUndoEnabled());
1343 rModel
.BegUndo( SvxResId(STR_TABLE_SPLIT
) );
1344 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
));
1347 if( xDlg
->IsHorizontal() )
1349 xRange
->split( 0, nCount
);
1353 xRange
->split( nCount
, 0 );
1359 aEnd
.mnRow
+= mxTable
->getRowCount() - nRowCount
;
1360 aEnd
.mnCol
+= mxTable
->getColumnCount() - nColCount
;
1362 setSelectedCells( aStart
, aEnd
);
1364 xDlg
->disposeOnce();
1368 void SvxTableController::DistributeColumns(const bool bOptimize
, const bool bMinimize
)
1370 if(!checkTableObject())
1373 SdrTableObj
& rTableObj(*mxTableObj
.get());
1374 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1375 const bool bUndo(rModel
.IsUndoEnabled());
1379 rModel
.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS
) );
1380 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
));
1383 CellPos aStart
, aEnd
;
1384 getSelectedCells( aStart
, aEnd
);
1385 rTableObj
.DistributeColumns( aStart
.mnCol
, aEnd
.mnCol
, bOptimize
, bMinimize
);
1391 void SvxTableController::DistributeRows(const bool bOptimize
, const bool bMinimize
)
1393 if(!checkTableObject())
1396 SdrTableObj
& rTableObj(*mxTableObj
.get());
1397 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1398 const bool bUndo(rModel
.IsUndoEnabled());
1402 rModel
.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS
) );
1403 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
));
1406 CellPos aStart
, aEnd
;
1407 getSelectedCells( aStart
, aEnd
);
1408 rTableObj
.DistributeRows( aStart
.mnRow
, aEnd
.mnRow
, bOptimize
, bMinimize
);
1414 bool SvxTableController::HasMarked() const
1416 return mbCellSelectionMode
&& mxTable
.is();
1419 bool SvxTableController::DeleteMarked()
1421 if(!checkTableObject() || !HasMarked())
1424 SdrTableObj
& rTableObj(*mxTableObj
.get());
1425 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1426 const bool bUndo(rModel
.IsUndoEnabled());
1427 bool bDeleteTable
= false;
1430 rModel
.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS
));
1432 CellPos aStart
, aEnd
;
1433 getSelectedCells( aStart
, aEnd
);
1434 const sal_Int32 nRemovedColumns
= aEnd
.mnCol
- aStart
.mnCol
+ 1;
1435 const sal_Int32 nRemovedRows
= aEnd
.mnRow
- aStart
.mnRow
+ 1;
1436 if( nRemovedColumns
== mxTable
->getColumnCount() && nRemovedRows
== mxTable
->getRowCount())
1438 bDeleteTable
= true;
1442 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
1444 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
1446 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
1447 if (xCell
.is() && xCell
->hasText())
1451 xCell
->SetOutlinerParaObject(std::nullopt
);
1458 mrView
.DeleteMarkedObj();
1468 bool SvxTableController::GetStyleSheet( SfxStyleSheet
*& rpStyleSheet
) const
1470 if( hasSelectedCells() )
1472 rpStyleSheet
= nullptr;
1476 SfxStyleSheet
* pRet
=nullptr;
1479 CellPos aStart
, aEnd
;
1480 const_cast<SvxTableController
&>(*this).getSelectedCells( aStart
, aEnd
);
1482 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
1484 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
1486 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
1489 SfxStyleSheet
* pSS
=xCell
->GetStyleSheet();
1494 else if(pRet
!= pSS
)
1502 rpStyleSheet
= pRet
;
1509 bool SvxTableController::SetStyleSheet( SfxStyleSheet
* pStyleSheet
, bool bDontRemoveHardAttr
)
1511 if( hasSelectedCells() && (!pStyleSheet
|| pStyleSheet
->GetFamily() == SfxStyleFamily::Frame
) )
1515 CellPos aStart
, aEnd
;
1516 getSelectedCells( aStart
, aEnd
);
1518 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
1520 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
1522 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
1524 xCell
->SetStyleSheet(pStyleSheet
,bDontRemoveHardAttr
);
1535 void SvxTableController::changeTableEdge(const SfxRequest
& rReq
)
1537 if (!checkTableObject())
1540 const auto* pType
= rReq
.GetArg
<SfxStringItem
>(SID_TABLE_BORDER_TYPE
);
1541 const auto* pIndex
= rReq
.GetArg
<SfxUInt16Item
>(SID_TABLE_BORDER_INDEX
);
1542 const auto* pOffset
= rReq
.GetArg
<SfxInt32Item
>(SID_TABLE_BORDER_OFFSET
);
1544 if (!(pType
&& pIndex
&& pOffset
))
1547 const OUString sType
= pType
->GetValue();
1548 const sal_uInt16 nIndex
= pIndex
->GetValue();
1549 const sal_Int32 nOffset
= convertTwipToMm100(pOffset
->GetValue());
1551 SdrTableObj
& rTableObj(*mxTableObj
.get());
1553 sal_Int32 nEdgeIndex
= -1;
1554 bool bHorizontal
= sType
.startsWith("row");
1556 if (sType
== "column-left" || sType
== "row-left")
1560 else if (sType
== "column-right")
1562 // Number of edges = number of columns + 1
1563 nEdgeIndex
= rTableObj
.getColumnCount();
1565 else if (sType
== "row-right")
1567 // Number of edges = number of rows + 1
1568 nEdgeIndex
= rTableObj
.getRowCount();
1570 else if (sType
== "column-middle" || sType
== "row-middle")
1572 nEdgeIndex
= nIndex
+ 1;
1578 TableModelNotifyGuard
aGuard(mxTable
.get());
1579 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1580 const bool bUndo(rModel
.IsUndoEnabled());
1583 auto pUndoObject
= rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
);
1584 rModel
.BegUndo(pUndoObject
->GetComment());
1586 auto* pGeoUndo
= static_cast<SdrUndoGeoObj
*>(pUndoObject
.get());
1588 pGeoUndo
->SetSkipChangeLayout(true);
1590 rModel
.AddUndo(std::move(pUndoObject
));
1592 tools::Rectangle aBoundRect
;
1593 if (rTableObj
.GetUserCall())
1594 aBoundRect
= rTableObj
.GetLastBoundRect();
1595 rTableObj
.changeEdge(bHorizontal
, nEdgeIndex
, nOffset
);
1596 rTableObj
.SetChanged();
1597 rTableObj
.SendUserCall(SdrUserCallType::Resize
, aBoundRect
);
1605 bool SvxTableController::checkTableObject()
1607 return mxTableObj
.get().is();
1611 SvxTableController::TblAction
SvxTableController::getKeyboardAction(const KeyEvent
& rKEvt
)
1613 const bool bMod1
= rKEvt
.GetKeyCode().IsMod1(); // ctrl
1614 const bool bMod2
= rKEvt
.GetKeyCode().IsMod2(); // Alt
1615 const bool bTextEdit
= mrView
.IsTextEdit();
1617 TblAction nAction
= TblAction::HandledByView
;
1619 rtl::Reference
<sdr::table::SdrTableObj
> pTableObj
= mxTableObj
.get();
1623 // handle special keys
1624 const sal_Int16 nCode
= rKEvt
.GetKeyCode().GetCode();
1627 case awt::Key::ESCAPE
: // handle escape
1631 // escape during text edit ends text edit
1632 nAction
= TblAction::StopTextEdit
;
1634 if( mbCellSelectionMode
)
1636 // escape with selected cells removes selection
1637 nAction
= TblAction::RemoveSelection
;
1641 case awt::Key::RETURN
: // handle return
1643 if( !bMod1
&& !bMod2
&& !bTextEdit
)
1645 // when not already editing, return starts text edit
1646 setSelectionStart( SdrTableObj::getFirstCell() );
1647 nAction
= TblAction::EditCell
;
1651 case awt::Key::F2
: // f2 toggles text edit
1653 if( bMod1
|| bMod2
) // f2 with modifiers is handled by the view
1656 else if( bTextEdit
)
1658 // f2 during text edit stops text edit
1659 nAction
= TblAction::StopTextEdit
;
1661 else if( mbCellSelectionMode
)
1663 // f2 with selected cells removes selection
1664 nAction
= TblAction::RemoveSelection
;
1668 // f2 with no selection and no text edit starts text edit
1669 setSelectionStart( SdrTableObj::getFirstCell() );
1670 nAction
= TblAction::EditCell
;
1674 case awt::Key::HOME
:
1675 case awt::Key::NUM7
:
1677 if( (bMod1
|| bMod2
) && (bTextEdit
|| mbCellSelectionMode
) )
1679 if( bMod1
&& !bMod2
)
1681 // ctrl + home jumps to first cell
1682 nAction
= TblAction::GotoFirstCell
;
1684 else if( !bMod1
&& bMod2
)
1686 // alt + home jumps to first column
1687 nAction
= TblAction::GotoFirstColumn
;
1693 case awt::Key::NUM1
:
1695 if( (bMod1
|| bMod2
) && (bTextEdit
|| mbCellSelectionMode
) )
1697 if( bMod1
&& !bMod2
)
1699 // ctrl + end jumps to last cell
1700 nAction
= TblAction::GotoLastCell
;
1702 else if( !bMod1
&& bMod2
)
1704 // alt + home jumps to last column
1705 nAction
= TblAction::GotoLastColumn
;
1713 if( bTextEdit
|| mbCellSelectionMode
)
1714 nAction
= TblAction::Tab
;
1719 case awt::Key::NUM8
:
1720 case awt::Key::DOWN
:
1721 case awt::Key::NUM2
:
1722 case awt::Key::LEFT
:
1723 case awt::Key::NUM4
:
1724 case awt::Key::RIGHT
:
1725 case awt::Key::NUM6
:
1728 if( !bMod1
&& bMod2
)
1730 if(bTextEdit
|| mbCellSelectionMode
)
1732 if( (nCode
== awt::Key::UP
) || (nCode
== awt::Key::NUM8
) )
1734 nAction
= TblAction::GotoLeftCell
;
1737 else if( (nCode
== awt::Key::DOWN
) || (nCode
== awt::Key::NUM2
) )
1739 nAction
= TblAction::GotoRightCell
;
1745 bool bTextMove
= false;
1746 OutlinerView
* pOLV
= mrView
.GetTextEditOutlinerView();
1750 // during text edit, check if we navigate out of the cell
1751 ESelection aOldSelection
= pOLV
->GetSelection();
1752 pOLV
->PostKeyEvent(rKEvt
);
1753 bTextMove
= aOldSelection
== pOLV
->GetSelection();
1756 nAction
= TblAction::NONE
;
1760 if( mbCellSelectionMode
|| bTextMove
)
1762 // no text edit, navigate in cells if selection active
1765 case awt::Key::LEFT
:
1766 case awt::Key::NUM4
:
1767 nAction
= TblAction::GotoLeftCell
;
1769 case awt::Key::RIGHT
:
1770 case awt::Key::NUM6
:
1771 nAction
= TblAction::GotoRightCell
;
1773 case awt::Key::DOWN
:
1774 case awt::Key::NUM2
:
1775 nAction
= TblAction::GotoDownCell
;
1778 case awt::Key::NUM8
:
1779 nAction
= TblAction::GotoUpCell
;
1785 case awt::Key::PAGEUP
:
1787 nAction
= TblAction::GotoFirstRow
;
1790 case awt::Key::PAGEDOWN
:
1792 nAction
= TblAction::GotoLastRow
;
1798 bool SvxTableController::executeAction(TblAction nAction
, bool bSelect
, vcl::Window
* pWindow
)
1800 rtl::Reference
<sdr::table::SdrTableObj
> pTableObj
= mxTableObj
.get();
1806 case TblAction::GotoFirstCell
:
1808 gotoCell( SdrTableObj::getFirstCell(), bSelect
, pWindow
, nAction
);
1812 case TblAction::GotoLeftCell
:
1814 gotoCell( pTableObj
->getLeftCell( getSelectionEnd(), !bSelect
), bSelect
, pWindow
, nAction
);
1818 case TblAction::GotoRightCell
:
1820 gotoCell( pTableObj
->getRightCell( getSelectionEnd(), !bSelect
), bSelect
, pWindow
, nAction
);
1824 case TblAction::GotoLastCell
:
1826 gotoCell( pTableObj
->getLastCell(), bSelect
, pWindow
, nAction
);
1830 case TblAction::GotoFirstColumn
:
1832 CellPos
aPos( SdrTableObj::getFirstCell().mnCol
, getSelectionEnd().mnRow
);
1833 gotoCell( aPos
, bSelect
, pWindow
, nAction
);
1837 case TblAction::GotoLastColumn
:
1839 CellPos
aPos( pTableObj
->getLastCell().mnCol
, getSelectionEnd().mnRow
);
1840 gotoCell( aPos
, bSelect
, pWindow
, nAction
);
1844 case TblAction::GotoFirstRow
:
1846 CellPos
aPos( getSelectionEnd().mnCol
, SdrTableObj::getFirstCell().mnRow
);
1847 gotoCell( aPos
, bSelect
, pWindow
, nAction
);
1851 case TblAction::GotoUpCell
:
1853 gotoCell( pTableObj
->getUpCell(getSelectionEnd(), !bSelect
), bSelect
, pWindow
, nAction
);
1857 case TblAction::GotoDownCell
:
1859 gotoCell( pTableObj
->getDownCell(getSelectionEnd(), !bSelect
), bSelect
, pWindow
, nAction
);
1863 case TblAction::GotoLastRow
:
1865 CellPos
aPos( getSelectionEnd().mnCol
, pTableObj
->getLastCell().mnRow
);
1866 gotoCell( aPos
, bSelect
, pWindow
, nAction
);
1870 case TblAction::EditCell
:
1871 EditCell( getSelectionStart(), pWindow
, nAction
);
1874 case TblAction::StopTextEdit
:
1878 case TblAction::RemoveSelection
:
1882 case TblAction::Tab
:
1885 gotoCell( pTableObj
->getPreviousCell( getSelectionEnd(), true ), false, pWindow
, nAction
);
1888 CellPos
aSelectionEnd( getSelectionEnd() );
1889 CellPos
aNextCell( pTableObj
->getNextCell( aSelectionEnd
, true ) );
1890 if( aSelectionEnd
== aNextCell
)
1892 onInsert( SID_TABLE_INSERT_ROW
);
1893 aNextCell
= pTableObj
->getNextCell( aSelectionEnd
, true );
1895 gotoCell( aNextCell
, false, pWindow
, nAction
);
1903 return nAction
!= TblAction::HandledByView
;
1907 void SvxTableController::gotoCell(const CellPos
& rPos
, bool bSelect
, vcl::Window
* pWindow
, TblAction nAction
/*= TblAction::NONE */)
1909 auto pTable
= mxTableObj
.get();
1910 if( pTable
&& pTable
->IsTextEditActive() )
1911 mrView
.SdrEndTextEdit(true);
1915 maCursorLastPos
= rPos
;
1917 pTable
->setActiveCell( rPos
);
1919 if( !mbCellSelectionMode
)
1921 setSelectedCells( maCursorFirstPos
, rPos
);
1925 UpdateSelection( rPos
);
1931 EditCell( rPos
, pWindow
, nAction
);
1936 const CellPos
& SvxTableController::getSelectionStart()
1938 checkCell( maCursorFirstPos
);
1939 return maCursorFirstPos
;
1943 void SvxTableController::setSelectionStart( const CellPos
& rPos
)
1945 maCursorFirstPos
= rPos
;
1949 const CellPos
& SvxTableController::getSelectionEnd()
1951 checkCell( maCursorLastPos
);
1952 return maCursorLastPos
;
1956 void SvxTableController::MergeRange( sal_Int32 nFirstCol
, sal_Int32 nFirstRow
, sal_Int32 nLastCol
, sal_Int32 nLastRow
)
1958 if(!checkTableObject() || !mxTable
.is())
1963 Reference
< XMergeableCellRange
> xRange( mxTable
->createCursorByRange( mxTable
->getCellRangeByPosition( nFirstCol
, nFirstRow
,nLastCol
, nLastRow
) ), UNO_QUERY_THROW
);
1965 if( xRange
->isMergeable() )
1967 SdrTableObj
& rTableObj(*mxTableObj
.get());
1968 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
1969 const bool bUndo(rModel
.IsUndoEnabled());
1973 rModel
.BegUndo( SvxResId(STR_TABLE_MERGE
) );
1974 rModel
.AddUndo(rModel
.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj
));
1978 mbHasJustMerged
= true;
1979 setSelectedCells( maCursorFirstPos
, maCursorFirstPos
);
1987 TOOLS_WARN_EXCEPTION( "svx.table", "" );
1992 void SvxTableController::checkCell( CellPos
& rPos
) const
1999 if( rPos
.mnCol
>= mxTable
->getColumnCount() )
2000 rPos
.mnCol
= mxTable
->getColumnCount()-1;
2002 if( rPos
.mnRow
>= mxTable
->getRowCount() )
2003 rPos
.mnRow
= mxTable
->getRowCount()-1;
2007 TOOLS_WARN_EXCEPTION("svx.table", "");
2012 void SvxTableController::findMergeOrigin( CellPos
& rPos
)
2019 Reference
< XMergeableCell
> xCell( mxTable
->getCellByPosition( rPos
.mnCol
, rPos
.mnRow
), UNO_QUERY_THROW
);
2020 if( xCell
->isMerged() )
2022 ::findMergeOrigin( mxTable
, rPos
.mnCol
, rPos
.mnRow
, rPos
.mnCol
, rPos
.mnRow
);
2027 TOOLS_WARN_EXCEPTION("svx.table", "");
2032 void SvxTableController::EditCell(const CellPos
& rPos
, vcl::Window
* pWindow
, TblAction nAction
/*= TblAction::NONE */)
2034 SdrPageView
* pPV(mrView
.GetSdrPageView());
2036 if(nullptr == pPV
|| !checkTableObject())
2039 SdrTableObj
& rTableObj(*mxTableObj
.get());
2041 if(rTableObj
.getSdrPageFromSdrObject() != pPV
->GetPage())
2044 bool bEmptyOutliner
= false;
2046 if(!rTableObj
.GetOutlinerParaObject() && mrView
.GetTextEditOutliner())
2048 ::Outliner
* pOutl
= mrView
.GetTextEditOutliner();
2049 sal_Int32 nParaCnt
= pOutl
->GetParagraphCount();
2050 Paragraph
* p1stPara
= pOutl
->GetParagraph( 0 );
2052 if(nParaCnt
==1 && p1stPara
)
2054 // with only one paragraph
2055 if (pOutl
->GetText(p1stPara
).isEmpty())
2057 bEmptyOutliner
= true;
2062 CellPos
aPos( rPos
);
2063 findMergeOrigin( aPos
);
2065 if( &rTableObj
== mrView
.GetTextEditObject() && !bEmptyOutliner
&& rTableObj
.IsTextEditActive( aPos
) )
2068 if( rTableObj
.IsTextEditActive() )
2069 mrView
.SdrEndTextEdit(true);
2071 rTableObj
.setActiveCell( aPos
);
2073 // create new outliner, owner will be the SdrObjEditView
2074 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
2075 std::unique_ptr
<SdrOutliner
> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject
, rModel
));
2077 if (pOutl
&& rTableObj
.IsVerticalWriting())
2078 pOutl
->SetVertical( true );
2080 if (!mrView
.SdrBeginTextEdit(&rTableObj
, pPV
, pWindow
, true, pOutl
.release()))
2083 maCursorLastPos
= maCursorFirstPos
= rPos
;
2085 OutlinerView
* pOLV
= mrView
.GetTextEditOutlinerView();
2087 // Move cursor to end of text
2088 ESelection aNewSelection
;
2090 const WritingMode eMode
= rTableObj
.GetWritingMode();
2091 if (((nAction
== TblAction::GotoLeftCell
) || (nAction
== TblAction::GotoRightCell
)) && (eMode
!= WritingMode_TB_RL
))
2093 const bool bLast
= ((nAction
== TblAction::GotoLeftCell
) && (eMode
== WritingMode_LR_TB
)) ||
2094 ((nAction
== TblAction::GotoRightCell
) && (eMode
== WritingMode_RL_TB
));
2097 aNewSelection
= ESelection::AtEnd();
2099 pOLV
->SetSelection(aNewSelection
);
2103 void SvxTableController::StopTextEdit()
2105 if(mrView
.IsTextEdit())
2107 mrView
.SdrEndTextEdit();
2108 mrView
.SetCurrentObj(SdrObjKind::Table
);
2109 mrView
.SetEditMode(SdrViewEditMode::Edit
);
2114 void SvxTableController::getSelectedCells( CellPos
& rFirst
, CellPos
& rLast
)
2116 if( mbCellSelectionMode
)
2118 checkCell( maCursorFirstPos
);
2119 checkCell( maCursorLastPos
);
2121 rFirst
.mnCol
= std::min( maCursorFirstPos
.mnCol
, maCursorLastPos
.mnCol
);
2122 rFirst
.mnRow
= std::min( maCursorFirstPos
.mnRow
, maCursorLastPos
.mnRow
);
2123 rLast
.mnCol
= std::max( maCursorFirstPos
.mnCol
, maCursorLastPos
.mnCol
);
2124 rLast
.mnRow
= std::max( maCursorFirstPos
.mnRow
, maCursorLastPos
.mnRow
);
2133 for( sal_Int32 nRow
= rFirst
.mnRow
; nRow
<= rLast
.mnRow
&& !bExt
; nRow
++ )
2135 for( sal_Int32 nCol
= rFirst
.mnCol
; nCol
<= rLast
.mnCol
&& !bExt
; nCol
++ )
2137 Reference
< XMergeableCell
> xCell( mxTable
->getCellByPosition( nCol
, nRow
), UNO_QUERY
);
2141 if( xCell
->isMerged() )
2143 CellPos
aPos( nCol
, nRow
);
2144 findMergeOrigin( aPos
);
2145 if( (aPos
.mnCol
< rFirst
.mnCol
) || (aPos
.mnRow
< rFirst
.mnRow
) )
2147 rFirst
.mnCol
= std::min( rFirst
.mnCol
, aPos
.mnCol
);
2148 rFirst
.mnRow
= std::min( rFirst
.mnRow
, aPos
.mnRow
);
2154 if( ((nCol
+ xCell
->getColumnSpan() - 1) > rLast
.mnCol
) || (nRow
+ xCell
->getRowSpan() - 1 ) > rLast
.mnRow
)
2156 rLast
.mnCol
= std::max( rLast
.mnCol
, nCol
+ xCell
->getColumnSpan() - 1 );
2157 rLast
.mnRow
= std::max( rLast
.mnRow
, nRow
+ xCell
->getRowSpan() - 1 );
2166 else if(mrView
.IsTextEdit())
2168 rFirst
= getSelectionStart();
2169 findMergeOrigin( rFirst
);
2174 Reference
< XMergeableCell
> xCell( mxTable
->getCellByPosition( rLast
.mnCol
, rLast
.mnRow
), UNO_QUERY
);
2177 rLast
.mnCol
+= xCell
->getColumnSpan() - 1;
2178 rLast
.mnRow
+= xCell
->getRowSpan() - 1;
2188 rLast
.mnRow
= mxTable
->getRowCount()-1;
2189 rLast
.mnCol
= mxTable
->getColumnCount()-1;
2200 void SvxTableController::StartSelection( const CellPos
& rPos
)
2203 mbCellSelectionMode
= true;
2204 maCursorLastPos
= maCursorFirstPos
= rPos
;
2205 mrView
.MarkListHasChanged();
2209 void SvxTableController::setSelectedCells( const CellPos
& rStart
, const CellPos
& rEnd
)
2212 mbCellSelectionMode
= true;
2213 maCursorFirstPos
= rStart
;
2214 UpdateSelection( rEnd
);
2218 bool SvxTableController::ChangeFontSize(bool bGrow
, const FontList
* pFontList
)
2220 if(!checkTableObject() || !mxTable
.is())
2223 SdrTableObj
& rTableObj(*mxTableObj
.get());
2224 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
2226 if (mrView
.IsTextEdit())
2229 CellPos aStart
, aEnd
;
2231 if(hasSelectedCells())
2233 getSelectedCells(aStart
, aEnd
);
2239 aEnd
.mnRow
= mxTable
->getRowCount() - 1;
2240 aEnd
.mnCol
= mxTable
->getColumnCount() - 1;
2243 for (sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++)
2245 for (sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++)
2247 CellRef
xCell(mxTable
->getCell(nCol
, nRow
));
2250 if (rModel
.IsUndoEnabled())
2253 SfxItemSet
aCellSet(xCell
->GetItemSet());
2254 if (EditView::ChangeFontSize(bGrow
, aCellSet
, pFontList
))
2256 xCell
->SetMergedItemSetAndBroadcast(aCellSet
, false);
2268 void SvxTableController::UpdateSelection( const CellPos
& rPos
)
2270 maCursorLastPos
= rPos
;
2271 mrView
.MarkListHasChanged();
2275 void SvxTableController::clearSelection()
2281 void SvxTableController::selectAll()
2285 CellPos
aPos2( mxTable
->getColumnCount()-1, mxTable
->getRowCount()-1 );
2286 if( (aPos2
.mnCol
>= 0) && (aPos2
.mnRow
>= 0) )
2289 setSelectedCells( aPos1
, aPos2
);
2295 void SvxTableController::RemoveSelection()
2297 if( mbCellSelectionMode
)
2299 mbCellSelectionMode
= false;
2300 mrView
.MarkListHasChanged();
2305 void SvxTableController::onTableModified()
2307 if( mnUpdateEvent
== nullptr )
2308 mnUpdateEvent
= Application::PostUserEvent( LINK( this, SvxTableController
, UpdateHdl
) );
2312 void SvxTableController::updateSelectionOverlay()
2314 // There is no need to update selection overlay after merging cells
2315 // since the selection overlay should remain the same
2316 if ( mbHasJustMerged
)
2319 destroySelectionOverlay();
2320 if( !mbCellSelectionMode
)
2323 rtl::Reference
<sdr::table::SdrTableObj
> pTableObj
= mxTableObj
.get();
2327 sdr::overlay::OverlayObjectCell::RangeVector aRanges
;
2329 tools::Rectangle aStartRect
, aEndRect
;
2330 CellPos aStart
,aEnd
;
2331 getSelectedCells( aStart
, aEnd
);
2332 pTableObj
->getCellBounds( aStart
, aStartRect
);
2334 basegfx::B2DRange
a2DRange( basegfx::B2DPoint(aStartRect
.Left(), aStartRect
.Top()) );
2335 a2DRange
.expand( basegfx::B2DPoint(aStartRect
.Right(), aStartRect
.Bottom()) );
2337 findMergeOrigin( aEnd
);
2338 pTableObj
->getCellBounds( aEnd
, aEndRect
);
2339 a2DRange
.expand( basegfx::B2DPoint(aEndRect
.Left(), aEndRect
.Top()) );
2340 a2DRange
.expand( basegfx::B2DPoint(aEndRect
.Right(), aEndRect
.Bottom()) );
2341 aRanges
.push_back( a2DRange
);
2343 ::Color
aHighlight( COL_BLUE
);
2344 OutputDevice
* pOutDev
= mrView
.GetFirstOutputDevice();
2346 aHighlight
= pOutDev
->GetSettings().GetStyleSettings().GetHighlightColor();
2348 const sal_uInt32 nCount
= mrView
.PaintWindowCount();
2349 for( sal_uInt32 nIndex
= 0; nIndex
< nCount
; nIndex
++ )
2351 SdrPaintWindow
* pPaintWindow
= mrView
.GetPaintWindow(nIndex
);
2354 const rtl::Reference
< sdr::overlay::OverlayManager
>& xOverlayManager
= pPaintWindow
->GetOverlayManager();
2355 if( xOverlayManager
.is() )
2357 std::unique_ptr
<sdr::overlay::OverlayObjectCell
> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight
, std::vector(aRanges
) ));
2359 xOverlayManager
->add(*pOverlay
);
2360 mpSelectionOverlay
.emplace();
2361 mpSelectionOverlay
->append(std::move(pOverlay
));
2366 // If tiled rendering, emit callbacks for sdr table selection.
2367 if (!(pOutDev
&& comphelper::LibreOfficeKit::isActive()))
2370 tools::Rectangle
aSelection(a2DRange
.getMinX(), a2DRange
.getMinY(), a2DRange
.getMaxX(), a2DRange
.getMaxY());
2372 if (pOutDev
->GetMapMode().GetMapUnit() == MapUnit::Map100thMM
)
2373 aSelection
= o3tl::toTwips(aSelection
, o3tl::Length::mm100
);
2375 if(SfxViewShell
* pViewShell
= SfxViewShell::Current())
2377 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA
, aSelection
.toString());
2378 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION
, aSelection
.toString());
2383 void SvxTableController::destroySelectionOverlay()
2385 if( !mpSelectionOverlay
)
2388 mpSelectionOverlay
.reset();
2390 if (comphelper::LibreOfficeKit::isActive())
2392 // Clear the LOK text selection so far provided by this table.
2393 if(SfxViewShell
* pViewShell
= SfxViewShell::Current())
2395 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA
, "EMPTY"_ostr
);
2396 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START
, "EMPTY"_ostr
);
2397 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END
, "EMPTY"_ostr
);
2398 pViewShell
->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION
, "EMPTY"_ostr
);
2404 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet
& rAttr
, bool bOnlyHardAttr
) const
2409 CellPos aStart
, aEnd
;
2410 const_cast<SvxTableController
&>(*this).getSelectedCells( aStart
, aEnd
);
2412 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
2414 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
2416 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
2417 if( xCell
.is() && !xCell
->isMerged() )
2419 const SfxItemSet
& rSet
= xCell
->GetItemSet();
2420 SfxWhichIter
aIter(rSet
);
2421 sal_uInt16
nWhich(aIter
.FirstWhich());
2424 SfxItemState nState
= aIter
.GetItemState(false);
2427 if(SfxItemState::INVALID
== nState
)
2428 rAttr
.InvalidateItem(nWhich
);
2430 rAttr
.MergeValue(rSet
.Get(nWhich
));
2432 else if(SfxItemState::SET
== nState
)
2434 const SfxPoolItem
& rItem
= rSet
.Get(nWhich
);
2435 rAttr
.MergeValue(rItem
);
2438 nWhich
= aIter
.NextWhich();
2446 static void ImplSetLinePreserveColor( SvxBoxItem
& rNewFrame
, const SvxBorderLine
* pNew
, SvxBoxItemLine nLine
)
2450 const SvxBorderLine
* pOld
= rNewFrame
.GetLine(nLine
);
2453 SvxBorderLine
aNewLine( *pNew
);
2454 aNewLine
.SetColor( pOld
->GetColor() );
2455 rNewFrame
.SetLine( &aNewLine
, nLine
);
2459 rNewFrame
.SetLine( pNew
, nLine
);
2463 static void ImplApplyBoxItem( CellPosFlag nCellPosFlags
, const SvxBoxItem
* pBoxItem
, const SvxBoxInfoItem
* pBoxInfoItem
, SvxBoxItem
& rNewFrame
)
2465 if (nCellPosFlags
& (CellPosFlag::Before
|CellPosFlag::After
|CellPosFlag::Upper
|CellPosFlag::Lower
))
2467 // current cell is outside the selection
2469 if (!(nCellPosFlags
& (CellPosFlag::Before
|CellPosFlag::After
))) // check if it's not any corner
2471 if (nCellPosFlags
& CellPosFlag::Upper
)
2473 if( pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::TOP
) )
2474 rNewFrame
.SetLine(nullptr, SvxBoxItemLine::BOTTOM
);
2476 else if (nCellPosFlags
& CellPosFlag::Lower
)
2478 if( pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::BOTTOM
) )
2479 rNewFrame
.SetLine( nullptr, SvxBoxItemLine::TOP
);
2482 else if (!(nCellPosFlags
& (CellPosFlag::Upper
|CellPosFlag::Lower
))) // check if it's not any corner
2484 if (nCellPosFlags
& CellPosFlag::Before
)
2486 if( pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::LEFT
) )
2487 rNewFrame
.SetLine( nullptr, SvxBoxItemLine::RIGHT
);
2489 else if (nCellPosFlags
& CellPosFlag::After
)
2491 if( pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::RIGHT
) )
2492 rNewFrame
.SetLine( nullptr, SvxBoxItemLine::LEFT
);
2498 // current cell is inside the selection
2500 if ((nCellPosFlags
& CellPosFlag::Left
) ? pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::LEFT
)
2501 : pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::VERT
))
2502 rNewFrame
.SetLine( (nCellPosFlags
& CellPosFlag::Left
) ? pBoxItem
->GetLeft() : pBoxInfoItem
->GetVert(), SvxBoxItemLine::LEFT
);
2504 if( (nCellPosFlags
& CellPosFlag::Right
) ? pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::RIGHT
) : pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::VERT
) )
2505 rNewFrame
.SetLine( (nCellPosFlags
& CellPosFlag::Right
) ? pBoxItem
->GetRight() : pBoxInfoItem
->GetVert(), SvxBoxItemLine::RIGHT
);
2507 if( (nCellPosFlags
& CellPosFlag::Top
) ? pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::TOP
) : pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::HORI
) )
2508 rNewFrame
.SetLine( (nCellPosFlags
& CellPosFlag::Top
) ? pBoxItem
->GetTop() : pBoxInfoItem
->GetHori(), SvxBoxItemLine::TOP
);
2510 if( (nCellPosFlags
& CellPosFlag::Bottom
) ? pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::BOTTOM
) : pBoxInfoItem
->IsValid(SvxBoxInfoItemValidFlags::HORI
) )
2511 rNewFrame
.SetLine( (nCellPosFlags
& CellPosFlag::Bottom
) ? pBoxItem
->GetBottom() : pBoxInfoItem
->GetHori(), SvxBoxItemLine::BOTTOM
);
2513 // apply distance to borders
2514 if( pBoxInfoItem
->IsValid( SvxBoxInfoItemValidFlags::DISTANCE
) )
2515 for( SvxBoxItemLine nLine
: o3tl::enumrange
<SvxBoxItemLine
>() )
2516 rNewFrame
.SetDistance( pBoxItem
->GetDistance( nLine
), nLine
);
2521 static void ImplSetLineColor( SvxBoxItem
& rNewFrame
, SvxBoxItemLine nLine
, const Color
& rColor
)
2523 const SvxBorderLine
* pSourceLine
= rNewFrame
.GetLine( nLine
);
2526 SvxBorderLine
aLine( *pSourceLine
);
2527 aLine
.SetColor( rColor
);
2528 rNewFrame
.SetLine( &aLine
, nLine
);
2533 static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags
, const SvxColorItem
* pLineColorItem
, SvxBoxItem
& rNewFrame
)
2535 const Color
aColor( pLineColorItem
->GetValue() );
2537 if (!(nCellPosFlags
& (CellPosFlag::Lower
|CellPosFlag::Before
|CellPosFlag::After
)))
2538 ImplSetLineColor( rNewFrame
, SvxBoxItemLine::BOTTOM
, aColor
);
2540 if (!(nCellPosFlags
& (CellPosFlag::Upper
|CellPosFlag::Before
|CellPosFlag::After
)))
2541 ImplSetLineColor( rNewFrame
, SvxBoxItemLine::TOP
, aColor
);
2543 if (!(nCellPosFlags
& (CellPosFlag::Upper
|CellPosFlag::Lower
|CellPosFlag::After
)))
2544 ImplSetLineColor( rNewFrame
, SvxBoxItemLine::RIGHT
, aColor
);
2546 if (!(nCellPosFlags
& (CellPosFlag::Upper
|CellPosFlag::Lower
|CellPosFlag::Before
)))
2547 ImplSetLineColor( rNewFrame
, SvxBoxItemLine::LEFT
, aColor
);
2551 static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags
, const SvxBorderLine
* pBorderLineItem
, SvxBoxItem
& rNewFrame
)
2553 if (nCellPosFlags
& (CellPosFlag::Before
|CellPosFlag::After
|CellPosFlag::Upper
|CellPosFlag::Lower
))
2555 if (!(nCellPosFlags
& (CellPosFlag::Before
|CellPosFlag::After
))) // check if it's not any corner
2557 if (nCellPosFlags
& CellPosFlag::Upper
)
2559 if( rNewFrame
.GetBottom() )
2560 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::BOTTOM
);
2562 else if (nCellPosFlags
& CellPosFlag::Lower
)
2564 if( rNewFrame
.GetTop() )
2565 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::TOP
);
2568 else if (!(nCellPosFlags
& (CellPosFlag::Upper
|CellPosFlag::Lower
))) // check if it's not any corner
2570 if (nCellPosFlags
& CellPosFlag::Before
)
2572 if( rNewFrame
.GetRight() )
2573 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::RIGHT
);
2575 else if (nCellPosFlags
& CellPosFlag::After
)
2577 if( rNewFrame
.GetLeft() )
2578 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::LEFT
);
2584 if( rNewFrame
.GetBottom() )
2585 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::BOTTOM
);
2586 if( rNewFrame
.GetTop() )
2587 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::TOP
);
2588 if( rNewFrame
.GetRight() )
2589 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::RIGHT
);
2590 if( rNewFrame
.GetLeft() )
2591 ImplSetLinePreserveColor( rNewFrame
, pBorderLineItem
, SvxBoxItemLine::LEFT
);
2596 void SvxTableController::ApplyBorderAttr( const SfxItemSet
& rAttr
)
2601 const sal_Int32 nRowCount
= mxTable
->getRowCount();
2602 const sal_Int32 nColCount
= mxTable
->getColumnCount();
2603 if( !(nRowCount
&& nColCount
) )
2606 const SvxBoxItem
* pBoxItem
= nullptr;
2607 if(SfxItemState::SET
== rAttr
.GetItemState(SDRATTR_TABLE_BORDER
, false) )
2608 pBoxItem
= &rAttr
.Get( SDRATTR_TABLE_BORDER
);
2610 const SvxBoxInfoItem
* pBoxInfoItem
= nullptr;
2611 if(SfxItemState::SET
== rAttr
.GetItemState(SDRATTR_TABLE_BORDER_INNER
, false) )
2612 pBoxInfoItem
= &rAttr
.Get( SDRATTR_TABLE_BORDER_INNER
);
2614 const SvxColorItem
* pLineColorItem
= nullptr;
2615 if(SfxItemState::SET
== rAttr
.GetItemState(SID_FRAME_LINECOLOR
, false) )
2616 pLineColorItem
= &rAttr
.Get( SID_FRAME_LINECOLOR
);
2618 const SvxBorderLine
* pBorderLineItem
= nullptr;
2619 if(SfxItemState::SET
== rAttr
.GetItemState(SID_FRAME_LINESTYLE
, false) )
2620 pBorderLineItem
= rAttr
.Get( SID_FRAME_LINESTYLE
).GetLine();
2622 if( pBoxInfoItem
&& !pBoxItem
)
2624 const static SvxBoxItem
gaEmptyBoxItem( SDRATTR_TABLE_BORDER
);
2625 pBoxItem
= &gaEmptyBoxItem
;
2627 else if( pBoxItem
&& !pBoxInfoItem
)
2629 const static SvxBoxInfoItem
gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER
);
2630 pBoxInfoItem
= &gaEmptyBoxInfoItem
;
2633 CellPos aStart
, aEnd
;
2634 getSelectedCells( aStart
, aEnd
);
2636 const sal_Int32 nLastRow
= std::min( aEnd
.mnRow
+ 2, nRowCount
);
2637 const sal_Int32 nLastCol
= std::min( aEnd
.mnCol
+ 2, nColCount
);
2639 for( sal_Int32 nRow
= std::max( aStart
.mnRow
- 1, sal_Int32(0) ); nRow
< nLastRow
; nRow
++ )
2641 CellPosFlag nRowFlags
= CellPosFlag::NONE
;
2642 nRowFlags
|= (nRow
== aStart
.mnRow
) ? CellPosFlag::Top
: CellPosFlag::NONE
;
2643 nRowFlags
|= (nRow
== aEnd
.mnRow
) ? CellPosFlag::Bottom
: CellPosFlag::NONE
;
2644 nRowFlags
|= (nRow
< aStart
.mnRow
) ? CellPosFlag::Upper
: CellPosFlag::NONE
;
2645 nRowFlags
|= (nRow
> aEnd
.mnRow
) ? CellPosFlag::Lower
: CellPosFlag::NONE
;
2647 for( sal_Int32 nCol
= std::max( aStart
.mnCol
- 1, sal_Int32(0) ); nCol
< nLastCol
; nCol
++ )
2649 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
2653 const SfxItemSet
& rSet
= xCell
->GetItemSet();
2654 const SvxBoxItem
* pOldOuter
= &rSet
.Get( SDRATTR_TABLE_BORDER
);
2656 SvxBoxItem
aNewFrame( *pOldOuter
);
2658 CellPosFlag nCellPosFlags
= nRowFlags
;
2659 nCellPosFlags
|= (nCol
== aStart
.mnCol
) ? CellPosFlag::Left
: CellPosFlag::NONE
;
2660 nCellPosFlags
|= (nCol
== aEnd
.mnCol
) ? CellPosFlag::Right
: CellPosFlag::NONE
;
2661 nCellPosFlags
|= (nCol
< aStart
.mnCol
) ? CellPosFlag::Before
: CellPosFlag::NONE
;
2662 nCellPosFlags
|= (nCol
> aEnd
.mnCol
) ? CellPosFlag::After
: CellPosFlag::NONE
;
2664 if( pBoxItem
&& pBoxInfoItem
)
2665 ImplApplyBoxItem( nCellPosFlags
, pBoxItem
, pBoxInfoItem
, aNewFrame
);
2667 if( pLineColorItem
)
2668 ImplApplyLineColorItem( nCellPosFlags
, pLineColorItem
, aNewFrame
);
2670 if( pBorderLineItem
)
2671 ImplApplyBorderLineItem( nCellPosFlags
, pBorderLineItem
, aNewFrame
);
2673 if (aNewFrame
!= *pOldOuter
)
2675 SfxItemSet
aAttr(*rSet
.GetPool(), rSet
.GetRanges());
2676 aAttr
.Put(aNewFrame
);
2677 xCell
->SetMergedItemSetAndBroadcast( aAttr
, false );
2684 void SvxTableController::UpdateTableShape()
2686 rtl::Reference
<SdrObject
> pTableObj
= mxTableObj
.get();
2689 pTableObj
->ActionChanged();
2690 pTableObj
->BroadcastObjectChange();
2692 updateSelectionOverlay();
2696 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet
& rAttr
, bool bReplaceAll
)
2698 if(!checkTableObject() || !mxTable
.is())
2701 SdrTableObj
& rTableObj(*mxTableObj
.get());
2702 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
2703 const bool bUndo(rModel
.IsUndoEnabled());
2706 rModel
.BegUndo( SvxResId(STR_TABLE_NUMFORMAT
) );
2708 CellPos aStart
, aEnd
;
2709 getSelectedCells( aStart
, aEnd
);
2711 SfxItemSet
aAttr(*rAttr
.GetPool(), rAttr
.GetRanges());
2714 const bool bFrame
= (rAttr
.GetItemState( SDRATTR_TABLE_BORDER
) == SfxItemState::SET
) || (rAttr
.GetItemState( SDRATTR_TABLE_BORDER_INNER
) == SfxItemState::SET
);
2718 aAttr
.ClearItem( SDRATTR_TABLE_BORDER
);
2719 aAttr
.ClearItem( SDRATTR_TABLE_BORDER_INNER
);
2722 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
2724 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
2726 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
2731 xCell
->SetMergedItemSetAndBroadcast(aAttr
, bReplaceAll
);
2738 ApplyBorderAttr( rAttr
);
2747 void SvxTableController::SetAttrToSelectedShape(const SfxItemSet
& rAttr
)
2749 if (!checkTableObject() || !mxTable
.is())
2752 // Filter out non-shadow items from rAttr.
2753 SfxItemSetFixed
<SDRATTR_SHADOW_FIRST
, SDRATTR_SHADOW_LAST
> aSet(*rAttr
.GetPool());
2758 // If there are no items to be applied on the shape, then don't set anything, it would
2759 // terminate text edit without a good reason, which affects undo/redo.
2763 // Set shadow items on the marked shape.
2764 mrView
.SetAttrToMarked(aSet
, /*bReplaceAll=*/false);
2767 bool SvxTableController::GetAttributes(SfxItemSet
& rTargetSet
, bool bOnlyHardAttr
) const
2769 if( mxTableObj
.get().is() && hasSelectedCells() )
2771 MergeAttrFromSelectedCells( rTargetSet
, bOnlyHardAttr
);
2773 if( mrView
.IsTextEdit() )
2775 OutlinerView
* pTextEditOutlinerView
= mrView
.GetTextEditOutlinerView();
2776 if(pTextEditOutlinerView
)
2778 // FALSE= consider InvalidItems not as the default, but as "holes"
2779 rTargetSet
.Put(pTextEditOutlinerView
->GetAttribs(), false);
2792 bool SvxTableController::SetAttributes(const SfxItemSet
& rSet
, bool bReplaceAll
)
2794 if( mbCellSelectionMode
|| mrView
.IsTextEdit() )
2796 SetAttrToSelectedCells( rSet
, bReplaceAll
);
2802 rtl::Reference
<SdrObject
> SvxTableController::GetMarkedSdrObjClone(SdrModel
& rTargetModel
)
2804 rtl::Reference
<SdrTableObj
> pRetval
;
2805 sdr::table::SdrTableObj
* pCurrentSdrTableObj(GetTableObj());
2807 if(nullptr == pCurrentSdrTableObj
)
2812 if(!mxTableObj
.get().is())
2817 // get selection and create full selection
2818 CellPos aStart
, aEnd
;
2819 const CellPos aFullStart
, aFullEnd(mxTable
->getColumnCount()-1, mxTable
->getRowCount()-1);
2821 getSelectedCells(aStart
, aEnd
);
2823 // compare to see if we have a partial selection
2824 if(aStart
!= aFullStart
|| aEnd
!= aFullEnd
)
2826 // create full clone
2827 pRetval
= SdrObject::Clone(*pCurrentSdrTableObj
, rTargetModel
);
2829 // limit SdrObject's TableModel to partial selection
2830 pRetval
->CropTableModelToSelection(aStart
, aEnd
);
2836 bool SvxTableController::PasteObjModel( const SdrModel
& rModel
)
2838 if( mxTableObj
.get().is() && (rModel
.GetPageCount() >= 1) )
2840 const SdrPage
* pPastePage
= rModel
.GetPage(0);
2841 if( pPastePage
&& pPastePage
->GetObjCount() == 1 )
2843 SdrTableObj
* pPasteTableObj
= dynamic_cast< SdrTableObj
* >( pPastePage
->GetObj(0) );
2844 if( pPasteTableObj
)
2846 return PasteObject( pPasteTableObj
);
2855 bool SvxTableController::PasteObject( SdrTableObj
const * pPasteTableObj
)
2857 if( !pPasteTableObj
)
2860 const rtl::Reference
< TableModel
>& xPasteTable( pPasteTableObj
->getUnoTable() );
2861 if( !xPasteTable
.is() )
2867 sal_Int32 nPasteColumns
= xPasteTable
->getColumnCount();
2868 sal_Int32 nPasteRows
= xPasteTable
->getRowCount();
2870 CellPos aStart
, aEnd
;
2871 getSelectedCells( aStart
, aEnd
);
2873 if( mrView
.IsTextEdit() )
2874 mrView
.SdrEndTextEdit(true);
2876 sal_Int32 nColumns
= mxTable
->getColumnCount();
2877 sal_Int32 nRows
= mxTable
->getRowCount();
2879 const sal_Int32 nMissing
= nPasteRows
- ( nRows
- aStart
.mnRow
);
2882 Reference
< XTableRows
> xRows( mxTable
->getRows() );
2883 xRows
->insertByIndex( nRows
, nMissing
);
2884 nRows
= mxTable
->getRowCount();
2887 nPasteRows
= std::min( nPasteRows
, nRows
- aStart
.mnRow
);
2888 nPasteColumns
= std::min( nPasteColumns
, nColumns
- aStart
.mnCol
);
2890 // copy cell contents
2891 for( sal_Int32 nRow
= 0; nRow
< nPasteRows
; ++nRow
)
2893 for( sal_Int32 nCol
= 0, nTargetCol
= aStart
.mnCol
; nCol
< nPasteColumns
; ++nCol
)
2895 CellRef
xTargetCell( mxTable
->getCell( nTargetCol
, aStart
.mnRow
+ nRow
) );
2896 if( xTargetCell
.is() && !xTargetCell
->isMerged() )
2898 CellRef
xSourceCell(xPasteTable
->getCell(nCol
, nRow
));
2899 if (xSourceCell
.is())
2901 xTargetCell
->AddUndo();
2902 // Prevent cell span exceeding the pasting range.
2903 if (nColumns
< nTargetCol
+ xSourceCell
->getColumnSpan())
2904 xTargetCell
->replaceContentAndFormatting(xSourceCell
);
2906 xTargetCell
->cloneFrom(xSourceCell
);
2908 nCol
+= xSourceCell
->getColumnSpan() - 1;
2909 nTargetCol
+= xTargetCell
->getColumnSpan();
2920 bool SvxTableController::ApplyFormatPaintBrush(SfxItemSet
& rFormatSet
, sal_Int16 nDepth
,
2921 bool bNoCharacterFormats
, bool bNoParagraphFormats
)
2923 if(!mbCellSelectionMode
)
2928 if(!checkTableObject())
2931 SdrTableObj
& rTableObj(*mxTableObj
.get());
2932 SdrModel
& rModel(rTableObj
.getSdrModelFromSdrObject());
2933 const bool bUndo(rModel
.IsUndoEnabled());
2936 rModel
.BegUndo(SvxResId(STR_TABLE_NUMFORMAT
));
2938 CellPos aStart
, aEnd
;
2939 getSelectedCells( aStart
, aEnd
);
2940 const bool bFrame
= (rFormatSet
.GetItemState( SDRATTR_TABLE_BORDER
) == SfxItemState::SET
) || (rFormatSet
.GetItemState( SDRATTR_TABLE_BORDER_INNER
) == SfxItemState::SET
);
2942 for( sal_Int32 nRow
= aStart
.mnRow
; nRow
<= aEnd
.mnRow
; nRow
++ )
2944 for( sal_Int32 nCol
= aStart
.mnCol
; nCol
<= aEnd
.mnCol
; nCol
++ )
2946 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
2951 SdrText
* pText
= xCell
.get();
2952 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet
, rTableObj
, pText
, nDepth
, bNoCharacterFormats
, bNoParagraphFormats
);
2959 ApplyBorderAttr( rFormatSet
);
2971 IMPL_LINK_NOARG(SvxTableController
, UpdateHdl
, void*, void)
2973 mnUpdateEvent
= nullptr;
2975 if( mbCellSelectionMode
)
2977 CellPos
aStart( maCursorFirstPos
);
2978 CellPos
aEnd( maCursorLastPos
);
2981 if( aStart
!= maCursorFirstPos
|| aEnd
!= maCursorLastPos
)
2983 setSelectedCells( aStart
, aEnd
);
2987 updateSelectionOverlay();
2988 mbHasJustMerged
= false;
2996 LinesState(SvxBoxItem
& rBoxItem_
, SvxBoxInfoItem
& rBoxInfoItem_
)
2997 : rBoxItem(rBoxItem_
)
2998 , rBoxInfoItem(rBoxInfoItem_
)
2999 , bDistanceIndeterminate(false)
3001 aBorderSet
.fill(false);
3002 aInnerLineSet
.fill(false);
3003 aBorderIndeterminate
.fill(false);
3004 aInnerLineIndeterminate
.fill(false);
3005 aDistanceSet
.fill(false);
3009 SvxBoxItem
& rBoxItem
;
3010 SvxBoxInfoItem
& rBoxInfoItem
;
3011 o3tl::enumarray
<SvxBoxItemLine
, bool> aBorderSet
;
3012 o3tl::enumarray
<SvxBoxInfoItemLine
, bool> aInnerLineSet
;
3013 o3tl::enumarray
<SvxBoxItemLine
, bool> aBorderIndeterminate
;
3014 o3tl::enumarray
<SvxBoxInfoItemLine
, bool> aInnerLineIndeterminate
;
3015 o3tl::enumarray
<SvxBoxItemLine
, bool> aDistanceSet
;
3016 o3tl::enumarray
<SvxBoxItemLine
, sal_uInt16
> aDistance
;
3017 bool bDistanceIndeterminate
;
3020 class BoxItemWrapper
3023 BoxItemWrapper(SvxBoxItem
& rBoxItem
, SvxBoxInfoItem
& rBoxInfoItem
, SvxBoxItemLine nBorderLine
, SvxBoxInfoItemLine nInnerLine
, bool bBorder
);
3025 const SvxBorderLine
* getLine() const;
3026 void setLine(const SvxBorderLine
* pLine
);
3029 SvxBoxItem
& m_rBoxItem
;
3030 SvxBoxInfoItem
& m_rBoxInfoItem
;
3031 const SvxBoxItemLine m_nBorderLine
;
3032 const SvxBoxInfoItemLine m_nInnerLine
;
3033 const bool m_bBorder
;
3036 BoxItemWrapper::BoxItemWrapper(
3037 SvxBoxItem
& rBoxItem
, SvxBoxInfoItem
& rBoxInfoItem
,
3038 const SvxBoxItemLine nBorderLine
, const SvxBoxInfoItemLine nInnerLine
, const bool bBorder
)
3039 : m_rBoxItem(rBoxItem
)
3040 , m_rBoxInfoItem(rBoxInfoItem
)
3041 , m_nBorderLine(nBorderLine
)
3042 , m_nInnerLine(nInnerLine
)
3043 , m_bBorder(bBorder
)
3047 const SvxBorderLine
* BoxItemWrapper::getLine() const
3050 return m_rBoxItem
.GetLine(m_nBorderLine
);
3052 return (m_nInnerLine
== SvxBoxInfoItemLine::HORI
) ? m_rBoxInfoItem
.GetHori() : m_rBoxInfoItem
.GetVert();
3055 void BoxItemWrapper::setLine(const SvxBorderLine
* pLine
)
3058 m_rBoxItem
.SetLine(pLine
, m_nBorderLine
);
3060 m_rBoxInfoItem
.SetLine(pLine
, m_nInnerLine
);
3063 void lcl_MergeBorderLine(
3064 LinesState
& rLinesState
, const SvxBorderLine
* const pLine
, const SvxBoxItemLine nLine
,
3065 SvxBoxInfoItemValidFlags nValidFlag
, const bool bBorder
= true)
3067 const SvxBoxInfoItemLine
nInnerLine(bBorder
? SvxBoxInfoItemLine::HORI
: ((nValidFlag
& SvxBoxInfoItemValidFlags::HORI
) ? SvxBoxInfoItemLine::HORI
: SvxBoxInfoItemLine::VERT
));
3068 BoxItemWrapper
aBoxItem(rLinesState
.rBoxItem
, rLinesState
.rBoxInfoItem
, nLine
, nInnerLine
, bBorder
);
3069 bool& rbSet(bBorder
? rLinesState
.aBorderSet
[nLine
] : rLinesState
.aInnerLineSet
[nInnerLine
]);
3073 bool& rbIndeterminate(bBorder
? rLinesState
.aBorderIndeterminate
[nLine
] : rLinesState
.aInnerLineIndeterminate
[nInnerLine
]);
3074 if (!rbIndeterminate
)
3076 const SvxBorderLine
* const pMergedLine(aBoxItem
.getLine());
3077 if ((pLine
&& !pMergedLine
) || (!pLine
&& pMergedLine
) || (pLine
&& (*pLine
!= *pMergedLine
)))
3079 aBoxItem
.setLine(nullptr);
3080 rbIndeterminate
= true;
3086 aBoxItem
.setLine(pLine
);
3091 void lcl_MergeBorderOrInnerLine(
3092 LinesState
& rLinesState
, const SvxBorderLine
* const pLine
, const SvxBoxItemLine nLine
,
3093 SvxBoxInfoItemValidFlags nValidFlag
, const bool bBorder
)
3096 lcl_MergeBorderLine(rLinesState
, pLine
, nLine
, nValidFlag
);
3099 const bool bVertical
= (nLine
== SvxBoxItemLine::LEFT
) || (nLine
== SvxBoxItemLine::RIGHT
);
3100 lcl_MergeBorderLine(rLinesState
, pLine
, nLine
, bVertical
? SvxBoxInfoItemValidFlags::VERT
: SvxBoxInfoItemValidFlags::HORI
, false);
3104 void lcl_MergeDistance(
3105 LinesState
& rLinesState
, const SvxBoxItemLine nIndex
, const sal_uInt16 nDistance
)
3107 if (rLinesState
.aDistanceSet
[nIndex
])
3109 if (!rLinesState
.bDistanceIndeterminate
)
3110 rLinesState
.bDistanceIndeterminate
= nDistance
!= rLinesState
.aDistance
[nIndex
];
3114 rLinesState
.aDistance
[nIndex
] = nDistance
;
3115 rLinesState
.aDistanceSet
[nIndex
] = true;
3119 void lcl_MergeCommonBorderAttr(LinesState
& rLinesState
, const SvxBoxItem
& rCellBoxItem
, const CellPosFlag nCellPosFlags
)
3121 if (nCellPosFlags
& (CellPosFlag::Before
|CellPosFlag::After
|CellPosFlag::Upper
|CellPosFlag::Lower
))
3123 // current cell is outside the selection
3125 if (!(nCellPosFlags
& (CellPosFlag::Before
|CellPosFlag::After
))) // check if it's not any corner
3127 if (nCellPosFlags
& CellPosFlag::Upper
)
3128 lcl_MergeBorderLine(rLinesState
, rCellBoxItem
.GetBottom(), SvxBoxItemLine::TOP
, SvxBoxInfoItemValidFlags::TOP
);
3129 else if (nCellPosFlags
& CellPosFlag::Lower
)
3130 lcl_MergeBorderLine(rLinesState
, rCellBoxItem
.GetTop(), SvxBoxItemLine::BOTTOM
, SvxBoxInfoItemValidFlags::BOTTOM
);
3132 else if (!(nCellPosFlags
& (CellPosFlag::Upper
|CellPosFlag::Lower
))) // check if it's not any corner
3134 if (nCellPosFlags
& CellPosFlag::Before
)
3135 lcl_MergeBorderLine(rLinesState
, rCellBoxItem
.GetRight(), SvxBoxItemLine::LEFT
, SvxBoxInfoItemValidFlags::LEFT
);
3136 else if (nCellPosFlags
& CellPosFlag::After
)
3137 lcl_MergeBorderLine(rLinesState
, rCellBoxItem
.GetLeft(), SvxBoxItemLine::RIGHT
, SvxBoxInfoItemValidFlags::RIGHT
);
3140 // NOTE: inner distances for cells outside the selected range
3141 // are not relevant -> we ignore them.
3145 // current cell is inside the selection
3147 lcl_MergeBorderOrInnerLine(rLinesState
, rCellBoxItem
.GetTop(), SvxBoxItemLine::TOP
, SvxBoxInfoItemValidFlags::TOP
, static_cast<bool>(nCellPosFlags
& CellPosFlag::Top
));
3148 lcl_MergeBorderOrInnerLine(rLinesState
, rCellBoxItem
.GetBottom(), SvxBoxItemLine::BOTTOM
, SvxBoxInfoItemValidFlags::BOTTOM
, static_cast<bool>(nCellPosFlags
& CellPosFlag::Bottom
));
3149 lcl_MergeBorderOrInnerLine(rLinesState
, rCellBoxItem
.GetLeft(), SvxBoxItemLine::LEFT
, SvxBoxInfoItemValidFlags::LEFT
, static_cast<bool>(nCellPosFlags
& CellPosFlag::Left
));
3150 lcl_MergeBorderOrInnerLine(rLinesState
, rCellBoxItem
.GetRight(), SvxBoxItemLine::RIGHT
, SvxBoxInfoItemValidFlags::RIGHT
, static_cast<bool>(nCellPosFlags
& CellPosFlag::Right
));
3152 lcl_MergeDistance(rLinesState
, SvxBoxItemLine::TOP
, rCellBoxItem
.GetDistance(SvxBoxItemLine::TOP
));
3153 lcl_MergeDistance(rLinesState
, SvxBoxItemLine::BOTTOM
, rCellBoxItem
.GetDistance(SvxBoxItemLine::BOTTOM
));
3154 lcl_MergeDistance(rLinesState
, SvxBoxItemLine::LEFT
, rCellBoxItem
.GetDistance(SvxBoxItemLine::LEFT
));
3155 lcl_MergeDistance(rLinesState
, SvxBoxItemLine::RIGHT
, rCellBoxItem
.GetDistance(SvxBoxItemLine::RIGHT
));
3161 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem
& rBoxItem
, SvxBoxInfoItem
& rBoxInfoItem
) const
3166 const sal_Int32 nRowCount
= mxTable
->getRowCount();
3167 const sal_Int32 nColCount
= mxTable
->getColumnCount();
3168 if( !(nRowCount
&& nColCount
) )
3171 CellPos aStart
, aEnd
;
3172 const_cast< SvxTableController
* >( this )->getSelectedCells( aStart
, aEnd
);
3174 // We are adding one more row/column around the block of selected cells.
3175 // We will be checking the adjoining border of these too.
3176 const sal_Int32 nLastRow
= std::min( aEnd
.mnRow
+ 2, nRowCount
);
3177 const sal_Int32 nLastCol
= std::min( aEnd
.mnCol
+ 2, nColCount
);
3179 rBoxInfoItem
.SetValid( SvxBoxInfoItemValidFlags::ALL
, false );
3180 LinesState
aLinesState( rBoxItem
, rBoxInfoItem
);
3182 /* Here we go through all the selected cells (enhanced by
3183 * the adjoining row/column on each side) and determine the
3184 * lines for presentation. The algorithm is simple:
3185 * 1. if a border or inner line is set (or unset) in all
3186 * cells to the same value, it will be used.
3187 * 2. if a border or inner line is set only in some cells,
3188 * it will be set to indeterminate state (SetValid() on
3191 for( sal_Int32 nRow
= std::max( aStart
.mnRow
- 1, sal_Int32(0) ); nRow
< nLastRow
; nRow
++ )
3193 CellPosFlag nRowFlags
= CellPosFlag::NONE
;
3194 nRowFlags
|= (nRow
== aStart
.mnRow
) ? CellPosFlag::Top
: CellPosFlag::NONE
;
3195 nRowFlags
|= (nRow
== aEnd
.mnRow
) ? CellPosFlag::Bottom
: CellPosFlag::NONE
;
3196 nRowFlags
|= (nRow
< aStart
.mnRow
) ? CellPosFlag::Upper
: CellPosFlag::NONE
;
3197 nRowFlags
|= (nRow
> aEnd
.mnRow
) ? CellPosFlag::Lower
: CellPosFlag::NONE
;
3199 for( sal_Int32 nCol
= std::max( aStart
.mnCol
- 1, sal_Int32(0) ); nCol
< nLastCol
; nCol
++ )
3201 CellRef
xCell( mxTable
->getCell( nCol
, nRow
) );
3205 CellPosFlag nCellPosFlags
= nRowFlags
;
3206 nCellPosFlags
|= (nCol
== aStart
.mnCol
) ? CellPosFlag::Left
: CellPosFlag::NONE
;
3207 nCellPosFlags
|= (nCol
== aEnd
.mnCol
) ? CellPosFlag::Right
: CellPosFlag::NONE
;
3208 nCellPosFlags
|= (nCol
< aStart
.mnCol
) ? CellPosFlag::Before
: CellPosFlag::NONE
;
3209 nCellPosFlags
|= (nCol
> aEnd
.mnCol
) ? CellPosFlag::After
: CellPosFlag::NONE
;
3211 const SfxItemSet
& rSet
= xCell
->GetItemSet();
3212 SvxBoxItem
aCellBoxItem(TextDistancesToSvxBoxItem(rSet
));
3213 lcl_MergeCommonBorderAttr( aLinesState
, aCellBoxItem
, nCellPosFlags
);
3217 if (!aLinesState
.aBorderIndeterminate
[SvxBoxItemLine::TOP
])
3218 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::TOP
);
3219 if (!aLinesState
.aBorderIndeterminate
[SvxBoxItemLine::BOTTOM
])
3220 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::BOTTOM
);
3221 if (!aLinesState
.aBorderIndeterminate
[SvxBoxItemLine::LEFT
])
3222 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::LEFT
);
3223 if (!aLinesState
.aBorderIndeterminate
[SvxBoxItemLine::RIGHT
])
3224 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::RIGHT
);
3225 if (!aLinesState
.aInnerLineIndeterminate
[SvxBoxInfoItemLine::HORI
])
3226 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::HORI
);
3227 if (!aLinesState
.aInnerLineIndeterminate
[SvxBoxInfoItemLine::VERT
])
3228 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::VERT
);
3230 if (!aLinesState
.bDistanceIndeterminate
)
3232 if (aLinesState
.aDistanceSet
[SvxBoxItemLine::TOP
])
3233 aLinesState
.rBoxItem
.SetDistance(aLinesState
.aDistance
[SvxBoxItemLine::TOP
], SvxBoxItemLine::TOP
);
3234 if (aLinesState
.aDistanceSet
[SvxBoxItemLine::BOTTOM
])
3235 aLinesState
.rBoxItem
.SetDistance(aLinesState
.aDistance
[SvxBoxItemLine::BOTTOM
], SvxBoxItemLine::BOTTOM
);
3236 if (aLinesState
.aDistanceSet
[SvxBoxItemLine::LEFT
])
3237 aLinesState
.rBoxItem
.SetDistance(aLinesState
.aDistance
[SvxBoxItemLine::LEFT
], SvxBoxItemLine::LEFT
);
3238 if (aLinesState
.aDistanceSet
[SvxBoxItemLine::RIGHT
])
3239 aLinesState
.rBoxItem
.SetDistance(aLinesState
.aDistance
[SvxBoxItemLine::RIGHT
], SvxBoxItemLine::RIGHT
);
3240 aLinesState
.rBoxInfoItem
.SetValid(SvxBoxInfoItemValidFlags::DISTANCE
);
3244 bool SvxTableController::selectRow( sal_Int32 row
)
3248 CellPos
aStart( 0, row
), aEnd( mxTable
->getColumnCount() - 1, row
);
3249 StartSelection( aEnd
);
3250 gotoCell( aStart
, true, nullptr );
3254 bool SvxTableController::selectColumn( sal_Int32 column
)
3258 CellPos
aStart( column
, 0 ), aEnd( column
, mxTable
->getRowCount() - 1 );
3259 StartSelection( aEnd
);
3260 gotoCell( aStart
, true, nullptr );
3264 bool SvxTableController::deselectRow( sal_Int32 row
)
3268 CellPos
aStart( 0, row
), aEnd( mxTable
->getColumnCount() - 1, row
);
3269 StartSelection( aEnd
);
3270 gotoCell( aStart
, false, nullptr );
3274 bool SvxTableController::deselectColumn( sal_Int32 column
)
3278 CellPos
aStart( column
, 0 ), aEnd( column
, mxTable
->getRowCount() - 1 );
3279 StartSelection( aEnd
);
3280 gotoCell( aStart
, false, nullptr );
3284 bool SvxTableController::isRowSelected( sal_Int32 nRow
)
3286 if( hasSelectedCells() )
3288 CellPos aFirstPos
, aLastPos
;
3289 getSelectedCells( aFirstPos
, aLastPos
);
3290 if( (aFirstPos
.mnCol
== 0) && (nRow
>= aFirstPos
.mnRow
&& nRow
<= aLastPos
.mnRow
) && (mxTable
->getColumnCount() - 1 == aLastPos
.mnCol
) )
3296 bool SvxTableController::isColumnSelected( sal_Int32 nColumn
)
3298 if( hasSelectedCells() )
3300 CellPos aFirstPos
, aLastPos
;
3301 getSelectedCells( aFirstPos
, aLastPos
);
3302 if( (aFirstPos
.mnRow
== 0) && (nColumn
>= aFirstPos
.mnCol
&& nColumn
<= aLastPos
.mnCol
) && (mxTable
->getRowCount() - 1 == aLastPos
.mnRow
) )
3308 bool SvxTableController::isRowHeader()
3310 if(!checkTableObject())
3313 SdrTableObj
& rTableObj(*mxTableObj
.get());
3314 TableStyleSettings
aSettings(rTableObj
.getTableStyleSettings());
3316 return aSettings
.mbUseFirstRow
;
3319 bool SvxTableController::isColumnHeader()
3321 if(!checkTableObject())
3324 SdrTableObj
& rTableObj(*mxTableObj
.get());
3325 TableStyleSettings
aSettings(rTableObj
.getTableStyleSettings());
3327 return aSettings
.mbUseFirstColumn
;
3330 bool SvxTableController::setCursorLogicPosition(const Point
& rPosition
, bool bPoint
)
3332 rtl::Reference
<SdrTableObj
> pTableObj
= mxTableObj
.get();
3333 if (pTableObj
->GetObjIdentifier() != SdrObjKind::Table
)
3337 if (pTableObj
->CheckTableHit(rPosition
, aCellPos
.mnCol
, aCellPos
.mnRow
) != TableHitKind::NONE
)
3339 // Position is a table cell.
3340 if (mbCellSelectionMode
)
3342 // We have a table selection already: adjust the point or the mark.
3344 setSelectedCells(maCursorFirstPos
, aCellPos
);
3346 setSelectedCells(aCellPos
, maCursorLastPos
);
3349 else if (aCellPos
!= maMouseDownPos
)
3351 // No selection, but rPosition is at another cell: start table selection.
3352 StartSelection(maMouseDownPos
);
3353 // Update graphic selection, should be hidden now.
3354 mrView
.AdjustMarkHdl();
3363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */