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