Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / table / tablecontroller.cxx
blob75c49f599e0273a116027628f0504b0281ca6b7b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
22 #include <svx/sdr/table/tablecontroller.hxx>
23 #include <tablemodel.hxx>
25 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
26 #include <com/sun/star/container/XIndexAccess.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/table/XMergeableCellRange.hpp>
30 #include <com/sun/star/table/XMergeableCell.hpp>
32 #include <sal/config.h>
33 #include <sal/log.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
38 #include <svl/whiter.hxx>
39 #include <svl/stritem.hxx>
41 #include <sfx2/request.hxx>
43 #include <svx/svdotable.hxx>
44 #include <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 <svx/selectioncontroller.hxx>
52 #include <svx/svdmodel.hxx>
53 #include <svx/sdrpaintwindow.hxx>
54 #include <svx/svxdlg.hxx>
55 #include <editeng/boxitem.hxx>
56 #include <cell.hxx>
57 #include <editeng/borderline.hxx>
58 #include <editeng/colritem.hxx>
59 #include <editeng/lineitem.hxx>
60 #include <svx/strings.hrc>
61 #include <svx/dialmgr.hxx>
62 #include <svx/svdpage.hxx>
63 #include <svx/sdmetitm.hxx>
64 #include <svx/sdtditm.hxx>
65 #include "tableundo.hxx"
66 #include "tablelayouter.hxx"
67 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
68 #include <memory>
69 #include <o3tl/enumarray.hxx>
70 #include <o3tl/enumrange.hxx>
71 #include <cppuhelper/implbase.hxx>
72 #include <comphelper/lok.hxx>
73 #include <sfx2/viewsh.hxx>
74 #include <editeng/editview.hxx>
75 #include <tools/UnitConversion.hxx>
76 #include <tools/diagnose_ex.h>
78 using ::editeng::SvxBorderLine;
79 using namespace sdr::table;
80 using namespace ::com::sun::star;
81 using namespace ::com::sun::star::uno;
82 using namespace ::com::sun::star::table;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::container;
85 using namespace ::com::sun::star::text;
86 using namespace ::com::sun::star::style;
88 namespace {
90 enum class CellPosFlag // signals the relative position of a cell to a selection
92 NONE = 0x0000, // not set or inside
93 // row
94 Before = 0x0001,
95 Left = 0x0002,
96 Right = 0x0004,
97 After = 0x0008,
98 // column
99 Upper = 0x0010,
100 Top = 0x0020,
101 Bottom = 0x0040,
102 Lower = 0x0080
107 namespace o3tl
108 { template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
110 namespace sdr::table {
112 namespace {
114 class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
116 public:
117 explicit SvxTableControllerModifyListener( SvxTableController* pController )
118 : mpController( pController ) {}
120 // XModifyListener
121 virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
123 // XEventListener
124 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
126 SvxTableController* mpController;
131 // XModifyListener
134 void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
136 if( mpController )
137 mpController->onTableModified();
141 // XEventListener
144 void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
146 mpController = nullptr;
152 rtl::Reference< sdr::SelectionController > CreateTableController(
153 SdrView& rView,
154 const SdrTableObj& rObj,
155 const rtl::Reference< sdr::SelectionController >& xRefController )
157 return SvxTableController::create(rView, rObj, xRefController);
161 rtl::Reference< sdr::SelectionController > SvxTableController::create(
162 SdrView& rView,
163 const SdrTableObj& rObj,
164 const rtl::Reference< sdr::SelectionController >& xRefController )
166 if( xRefController.is() )
168 SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
170 if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
172 return xRefController;
176 return new SvxTableController(rView, rObj);
180 SvxTableController::SvxTableController(
181 SdrView& rView,
182 const SdrTableObj& rObj)
183 : mbCellSelectionMode(false)
184 ,mbHasJustMerged(false)
185 ,mbLeftButtonDown(false)
186 ,mrView(rView)
187 ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
188 ,mnUpdateEvent( nullptr )
190 mxTableObj->getActiveCellPos( maCursorFirstPos );
191 maCursorLastPos = maCursorFirstPos;
193 Reference< XTable > xTable( mxTableObj->getTable() );
194 if( xTable.is() )
196 mxModifyListener = new SvxTableControllerModifyListener( this );
197 xTable->addModifyListener( mxModifyListener );
199 mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
203 SvxTableController::~SvxTableController()
205 if( mnUpdateEvent )
207 Application::RemoveUserEvent( mnUpdateEvent );
210 if( mxModifyListener.is() && mxTableObj )
212 Reference< XTable > xTable( mxTableObj->getTable() );
213 if( xTable.is() )
215 xTable->removeModifyListener( mxModifyListener );
216 mxModifyListener.clear();
221 bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
223 if(!checkTableObject())
224 return false;
226 SdrTableObj& rTableObj(*mxTableObj);
227 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
229 // check if we are read only
230 if( rModel.IsReadOnly())
232 switch( rKEvt.GetKeyCode().GetCode() )
234 case awt::Key::DOWN:
235 case awt::Key::UP:
236 case awt::Key::LEFT:
237 case awt::Key::RIGHT:
238 case awt::Key::TAB:
239 case awt::Key::HOME:
240 case awt::Key::END:
241 case awt::Key::NUM2:
242 case awt::Key::NUM4:
243 case awt::Key::NUM6:
244 case awt::Key::NUM8:
245 case awt::Key::ESCAPE:
246 case awt::Key::F2:
247 break;
248 default:
249 // tell the view we eat the event, no further processing needed
250 return true;
254 TblAction nAction = getKeyboardAction(rKEvt);
256 return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
259 namespace {
261 Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
263 if (!pWindow)
264 return rPoint;
266 return pWindow->PixelToLogic(rPoint);
271 bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
273 if (comphelper::LibreOfficeKit::isActive() && !pWindow)
275 // Tiled rendering: get the window that has the disabled map mode.
276 if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
278 if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
279 pWindow = static_cast<vcl::Window*>(pOutputDevice);
283 if( !pWindow || !checkTableObject() )
284 return false;
286 SdrViewEvent aVEvt;
287 if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
288 return false;
290 TableHitKind eHit = mxTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
292 mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
294 if( eHit == TableHitKind::Cell )
296 StartSelection( maMouseDownPos );
297 return true;
300 if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
301 return true; // right click will become context menu
303 // for cell selection with the mouse remember our first hit
304 if( mbLeftButtonDown )
306 RemoveSelection();
308 SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
310 if( pHdl )
312 mbLeftButtonDown = false;
314 else
316 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
318 if (!pTableObj || eHit == TableHitKind::NONE)
320 mbLeftButtonDown = false;
325 if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
327 bool bEmptyOutliner = false;
328 if (Outliner* pOutliner = mrView.GetTextEditOutliner())
330 if (pOutliner->GetParagraphCount() == 1)
332 if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
333 bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
336 if (bEmptyOutliner)
338 // Tiled rendering: a left double-click in an empty cell: select it.
339 StartSelection(maMouseDownPos);
340 setSelectedCells(maMouseDownPos, maMouseDownPos);
341 // Update graphic selection, should be hidden now.
342 mrView.AdjustMarkHdl();
343 return true;
347 return false;
351 bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
353 if( !checkTableObject() )
354 return false;
356 mbLeftButtonDown = false;
358 return rMEvt.GetClicks() == 2;
362 bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
364 if( !checkTableObject() )
365 return false;
367 SdrTableObj* pTableObj = mxTableObj.get();
368 CellPos aPos;
369 if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
371 if(aPos != maMouseDownPos)
373 if( mbCellSelectionMode )
375 setSelectedCells( maMouseDownPos, aPos );
376 return true;
378 else
380 StartSelection( maMouseDownPos );
383 else if( mbCellSelectionMode )
385 UpdateSelection( aPos );
386 return true;
389 return false;
393 void SvxTableController::onSelectionHasChanged()
395 bool bSelected = false;
397 SdrTableObj* pTableObj = mxTableObj.get();
398 if( pTableObj && pTableObj->IsTextEditActive() )
400 pTableObj->getActiveCellPos( maCursorFirstPos );
401 maCursorLastPos = maCursorFirstPos;
402 mbCellSelectionMode = false;
404 else
406 const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
407 if( rMarkList.GetMarkCount() == 1 )
408 bSelected = mxTableObj.get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
409 /* fdo#46186 Selecting the table means selecting the entire cells */
410 if (!hasSelectedCells() && pTableObj)
412 maCursorFirstPos = SdrTableObj::getFirstCell();
413 maCursorLastPos = pTableObj->getLastCell();
414 mbCellSelectionMode=true;
418 if( bSelected )
420 updateSelectionOverlay();
422 else
424 destroySelectionOverlay();
429 void SvxTableController::GetState( SfxItemSet& rSet )
431 if(!mxTable.is() || !mxTableObj.is())
432 return;
434 SdrTableObj& rTableObj(*mxTableObj);
435 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
436 std::unique_ptr<SfxItemSet> xSet;
437 bool bVertDone(false);
439 // Iterate over all requested items in the set.
440 SfxWhichIter aIter( rSet );
441 sal_uInt16 nWhich = aIter.FirstWhich();
442 while (nWhich)
444 switch (nWhich)
446 case SID_TABLE_VERT_BOTTOM:
447 case SID_TABLE_VERT_CENTER:
448 case SID_TABLE_VERT_NONE:
450 if(!bVertDone)
452 if (!xSet)
454 xSet.reset(new SfxItemSet(rModel.GetItemPool()));
455 MergeAttrFromSelectedCells(*xSet, false);
458 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
460 if (xSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
461 eAdj = xSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
463 rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
464 rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
465 rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
466 bVertDone = true;
468 break;
470 case SID_TABLE_DELETE_ROW:
471 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
472 rSet.DisableItem(SID_TABLE_DELETE_ROW);
473 break;
474 case SID_TABLE_DELETE_COL:
475 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
476 rSet.DisableItem(SID_TABLE_DELETE_COL);
477 break;
478 case SID_TABLE_DELETE_TABLE:
479 if( !mxTable.is() )
480 rSet.DisableItem(SID_TABLE_DELETE_TABLE);
481 break;
482 case SID_TABLE_MERGE_CELLS:
483 if( !mxTable.is() || !hasSelectedCells() )
484 rSet.DisableItem(SID_TABLE_MERGE_CELLS);
485 break;
486 case SID_TABLE_SPLIT_CELLS:
487 if( !hasSelectedCells() || !mxTable.is() )
488 rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
489 break;
491 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
492 case SID_TABLE_DISTRIBUTE_COLUMNS:
493 case SID_TABLE_DISTRIBUTE_ROWS:
495 bool bDistributeColumns = false;
496 bool bDistributeRows = false;
497 if( mxTable.is() )
499 CellPos aStart, aEnd;
500 getSelectedCells( aStart, aEnd );
502 bDistributeColumns = aStart.mnCol != aEnd.mnCol;
503 bDistributeRows = aStart.mnRow != aEnd.mnRow;
505 if( !bDistributeColumns )
506 rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
507 if( !bDistributeRows )
509 rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
510 rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
512 break;
515 default:
516 break;
518 nWhich = aIter.NextWhich();
523 void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
525 if(!checkTableObject())
526 return;
528 SdrTableObj& rTableObj(*mxTableObj);
529 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
530 bool bInsertAfter = true;
531 sal_uInt16 nCount = 0;
533 if( pArgs )
535 const SfxPoolItem* pItem = nullptr;
536 pArgs->GetItemState(nSId, false, &pItem);
537 if (pItem)
539 nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
540 if(SfxItemState::SET == pArgs->GetItemState(SID_TABLE_PARAM_INSERT_AFTER, true, &pItem))
541 bInsertAfter = static_cast<const SfxBoolItem*>(pItem)->GetValue();
545 CellPos aStart, aEnd;
546 if( hasSelectedCells() )
548 getSelectedCells( aStart, aEnd );
550 else
552 if( bInsertAfter )
554 aStart.mnCol = mxTable->getColumnCount() - 1;
555 aStart.mnRow = mxTable->getRowCount() - 1;
556 aEnd = aStart;
560 if( rTableObj.IsTextEditActive() )
561 mrView.SdrEndTextEdit(true);
563 RemoveSelection();
565 const OUString sSize( "Size" );
566 const bool bUndo(rModel.IsUndoEnabled());
568 switch( nSId )
570 case SID_TABLE_INSERT_COL:
572 TableModelNotifyGuard aGuard( mxTable.get() );
574 if( bUndo )
576 rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
577 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
580 Reference< XTableColumns > xCols( mxTable->getColumns() );
581 const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
582 const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
583 xCols->insertByIndex( nNewStartColumn, nNewColumns );
585 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
587 // Resolves fdo#61540
588 // On Insert before, the reference column whose size is going to be
589 // used for newly created column(s) is wrong. As the new columns are
590 // inserted before the reference column, the reference column moved
591 // to the new position by no., of new columns i.e (earlier+newcolumns).
592 Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
593 setPropertyValue( sSize,
594 Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
595 getPropertyValue( sSize ) );
598 // Copy cell properties
599 sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
600 sal_Int32 nRowSpan = 0;
601 bool bNewSpan = false;
603 for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
605 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
607 // When we insert new COLUMNs, we want to copy ROW spans.
608 if (xSourceCell.is() && nRowSpan == 0)
610 // we are not in a span yet. Let's find out if the current cell is in a span.
611 sal_Int32 nColSpan = sal_Int32();
612 sal_Int32 nSpanInfoCol = sal_Int32();
614 if( xSourceCell->getRowSpan() > 1 )
616 // The current cell is the top-left cell in a span.
617 // Get the span info and propagate it to the target.
618 nRowSpan = xSourceCell->getRowSpan();
619 nColSpan = xSourceCell->getColumnSpan();
620 nSpanInfoCol = nPropSrcCol;
622 else if( xSourceCell->isMerged() )
624 // The current cell is a middle cell in a 2D span.
625 // Look for the top-left cell in the span.
626 for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
628 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
629 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
631 nRowSpan = xMergeInfoCell->getRowSpan();
632 nColSpan = xMergeInfoCell->getColumnSpan();
633 break;
636 if( nRowSpan == 1 )
637 nRowSpan = 0;
640 // The target columns are outside the span; Start a new span.
641 if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
642 bNewSpan = true;
645 // Now copy the properties from the source to the targets
646 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
648 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
649 if( xTargetCell.is() )
651 if( nRowSpan > 0 )
653 if( bNewSpan )
654 xTargetCell->merge( 1, nRowSpan );
655 else
656 xTargetCell->setMerged();
658 xTargetCell->copyFormatFrom( xSourceCell );
662 if( nRowSpan > 0 )
664 --nRowSpan;
665 bNewSpan = false;
669 if( bUndo )
670 rModel.EndUndo();
672 aStart.mnCol = nNewStartColumn;
673 aStart.mnRow = 0;
674 aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
675 aEnd.mnRow = mxTable->getRowCount() - 1;
676 break;
679 case SID_TABLE_INSERT_ROW:
681 TableModelNotifyGuard aGuard( mxTable.get() );
683 if( bUndo )
685 rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
686 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
689 Reference< XTableRows > xRows( mxTable->getRows() );
690 const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
691 const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
692 xRows->insertByIndex( nNewRowStart, nNewRows );
694 for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
696 Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
697 setPropertyValue( sSize,
698 Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
699 getPropertyValue( sSize ) );
702 // Copy the cell properties
703 sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
704 sal_Int32 nColSpan = 0;
705 bool bNewSpan = false;
707 for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
709 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
711 if (!xSourceCell.is())
712 continue;
714 // When we insert new ROWs, we want to copy COLUMN spans.
715 if( nColSpan == 0 )
717 // we are not in a span yet. Let's find out if the current cell is in a span.
718 sal_Int32 nRowSpan = sal_Int32();
719 sal_Int32 nSpanInfoRow = sal_Int32();
721 if( xSourceCell->getColumnSpan() > 1 )
723 // The current cell is the top-left cell in a span.
724 // Get the span info and propagate it to the target.
725 nColSpan = xSourceCell->getColumnSpan();
726 nRowSpan = xSourceCell->getRowSpan();
727 nSpanInfoRow = nPropSrcRow;
729 else if( xSourceCell->isMerged() )
731 // The current cell is a middle cell in a 2D span.
732 // Look for the top-left cell in the span.
733 for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
735 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
736 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
738 nColSpan = xMergeInfoCell->getColumnSpan();
739 nRowSpan = xMergeInfoCell->getRowSpan();
740 break;
743 if( nColSpan == 1 )
744 nColSpan = 0;
747 // Inserted rows are outside the span; Start a new span.
748 if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
749 bNewSpan = true;
752 // Now copy the properties from the source to the targets
753 for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
755 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
756 if( xTargetCell.is() )
758 if( nColSpan > 0 )
760 if( bNewSpan )
761 xTargetCell->merge( nColSpan, 1 );
762 else
763 xTargetCell->setMerged();
765 xTargetCell->copyFormatFrom( xSourceCell );
769 if( nColSpan > 0 )
771 --nColSpan;
772 bNewSpan = false;
776 if( bUndo )
777 rModel.EndUndo();
779 aStart.mnCol = 0;
780 aStart.mnRow = nNewRowStart;
781 aEnd.mnCol = mxTable->getColumnCount() - 1;
782 aEnd.mnRow = aStart.mnRow + nNewRows - 1;
783 break;
787 StartSelection( aStart );
788 UpdateSelection( aEnd );
792 void SvxTableController::onDelete( sal_uInt16 nSId )
794 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
795 if( !pTableObj || !mxTable.is() )
796 return;
798 if( nSId == SID_TABLE_DELETE_TABLE )
800 if( pTableObj->IsTextEditActive() )
801 mrView.SdrEndTextEdit(true);
803 mrView.DeleteMarkedObj();
805 else if( hasSelectedCells() )
807 CellPos aStart, aEnd;
808 getSelectedCells( aStart, aEnd );
810 if( pTableObj->IsTextEditActive() )
811 mrView.SdrEndTextEdit(true);
813 RemoveSelection();
815 bool bDeleteTable = false;
816 switch( nSId )
818 case SID_TABLE_DELETE_COL:
820 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
821 if( nRemovedColumns == mxTable->getColumnCount() )
823 bDeleteTable = true;
825 else
827 Reference< XTableColumns > xCols( mxTable->getColumns() );
828 xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
830 break;
833 case SID_TABLE_DELETE_ROW:
835 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
836 if( nRemovedRows == mxTable->getRowCount() )
838 bDeleteTable = true;
840 else
842 Reference< XTableRows > xRows( mxTable->getRows() );
843 xRows->removeByIndex( aStart.mnRow, nRemovedRows );
845 break;
849 if( bDeleteTable )
850 mrView.DeleteMarkedObj();
851 else
852 UpdateTableShape();
857 void SvxTableController::onSelect( sal_uInt16 nSId )
859 if( !mxTable.is() )
860 return;
862 const sal_Int32 nRowCount = mxTable->getRowCount();
863 const sal_Int32 nColCount = mxTable->getColumnCount();
864 if( !(nRowCount && nColCount) )
865 return;
867 CellPos aStart, aEnd;
868 getSelectedCells( aStart, aEnd );
870 switch( nSId )
872 case SID_TABLE_SELECT_ALL:
873 aEnd.mnCol = 0; aEnd.mnRow = 0;
874 aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
875 break;
876 case SID_TABLE_SELECT_COL:
877 aEnd.mnRow = nRowCount - 1;
878 aStart.mnRow = 0;
879 break;
880 case SID_TABLE_SELECT_ROW:
881 aEnd.mnCol = nColCount - 1;
882 aStart.mnCol = 0;
883 break;
886 StartSelection( aEnd );
887 gotoCell( aStart, true, nullptr );
890 namespace
892 SvxBoxItem mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet& rAttrSet)
894 // merge drawing layer text distance items into SvxBoxItem used by the dialog
895 SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
896 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
897 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
898 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
899 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
900 return aBoxItem;
904 void SvxTableController::onFormatTable(const SfxRequest& rReq)
906 if(!mxTableObj.is())
907 return;
909 SdrTableObj& rTableObj(*mxTableObj);
910 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
911 const SfxItemSet* pArgs = rReq.GetArgs();
913 if(pArgs)
914 return;
916 SfxItemSet aNewAttr(rModel.GetItemPool());
918 // merge drawing layer text distance items into SvxBoxItem used by the dialog
919 SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
921 SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
923 MergeAttrFromSelectedCells(aNewAttr, false);
924 FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
925 aNewAttr.Put( aBoxItem );
926 aNewAttr.Put( aBoxInfoItem );
928 // Fill in shadow properties.
929 const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
930 for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
932 if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
934 continue;
937 aNewAttr.Put(rTableItemSet.Get(nWhich));
940 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
941 VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
942 rReq.GetFrameWeld(),
943 &aNewAttr,
944 rModel) );
946 // Even Cancel Button is returning positive(101) value,
947 xDlg->StartExecuteAsync([xDlg, this, aBoxItem, aBoxInfoItem](int nResult){
948 if (nResult == RET_OK)
950 SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
952 //Only properties that were unchanged by the dialog appear in this
953 //itemset. We had constructed these two properties from other
954 //ones, so if they were not changed, then forcible set them back to
955 //their originals in the new result set so we can decompose that
956 //unchanged state back to their input properties
957 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
959 aNewSet.Put(aBoxItem);
961 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
963 aNewSet.Put(aBoxInfoItem);
966 SvxBoxItem aNewBoxItem( aNewSet.Get( SDRATTR_TABLE_BORDER ) );
968 if( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) != aBoxItem.GetDistance( SvxBoxItemLine::LEFT ) )
969 aNewSet.Put(makeSdrTextLeftDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) ) );
971 if( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) != aBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) )
972 aNewSet.Put(makeSdrTextRightDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) ) );
974 if( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) != aBoxItem.GetDistance( SvxBoxItemLine::TOP ) )
975 aNewSet.Put(makeSdrTextUpperDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) ) );
977 if( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) != aBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
978 aNewSet.Put(makeSdrTextLowerDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) ) );
980 if (checkTableObject() && mxTable.is())
982 // Create a single undo action when applying the result of the dialog.
983 SdrTableObj& rTableObject(*mxTableObj);
984 SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
985 bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
986 if (bUndo)
988 rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
991 this->SetAttrToSelectedCells(aNewSet, false);
993 this->SetAttrToSelectedShape(aNewSet);
995 if (bUndo)
997 rSdrModel.EndUndo();
1002 xDlg->disposeOnce();
1006 void SvxTableController::Execute( SfxRequest& rReq )
1008 const sal_uInt16 nSId = rReq.GetSlot();
1009 switch( nSId )
1011 case SID_TABLE_INSERT_ROW:
1012 case SID_TABLE_INSERT_COL:
1013 onInsert( nSId, rReq.GetArgs() );
1014 break;
1015 case SID_TABLE_DELETE_ROW:
1016 case SID_TABLE_DELETE_COL:
1017 case SID_TABLE_DELETE_TABLE:
1018 onDelete( nSId );
1019 break;
1020 case SID_TABLE_SELECT_ALL:
1021 case SID_TABLE_SELECT_COL:
1022 case SID_TABLE_SELECT_ROW:
1023 onSelect( nSId );
1024 break;
1025 case SID_FORMAT_TABLE_DLG:
1026 onFormatTable( rReq );
1027 break;
1029 case SID_FRAME_LINESTYLE:
1030 case SID_FRAME_LINECOLOR:
1031 case SID_ATTR_BORDER:
1033 const SfxItemSet* pArgs = rReq.GetArgs();
1034 if( pArgs )
1035 ApplyBorderAttr( *pArgs );
1037 break;
1039 case SID_ATTR_FILL_STYLE:
1041 const SfxItemSet* pArgs = rReq.GetArgs();
1042 if( pArgs )
1043 SetAttributes( *pArgs, false );
1045 break;
1047 case SID_TABLE_MERGE_CELLS:
1048 MergeMarkedCells();
1049 break;
1051 case SID_TABLE_SPLIT_CELLS:
1052 SplitMarkedCells(rReq);
1053 break;
1055 case SID_TABLE_MINIMAL_COLUMN_WIDTH:
1056 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
1057 break;
1059 case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
1060 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
1061 break;
1063 case SID_TABLE_DISTRIBUTE_COLUMNS:
1064 DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
1065 break;
1067 case SID_TABLE_MINIMAL_ROW_HEIGHT:
1068 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
1069 break;
1071 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
1072 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
1073 break;
1075 case SID_TABLE_DISTRIBUTE_ROWS:
1076 DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
1077 break;
1079 case SID_TABLE_VERT_BOTTOM:
1080 case SID_TABLE_VERT_CENTER:
1081 case SID_TABLE_VERT_NONE:
1082 SetVertical( nSId );
1083 break;
1085 case SID_AUTOFORMAT:
1086 case SID_TABLE_SORT_DIALOG:
1087 case SID_TABLE_AUTOSUM:
1088 default:
1089 break;
1091 case SID_TABLE_STYLE:
1092 SetTableStyle( rReq.GetArgs() );
1093 break;
1095 case SID_TABLE_STYLE_SETTINGS:
1096 SetTableStyleSettings( rReq.GetArgs() );
1097 break;
1098 case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
1099 changeTableEdge(rReq);
1100 break;
1104 void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
1106 if(!checkTableObject())
1107 return;
1109 SdrTableObj& rTableObj(*mxTableObj);
1110 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1112 if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
1113 return;
1115 const SfxStringItem* pArg = dynamic_cast< const SfxStringItem* >( &pArgs->Get( SID_TABLE_STYLE ) );
1116 if( !(pArg && mxTable.is()) )
1117 return;
1121 Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
1122 Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
1123 Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
1125 if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
1127 // found table style with the same name
1128 Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
1130 const bool bUndo = rModel.IsUndoEnabled();
1132 if( bUndo )
1134 rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
1135 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1138 rTableObj.setTableStyle( xNewTableStyle );
1140 const sal_Int32 nRowCount = mxTable->getRowCount();
1141 const sal_Int32 nColCount = mxTable->getColumnCount();
1142 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1144 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
1146 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1147 if( xCell.is() )
1149 SfxItemSet aSet( xCell->GetItemSet() );
1150 bool bChanges = false;
1151 SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
1152 SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
1153 if (pStyleSheet)
1155 const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
1157 for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
1159 if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
1161 aSet.ClearItem( nWhich );
1162 bChanges = true;
1167 if( bChanges )
1169 if( bUndo )
1170 xCell->AddUndo();
1172 xCell->SetMergedItemSetAndBroadcast( aSet, true );
1176 catch( Exception& )
1178 TOOLS_WARN_EXCEPTION("svx.table", "");
1182 if( bUndo )
1183 rModel.EndUndo();
1186 catch( Exception& )
1188 TOOLS_WARN_EXCEPTION("svx.table", "");
1192 void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
1194 if(!checkTableObject())
1195 return;
1197 SdrTableObj& rTableObj(*mxTableObj);
1198 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1200 TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
1201 const SfxPoolItem *pPoolItem=nullptr;
1203 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTROWSTYLE, false,&pPoolItem) )
1204 aSettings.mbUseFirstRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1206 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTROWSTYLE, false,&pPoolItem) )
1207 aSettings.mbUseLastRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1209 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGROWSTYLE, false,&pPoolItem) )
1210 aSettings.mbUseRowBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1212 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTCOLUMNSTYLE, false,&pPoolItem) )
1213 aSettings.mbUseFirstColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1215 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTCOLUMNSTYLE, false,&pPoolItem) )
1216 aSettings.mbUseLastColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1218 if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGCOLUMNSTYLE, false,&pPoolItem) )
1219 aSettings.mbUseColumnBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1221 if( aSettings == rTableObj.getTableStyleSettings() )
1222 return;
1224 const bool bUndo(rModel.IsUndoEnabled());
1226 if( bUndo )
1228 rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
1229 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1232 rTableObj.setTableStyleSettings( aSettings );
1234 if( bUndo )
1235 rModel.EndUndo();
1238 void SvxTableController::SetVertical( sal_uInt16 nSId )
1240 if(!checkTableObject())
1241 return;
1243 SdrTableObj& rTableObj(*mxTableObj);
1244 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1246 TableModelNotifyGuard aGuard( mxTable.get() );
1247 const bool bUndo(rModel.IsUndoEnabled());
1249 if (bUndo)
1251 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
1252 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
1255 CellPos aStart, aEnd;
1256 getSelectedCells( aStart, aEnd );
1258 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
1260 switch( nSId )
1262 case SID_TABLE_VERT_BOTTOM:
1263 eAdj = SDRTEXTVERTADJUST_BOTTOM;
1264 break;
1265 case SID_TABLE_VERT_CENTER:
1266 eAdj = SDRTEXTVERTADJUST_CENTER;
1267 break;
1268 //case SID_TABLE_VERT_NONE:
1269 default:
1270 break;
1273 SdrTextVertAdjustItem aItem( eAdj );
1275 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1277 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1279 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1280 if( xCell.is() )
1282 if (bUndo)
1283 xCell->AddUndo();
1284 SfxItemSet aSet(xCell->GetItemSet());
1285 aSet.Put(aItem);
1286 xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
1291 UpdateTableShape();
1293 if (bUndo)
1294 rModel.EndUndo();
1297 void SvxTableController::MergeMarkedCells()
1299 CellPos aStart, aEnd;
1300 getSelectedCells( aStart, aEnd );
1301 SdrTableObj* pTableObj = mxTableObj.get();
1302 if( pTableObj )
1304 if( pTableObj->IsTextEditActive() )
1305 mrView.SdrEndTextEdit(true);
1307 TableModelNotifyGuard aGuard( mxTable.get() );
1308 MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
1312 void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
1314 if(!checkTableObject() || !mxTable.is())
1315 return;
1317 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1318 VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
1320 xDlg->StartExecuteAsync([xDlg, this](int) {
1321 const sal_Int32 nCount = xDlg->GetCount() - 1;
1323 if( nCount < 1 )
1324 return;
1326 CellPos aStart, aEnd;
1327 getSelectedCells( aStart, aEnd );
1328 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
1329 const sal_Int32 nRowCount = mxTable->getRowCount();
1330 const sal_Int32 nColCount = mxTable->getColumnCount();
1331 SdrTableObj& rTableObj(*mxTableObj);
1333 if( rTableObj.IsTextEditActive() )
1334 mrView.SdrEndTextEdit(true);
1336 TableModelNotifyGuard aGuard( mxTable.get() );
1337 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1338 const bool bUndo(rModel.IsUndoEnabled());
1340 if( bUndo )
1342 rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
1343 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1346 if( xDlg->IsHorizontal() )
1348 xRange->split( 0, nCount );
1350 else
1352 xRange->split( nCount, 0 );
1355 if( bUndo )
1356 rModel.EndUndo();
1358 aEnd.mnRow += mxTable->getRowCount() - nRowCount;
1359 aEnd.mnCol += mxTable->getColumnCount() - nColCount;
1361 setSelectedCells( aStart, aEnd );
1363 xDlg->disposeOnce();
1367 void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
1369 if(!checkTableObject())
1370 return;
1372 SdrTableObj& rTableObj(*mxTableObj);
1373 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1374 const bool bUndo(rModel.IsUndoEnabled());
1376 if( bUndo )
1378 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
1379 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1382 CellPos aStart, aEnd;
1383 getSelectedCells( aStart, aEnd );
1384 rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
1386 if( bUndo )
1387 rModel.EndUndo();
1390 void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
1392 if(!checkTableObject())
1393 return;
1395 SdrTableObj& rTableObj(*mxTableObj);
1396 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1397 const bool bUndo(rModel.IsUndoEnabled());
1399 if( bUndo )
1401 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
1402 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1405 CellPos aStart, aEnd;
1406 getSelectedCells( aStart, aEnd );
1407 rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
1409 if( bUndo )
1410 rModel.EndUndo();
1413 bool SvxTableController::HasMarked() const
1415 return mbCellSelectionMode && mxTable.is();
1418 bool SvxTableController::DeleteMarked()
1420 if(!checkTableObject() || !HasMarked())
1421 return false;
1423 SdrTableObj& rTableObj(*mxTableObj);
1424 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1425 const bool bUndo(rModel.IsUndoEnabled());
1427 if (bUndo)
1428 rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
1430 CellPos aStart, aEnd;
1431 getSelectedCells( aStart, aEnd );
1432 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1434 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1436 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1437 if (xCell.is() && xCell->hasText())
1439 if (bUndo)
1440 xCell->AddUndo();
1441 xCell->SetOutlinerParaObject(nullptr);
1446 if (bUndo)
1447 rModel.EndUndo();
1449 UpdateTableShape();
1450 return true;
1453 bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
1455 if( hasSelectedCells() )
1457 rpStyleSheet = nullptr;
1459 if( mxTable.is() )
1461 SfxStyleSheet* pRet=nullptr;
1462 bool b1st=true;
1464 CellPos aStart, aEnd;
1465 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
1467 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1469 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1471 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1472 if( xCell.is() )
1474 SfxStyleSheet* pSS=xCell->GetStyleSheet();
1475 if(b1st)
1477 pRet=pSS;
1479 else if(pRet != pSS)
1481 return true;
1483 b1st=false;
1487 rpStyleSheet = pRet;
1488 return true;
1491 return false;
1494 bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
1496 if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
1498 if( mxTable.is() )
1500 CellPos aStart, aEnd;
1501 getSelectedCells( aStart, aEnd );
1503 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1505 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1507 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1508 if( xCell.is() )
1509 xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
1513 UpdateTableShape();
1514 return true;
1517 return false;
1520 void SvxTableController::changeTableEdge(const SfxRequest& rReq)
1522 if (!checkTableObject())
1523 return;
1525 const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
1526 const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
1527 const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
1529 if (!(pType && pIndex && pOffset))
1530 return;
1532 const OUString sType = pType->GetValue();
1533 const sal_uInt16 nIndex = pIndex->GetValue();
1534 const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
1536 SdrTableObj& rTableObj(*mxTableObj);
1538 sal_Int32 nEdgeIndex = -1;
1539 bool bHorizontal = sType.startsWith("row");
1541 if (sType == "column-left" || sType == "row-left")
1543 nEdgeIndex = 0;
1545 else if (sType == "column-right")
1547 // Number of edges = number of columns + 1
1548 nEdgeIndex = rTableObj.getColumnCount();
1550 else if (sType == "row-right")
1552 // Number of edges = number of rows + 1
1553 nEdgeIndex = rTableObj.getRowCount();
1555 else if (sType == "column-middle" || sType == "row-middle")
1557 nEdgeIndex = nIndex + 1;
1560 if (nEdgeIndex < 0)
1561 return;
1563 TableModelNotifyGuard aGuard(mxTable.get());
1564 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1565 const bool bUndo(rModel.IsUndoEnabled());
1566 if (bUndo)
1568 auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
1569 rModel.BegUndo(pUndoObject->GetComment());
1571 auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
1572 if (pGeoUndo)
1573 pGeoUndo->SetSkipChangeLayout(true);
1575 rModel.AddUndo(std::move(pUndoObject));
1577 tools::Rectangle aBoundRect;
1578 if (rTableObj.GetUserCall())
1579 aBoundRect = rTableObj.GetLastBoundRect();
1580 rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
1581 rTableObj.SetChanged();
1582 rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
1583 if (bUndo)
1584 rModel.EndUndo();
1587 // internals
1590 bool SvxTableController::checkTableObject()
1592 return mxTableObj.is();
1596 SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
1598 const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
1599 const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
1600 const bool bTextEdit = mrView.IsTextEdit();
1602 TblAction nAction = TblAction::HandledByView;
1604 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
1605 if( !pTableObj )
1606 return nAction;
1608 // handle special keys
1609 const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
1610 switch( nCode )
1612 case awt::Key::ESCAPE: // handle escape
1614 if( bTextEdit )
1616 // escape during text edit ends text edit
1617 nAction = TblAction::StopTextEdit;
1619 if( mbCellSelectionMode )
1621 // escape with selected cells removes selection
1622 nAction = TblAction::RemoveSelection;
1624 break;
1626 case awt::Key::RETURN: // handle return
1628 if( !bMod1 && !bMod2 && !bTextEdit )
1630 // when not already editing, return starts text edit
1631 setSelectionStart( SdrTableObj::getFirstCell() );
1632 nAction = TblAction::EditCell;
1634 break;
1636 case awt::Key::F2: // f2 toggles text edit
1638 if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
1641 else if( bTextEdit )
1643 // f2 during text edit stops text edit
1644 nAction = TblAction::StopTextEdit;
1646 else if( mbCellSelectionMode )
1648 // f2 with selected cells removes selection
1649 nAction = TblAction::RemoveSelection;
1651 else
1653 // f2 with no selection and no text edit starts text edit
1654 setSelectionStart( SdrTableObj::getFirstCell() );
1655 nAction = TblAction::EditCell;
1657 break;
1659 case awt::Key::HOME:
1660 case awt::Key::NUM7:
1662 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1664 if( bMod1 && !bMod2 )
1666 // ctrl + home jumps to first cell
1667 nAction = TblAction::GotoFirstCell;
1669 else if( !bMod1 && bMod2 )
1671 // alt + home jumps to first column
1672 nAction = TblAction::GotoFirstColumn;
1675 break;
1677 case awt::Key::END:
1678 case awt::Key::NUM1:
1680 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1682 if( bMod1 && !bMod2 )
1684 // ctrl + end jumps to last cell
1685 nAction = TblAction::GotoLastCell;
1687 else if( !bMod1 && bMod2 )
1689 // alt + home jumps to last column
1690 nAction = TblAction::GotoLastColumn;
1693 break;
1696 case awt::Key::TAB:
1698 if( bTextEdit || mbCellSelectionMode )
1699 nAction = TblAction::Tab;
1700 break;
1703 case awt::Key::UP:
1704 case awt::Key::NUM8:
1705 case awt::Key::DOWN:
1706 case awt::Key::NUM2:
1707 case awt::Key::LEFT:
1708 case awt::Key::NUM4:
1709 case awt::Key::RIGHT:
1710 case awt::Key::NUM6:
1713 if( !bMod1 && bMod2 )
1715 if(bTextEdit || mbCellSelectionMode)
1717 if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
1719 nAction = TblAction::GotoLeftCell;
1720 break;
1722 else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
1724 nAction = TblAction::GotoRightCell;
1725 break;
1730 bool bTextMove = false;
1731 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
1732 if( pOLV )
1734 RemoveSelection();
1735 // during text edit, check if we navigate out of the cell
1736 ESelection aOldSelection = pOLV->GetSelection();
1737 pOLV->PostKeyEvent(rKEvt);
1738 bTextMove = aOldSelection == pOLV->GetSelection();
1739 if( !bTextMove )
1741 nAction = TblAction::NONE;
1745 if( mbCellSelectionMode || bTextMove )
1747 // no text edit, navigate in cells if selection active
1748 switch( nCode )
1750 case awt::Key::LEFT:
1751 case awt::Key::NUM4:
1752 nAction = TblAction::GotoLeftCell;
1753 break;
1754 case awt::Key::RIGHT:
1755 case awt::Key::NUM6:
1756 nAction = TblAction::GotoRightCell;
1757 break;
1758 case awt::Key::DOWN:
1759 case awt::Key::NUM2:
1760 nAction = TblAction::GotoDownCell;
1761 break;
1762 case awt::Key::UP:
1763 case awt::Key::NUM8:
1764 nAction = TblAction::GotoUpCell;
1765 break;
1768 break;
1770 case awt::Key::PAGEUP:
1771 if( bMod2 )
1772 nAction = TblAction::GotoFirstRow;
1773 break;
1775 case awt::Key::PAGEDOWN:
1776 if( bMod2 )
1777 nAction = TblAction::GotoLastRow;
1778 break;
1780 return nAction;
1783 bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
1785 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
1786 if( !pTableObj )
1787 return false;
1789 switch( nAction )
1791 case TblAction::GotoFirstCell:
1793 gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
1794 break;
1797 case TblAction::GotoLeftCell:
1799 gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
1800 break;
1803 case TblAction::GotoRightCell:
1805 gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
1806 break;
1809 case TblAction::GotoLastCell:
1811 gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
1812 break;
1815 case TblAction::GotoFirstColumn:
1817 CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
1818 gotoCell( aPos, bSelect, pWindow, nAction );
1819 break;
1822 case TblAction::GotoLastColumn:
1824 CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
1825 gotoCell( aPos, bSelect, pWindow, nAction );
1826 break;
1829 case TblAction::GotoFirstRow:
1831 CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
1832 gotoCell( aPos, bSelect, pWindow, nAction );
1833 break;
1836 case TblAction::GotoUpCell:
1838 gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1839 break;
1842 case TblAction::GotoDownCell:
1844 gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1845 break;
1848 case TblAction::GotoLastRow:
1850 CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
1851 gotoCell( aPos, bSelect, pWindow, nAction );
1852 break;
1855 case TblAction::EditCell:
1856 EditCell( getSelectionStart(), pWindow, nAction );
1857 break;
1859 case TblAction::StopTextEdit:
1860 StopTextEdit();
1861 break;
1863 case TblAction::RemoveSelection:
1864 RemoveSelection();
1865 break;
1867 case TblAction::Tab:
1869 if( bSelect )
1870 gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
1871 else
1873 CellPos aSelectionEnd( getSelectionEnd() );
1874 CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
1875 if( aSelectionEnd == aNextCell )
1877 onInsert( SID_TABLE_INSERT_ROW );
1878 aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
1880 gotoCell( aNextCell, false, pWindow, nAction );
1882 break;
1884 default:
1885 break;
1888 return nAction != TblAction::HandledByView;
1892 void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
1894 if( mxTableObj.is() && mxTableObj->IsTextEditActive() )
1895 mrView.SdrEndTextEdit(true);
1897 if( bSelect )
1899 maCursorLastPos = rPos;
1900 if( mxTableObj.is() )
1901 mxTableObj->setActiveCell( rPos );
1903 if( !mbCellSelectionMode )
1905 setSelectedCells( maCursorFirstPos, rPos );
1907 else
1909 UpdateSelection( rPos );
1912 else
1914 RemoveSelection();
1915 EditCell( rPos, pWindow, nAction );
1920 const CellPos& SvxTableController::getSelectionStart()
1922 checkCell( maCursorFirstPos );
1923 return maCursorFirstPos;
1927 void SvxTableController::setSelectionStart( const CellPos& rPos )
1929 maCursorFirstPos = rPos;
1933 const CellPos& SvxTableController::getSelectionEnd()
1935 checkCell( maCursorLastPos );
1936 return maCursorLastPos;
1940 void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
1942 if(!checkTableObject() || !mxTable.is())
1943 return;
1947 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
1949 if( xRange->isMergeable() )
1951 SdrTableObj& rTableObj(*mxTableObj);
1952 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1953 const bool bUndo(rModel.IsUndoEnabled());
1955 if( bUndo )
1957 rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
1958 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1961 xRange->merge();
1962 mbHasJustMerged = true;
1963 setSelectedCells( maCursorFirstPos, maCursorFirstPos );
1965 if( bUndo )
1966 rModel.EndUndo();
1969 catch( Exception& )
1971 TOOLS_WARN_EXCEPTION( "svx.table", "" );
1976 void SvxTableController::checkCell( CellPos& rPos )
1978 if( !mxTable.is() )
1979 return;
1983 if( rPos.mnCol >= mxTable->getColumnCount() )
1984 rPos.mnCol = mxTable->getColumnCount()-1;
1986 if( rPos.mnRow >= mxTable->getRowCount() )
1987 rPos.mnRow = mxTable->getRowCount()-1;
1989 catch( Exception& )
1991 TOOLS_WARN_EXCEPTION("svx.table", "");
1996 void SvxTableController::findMergeOrigin( CellPos& rPos )
1998 if( !mxTable.is() )
1999 return;
2003 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
2004 if( xCell->isMerged() )
2006 ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
2009 catch( Exception& )
2011 TOOLS_WARN_EXCEPTION("svx.table", "");
2016 void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
2018 SdrPageView* pPV(mrView.GetSdrPageView());
2020 if(nullptr == pPV || !checkTableObject())
2021 return;
2023 SdrTableObj& rTableObj(*mxTableObj);
2025 if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
2026 return;
2028 bool bEmptyOutliner = false;
2030 if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
2032 ::Outliner* pOutl = mrView.GetTextEditOutliner();
2033 sal_Int32 nParaCnt = pOutl->GetParagraphCount();
2034 Paragraph* p1stPara = pOutl->GetParagraph( 0 );
2036 if(nParaCnt==1 && p1stPara)
2038 // with only one paragraph
2039 if (pOutl->GetText(p1stPara).isEmpty())
2041 bEmptyOutliner = true;
2046 CellPos aPos( rPos );
2047 findMergeOrigin( aPos );
2049 if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
2050 return;
2052 if( rTableObj.IsTextEditActive() )
2053 mrView.SdrEndTextEdit(true);
2055 rTableObj.setActiveCell( aPos );
2057 // create new outliner, owner will be the SdrObjEditView
2058 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2059 std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
2061 if (pOutl && rTableObj.IsVerticalWriting())
2062 pOutl->SetVertical( true );
2064 if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
2065 return;
2067 maCursorLastPos = maCursorFirstPos = rPos;
2069 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
2071 // Move cursor to end of text
2072 ESelection aNewSelection;
2074 const WritingMode eMode = rTableObj.GetWritingMode();
2075 if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
2077 const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
2078 ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
2080 if( bLast )
2081 aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
2083 pOLV->SetSelection(aNewSelection);
2087 void SvxTableController::StopTextEdit()
2089 if(mrView.IsTextEdit())
2091 mrView.SdrEndTextEdit();
2092 mrView.SetCurrentObj(OBJ_TABLE);
2093 mrView.SetEditMode(SdrViewEditMode::Edit);
2098 void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
2100 if( mbCellSelectionMode )
2102 checkCell( maCursorFirstPos );
2103 checkCell( maCursorLastPos );
2105 rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2106 rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2107 rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2108 rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2110 bool bExt = false;
2111 if( mxTable.is() ) do
2113 bExt = false;
2114 for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
2116 for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
2118 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
2119 if( !xCell.is() )
2120 continue;
2122 if( xCell->isMerged() )
2124 CellPos aPos( nCol, nRow );
2125 findMergeOrigin( aPos );
2126 if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
2128 rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
2129 rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
2130 bExt = true;
2133 else
2135 if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
2137 rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
2138 rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
2139 bExt = true;
2145 while(bExt);
2147 else if(mrView.IsTextEdit())
2149 rFirst = getSelectionStart();
2150 findMergeOrigin( rFirst );
2151 rLast = rFirst;
2153 if( mxTable.is() )
2155 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
2156 if( xCell.is() )
2158 rLast.mnCol += xCell->getColumnSpan() - 1;
2159 rLast.mnRow += xCell->getRowSpan() - 1;
2163 else
2165 rFirst.mnCol = 0;
2166 rFirst.mnRow = 0;
2167 if( mxTable.is() )
2169 rLast.mnRow = mxTable->getRowCount()-1;
2170 rLast.mnCol = mxTable->getColumnCount()-1;
2172 else
2174 rLast.mnRow = 0;
2175 rLast.mnCol = 0;
2181 void SvxTableController::StartSelection( const CellPos& rPos )
2183 StopTextEdit();
2184 mbCellSelectionMode = true;
2185 maCursorLastPos = maCursorFirstPos = rPos;
2186 mrView.MarkListHasChanged();
2190 void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
2192 StopTextEdit();
2193 mbCellSelectionMode = true;
2194 maCursorFirstPos = rStart;
2195 UpdateSelection( rEnd );
2199 bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
2201 if(!checkTableObject() || !mxTable.is())
2202 return false;
2204 SdrTableObj& rTableObj(*mxTableObj);
2205 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2207 if (mrView.IsTextEdit())
2208 return true;
2210 CellPos aStart, aEnd;
2212 if(hasSelectedCells())
2214 getSelectedCells(aStart, aEnd);
2216 else
2218 aStart.mnRow = 0;
2219 aStart.mnCol = 0;
2220 aEnd.mnRow = mxTable->getRowCount() - 1;
2221 aEnd.mnCol = mxTable->getColumnCount() - 1;
2224 for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
2226 for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
2228 CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
2229 if (xCell.is())
2231 if (rModel.IsUndoEnabled())
2232 xCell->AddUndo();
2234 SfxItemSet aCellSet(xCell->GetItemSet());
2235 if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
2237 xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
2243 UpdateTableShape();
2245 return true;
2249 void SvxTableController::UpdateSelection( const CellPos& rPos )
2251 maCursorLastPos = rPos;
2252 mrView.MarkListHasChanged();
2256 void SvxTableController::clearSelection()
2258 RemoveSelection();
2262 void SvxTableController::selectAll()
2264 if( mxTable.is() )
2266 CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
2267 if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
2269 CellPos aPos1;
2270 setSelectedCells( aPos1, aPos2 );
2276 void SvxTableController::RemoveSelection()
2278 if( mbCellSelectionMode )
2280 mbCellSelectionMode = false;
2281 mrView.MarkListHasChanged();
2286 void SvxTableController::onTableModified()
2288 if( mnUpdateEvent == nullptr )
2289 mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
2293 void SvxTableController::updateSelectionOverlay()
2295 // There is no need to update selection overlay after merging cells
2296 // since the selection overlay should remain the same
2297 if ( mbHasJustMerged )
2298 return;
2300 destroySelectionOverlay();
2301 if( !mbCellSelectionMode )
2302 return;
2304 sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
2305 if( !pTableObj )
2306 return;
2308 sdr::overlay::OverlayObjectCell::RangeVector aRanges;
2310 tools::Rectangle aStartRect, aEndRect;
2311 CellPos aStart,aEnd;
2312 getSelectedCells( aStart, aEnd );
2313 pTableObj->getCellBounds( aStart, aStartRect );
2315 basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
2316 a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
2318 findMergeOrigin( aEnd );
2319 pTableObj->getCellBounds( aEnd, aEndRect );
2320 a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
2321 a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
2322 aRanges.push_back( a2DRange );
2324 ::Color aHighlight( COL_BLUE );
2325 OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
2326 if( pOutDev )
2327 aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
2329 const sal_uInt32 nCount = mrView.PaintWindowCount();
2330 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
2332 SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
2333 if( pPaintWindow )
2335 const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
2336 if( xOverlayManager.is() )
2338 std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, aRanges ));
2340 xOverlayManager->add(*pOverlay);
2341 mpSelectionOverlay.reset(new sdr::overlay::OverlayObjectList);
2342 mpSelectionOverlay->append(std::move(pOverlay));
2347 // If tiled rendering, emit callbacks for sdr table selection.
2348 if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
2349 return;
2351 tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
2353 if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2354 aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
2356 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2358 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString().getStr());
2359 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString().getStr());
2364 void SvxTableController::destroySelectionOverlay()
2366 if( !mpSelectionOverlay )
2367 return;
2369 mpSelectionOverlay.reset();
2371 if (comphelper::LibreOfficeKit::isActive())
2373 // Clear the LOK text selection so far provided by this table.
2374 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2376 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY");
2377 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
2378 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
2379 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
2385 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
2387 if( !mxTable.is() )
2388 return;
2390 CellPos aStart, aEnd;
2391 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
2393 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2395 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2397 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2398 if( xCell.is() && !xCell->isMerged() )
2400 const SfxItemSet& rSet = xCell->GetItemSet();
2401 SfxWhichIter aIter(rSet);
2402 sal_uInt16 nWhich(aIter.FirstWhich());
2403 while(nWhich)
2405 if(!bOnlyHardAttr)
2407 if(SfxItemState::DONTCARE == rSet.GetItemState(nWhich, false))
2408 rAttr.InvalidateItem(nWhich);
2409 else
2410 rAttr.MergeValue(rSet.Get(nWhich), true);
2412 else if(SfxItemState::SET == rSet.GetItemState(nWhich, false))
2414 const SfxPoolItem& rItem = rSet.Get(nWhich);
2415 rAttr.MergeValue(rItem, true);
2418 nWhich = aIter.NextWhich();
2426 static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
2428 if( pNew )
2430 const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
2431 if( pOld )
2433 SvxBorderLine aNewLine( *pNew );
2434 aNewLine.SetColor( pOld->GetColor() );
2435 rNewFrame.SetLine( &aNewLine, nLine );
2436 return;
2439 rNewFrame.SetLine( pNew, nLine );
2443 static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
2445 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2447 // current cell is outside the selection
2449 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2451 if (nCellPosFlags & CellPosFlag::Upper)
2453 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
2454 rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
2456 else if (nCellPosFlags & CellPosFlag::Lower)
2458 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
2459 rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
2462 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2464 if (nCellPosFlags & CellPosFlag::Before)
2466 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
2467 rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
2469 else if (nCellPosFlags & CellPosFlag::After)
2471 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
2472 rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
2476 else
2478 // current cell is inside the selection
2480 if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
2481 : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
2482 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
2484 if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2485 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
2487 if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2488 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
2490 if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2491 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
2493 // apply distance to borders
2494 if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
2495 for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
2496 rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
2501 static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
2503 const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
2504 if( pSourceLine )
2506 SvxBorderLine aLine( *pSourceLine );
2507 aLine.SetColor( rColor );
2508 rNewFrame.SetLine( &aLine, nLine );
2513 static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
2515 const Color aColor( pLineColorItem->GetValue() );
2517 if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
2518 ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
2520 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
2521 ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
2523 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
2524 ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
2526 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
2527 ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
2531 static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
2533 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2535 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2537 if (nCellPosFlags & CellPosFlag::Upper)
2539 if( rNewFrame.GetBottom() )
2540 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2542 else if (nCellPosFlags & CellPosFlag::Lower)
2544 if( rNewFrame.GetTop() )
2545 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2548 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2550 if (nCellPosFlags & CellPosFlag::Before)
2552 if( rNewFrame.GetRight() )
2553 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2555 else if (nCellPosFlags & CellPosFlag::After)
2557 if( rNewFrame.GetLeft() )
2558 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2562 else
2564 if( rNewFrame.GetBottom() )
2565 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2566 if( rNewFrame.GetTop() )
2567 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2568 if( rNewFrame.GetRight() )
2569 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2570 if( rNewFrame.GetLeft() )
2571 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2576 void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
2578 if( !mxTable.is() )
2579 return;
2581 const sal_Int32 nRowCount = mxTable->getRowCount();
2582 const sal_Int32 nColCount = mxTable->getColumnCount();
2583 if( !(nRowCount && nColCount) )
2584 return;
2586 const SvxBoxItem* pBoxItem = nullptr;
2587 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
2588 pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
2590 const SvxBoxInfoItem* pBoxInfoItem = nullptr;
2591 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
2592 pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
2594 const SvxColorItem* pLineColorItem = nullptr;
2595 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
2596 pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
2598 const SvxBorderLine* pBorderLineItem = nullptr;
2599 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
2600 pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
2602 if( pBoxInfoItem && !pBoxItem )
2604 const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
2605 pBoxItem = &gaEmptyBoxItem;
2607 else if( pBoxItem && !pBoxInfoItem )
2609 const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
2610 pBoxInfoItem = &gaEmptyBoxInfoItem;
2613 CellPos aStart, aEnd;
2614 getSelectedCells( aStart, aEnd );
2616 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
2617 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
2619 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
2621 CellPosFlag nRowFlags = CellPosFlag::NONE;
2622 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
2623 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
2624 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
2625 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
2627 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
2629 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2630 if( !xCell.is() )
2631 continue;
2633 const SfxItemSet& rSet = xCell->GetItemSet();
2634 const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
2636 SvxBoxItem aNewFrame( *pOldOuter );
2638 CellPosFlag nCellPosFlags = nRowFlags;
2639 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
2640 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
2641 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
2642 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
2644 if( pBoxItem && pBoxInfoItem )
2645 ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
2647 if( pLineColorItem )
2648 ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
2650 if( pBorderLineItem )
2651 ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
2653 if (aNewFrame != *pOldOuter)
2655 SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
2656 aAttr.Put(aNewFrame);
2657 xCell->SetMergedItemSetAndBroadcast( aAttr, false );
2664 void SvxTableController::UpdateTableShape()
2666 SdrObject* pTableObj = mxTableObj.get();
2667 if( pTableObj )
2669 pTableObj->ActionChanged();
2670 pTableObj->BroadcastObjectChange();
2672 updateSelectionOverlay();
2676 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
2678 if(!checkTableObject() || !mxTable.is())
2679 return;
2681 SdrTableObj& rTableObj(*mxTableObj);
2682 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2683 const bool bUndo(rModel.IsUndoEnabled());
2685 if( bUndo )
2686 rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
2688 CellPos aStart, aEnd;
2689 getSelectedCells( aStart, aEnd );
2691 SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
2692 aAttr.Put(rAttr);
2694 const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2696 if( bFrame )
2698 aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2699 aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2702 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2704 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2706 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2707 if( xCell.is() )
2709 if( bUndo )
2710 xCell->AddUndo();
2711 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2716 if( bFrame )
2718 ApplyBorderAttr( rAttr );
2721 UpdateTableShape();
2723 if( bUndo )
2724 rModel.EndUndo();
2727 void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
2729 if (!checkTableObject() || !mxTable.is())
2730 return;
2732 // Filter out non-shadow items from rAttr.
2733 SfxItemSet aSet(*rAttr.GetPool(), svl::Items<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST>{});
2734 aSet.Put(rAttr);
2736 // Set shadow items on the marked shape.
2737 mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
2740 bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2742 if( mxTableObj.is() && hasSelectedCells() )
2744 MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
2746 if( mrView.IsTextEdit() )
2748 OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
2749 if(pTextEditOutlinerView)
2751 // FALSE= consider InvalidItems not as the default, but as "holes"
2752 rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
2756 return true;
2758 else
2760 return false;
2765 bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2767 if( mbCellSelectionMode || mrView.IsTextEdit() )
2769 SetAttrToSelectedCells( rSet, bReplaceAll );
2770 return true;
2772 return false;
2775 SdrObject* SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
2777 SdrTableObj* pRetval(nullptr);
2778 sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
2780 if(nullptr == pCurrentSdrTableObj)
2782 return pRetval;
2785 if(!mxTableObj.is())
2787 return pRetval;
2790 // get selection and create full selection
2791 CellPos aStart, aEnd;
2792 const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
2794 getSelectedCells(aStart, aEnd);
2796 // compare to see if we have a partial selection
2797 if(aStart != aFullStart || aEnd != aFullEnd)
2799 // create full clone
2800 pRetval = pCurrentSdrTableObj->CloneSdrObject(rTargetModel);
2802 // limit SdrObject's TableModel to partial selection
2803 pRetval->CropTableModelToSelection(aStart, aEnd);
2806 return pRetval;
2809 bool SvxTableController::PasteObjModel( const SdrModel& rModel )
2811 if( mxTableObj.is() && (rModel.GetPageCount() >= 1) )
2813 const SdrPage* pPastePage = rModel.GetPage(0);
2814 if( pPastePage && pPastePage->GetObjCount() == 1 )
2816 SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
2817 if( pPasteTableObj )
2819 return PasteObject( pPasteTableObj );
2824 return false;
2828 bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
2830 if( !pPasteTableObj )
2831 return false;
2833 Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
2834 if( !xPasteTable.is() )
2835 return false;
2837 if( !mxTable.is() )
2838 return false;
2840 sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
2841 sal_Int32 nPasteRows = xPasteTable->getRowCount();
2843 CellPos aStart, aEnd;
2844 getSelectedCells( aStart, aEnd );
2846 if( mrView.IsTextEdit() )
2847 mrView.SdrEndTextEdit(true);
2849 sal_Int32 nColumns = mxTable->getColumnCount();
2850 sal_Int32 nRows = mxTable->getRowCount();
2852 const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
2853 if( nMissing > 0 )
2855 Reference< XTableRows > xRows( mxTable->getRows() );
2856 xRows->insertByIndex( nRows, nMissing );
2857 nRows = mxTable->getRowCount();
2860 nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
2861 nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
2863 // copy cell contents
2864 for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
2866 for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
2868 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
2869 if( xTargetCell.is() && !xTargetCell->isMerged() )
2871 CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
2872 if (xSourceCell.is())
2874 xTargetCell->AddUndo();
2875 // Prevent cell span exceeding the pasting range.
2876 if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
2877 xTargetCell->replaceContentAndFormatting(xSourceCell);
2878 else
2879 xTargetCell->cloneFrom(xSourceCell);
2881 nCol += xSourceCell->getColumnSpan() - 1;
2882 nTargetCol += xTargetCell->getColumnSpan();
2888 UpdateTableShape();
2890 return true;
2893 bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
2895 if(!mbCellSelectionMode)
2897 return false;
2900 if(!checkTableObject())
2901 return false;
2903 SdrTableObj& rTableObj(*mxTableObj);
2904 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2905 const bool bUndo(rModel.IsUndoEnabled());
2907 if( bUndo )
2908 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
2910 CellPos aStart, aEnd;
2911 getSelectedCells( aStart, aEnd );
2912 const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2914 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2916 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2918 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2919 if( xCell.is() )
2921 if (bUndo)
2922 xCell->AddUndo();
2923 SdrText* pText = xCell.get();
2924 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
2929 if( bFrame )
2931 ApplyBorderAttr( rFormatSet );
2934 UpdateTableShape();
2936 if( bUndo )
2937 rModel.EndUndo();
2939 return true;
2943 IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
2945 mnUpdateEvent = nullptr;
2947 if( mbCellSelectionMode )
2949 CellPos aStart( maCursorFirstPos );
2950 CellPos aEnd( maCursorLastPos );
2951 checkCell(aStart);
2952 checkCell(aEnd);
2953 if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
2955 setSelectedCells( aStart, aEnd );
2959 updateSelectionOverlay();
2960 mbHasJustMerged = false;
2963 namespace
2966 struct LinesState
2968 LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
2969 : rBoxItem(rBoxItem_)
2970 , rBoxInfoItem(rBoxInfoItem_)
2971 , bDistanceIndeterminate(false)
2973 aBorderSet.fill(false);
2974 aInnerLineSet.fill(false);
2975 aBorderIndeterminate.fill(false);
2976 aInnerLineIndeterminate.fill(false);
2977 aDistanceSet.fill(false);
2978 aDistance.fill(0);
2981 SvxBoxItem& rBoxItem;
2982 SvxBoxInfoItem& rBoxInfoItem;
2983 o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
2984 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
2985 o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
2986 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
2987 o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
2988 o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
2989 bool bDistanceIndeterminate;
2992 class BoxItemWrapper
2994 public:
2995 BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
2997 const SvxBorderLine* getLine() const;
2998 void setLine(const SvxBorderLine* pLine);
3000 private:
3001 SvxBoxItem& m_rBoxItem;
3002 SvxBoxInfoItem& m_rBoxInfoItem;
3003 const SvxBoxItemLine m_nBorderLine;
3004 const SvxBoxInfoItemLine m_nInnerLine;
3005 const bool m_bBorder;
3008 BoxItemWrapper::BoxItemWrapper(
3009 SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
3010 const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
3011 : m_rBoxItem(rBoxItem)
3012 , m_rBoxInfoItem(rBoxInfoItem)
3013 , m_nBorderLine(nBorderLine)
3014 , m_nInnerLine(nInnerLine)
3015 , m_bBorder(bBorder)
3019 const SvxBorderLine* BoxItemWrapper::getLine() const
3021 if (m_bBorder)
3022 return m_rBoxItem.GetLine(m_nBorderLine);
3023 else
3024 return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
3027 void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
3029 if (m_bBorder)
3030 m_rBoxItem.SetLine(pLine, m_nBorderLine);
3031 else
3032 m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
3035 void lcl_MergeBorderLine(
3036 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3037 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
3039 const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
3040 BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
3041 bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
3043 if (rbSet)
3045 bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
3046 if (!rbIndeterminate)
3048 const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
3049 if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
3051 aBoxItem.setLine(nullptr);
3052 rbIndeterminate = true;
3056 else
3058 aBoxItem.setLine(pLine);
3059 rbSet = true;
3063 void lcl_MergeBorderOrInnerLine(
3064 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3065 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
3067 if (bBorder)
3068 lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
3069 else
3071 const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
3072 lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
3076 void lcl_MergeDistance(
3077 LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
3079 if (rLinesState.aDistanceSet[nIndex])
3081 if (!rLinesState.bDistanceIndeterminate)
3082 rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
3084 else
3086 rLinesState.aDistance[nIndex] = nDistance;
3087 rLinesState.aDistanceSet[nIndex] = true;
3091 void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
3093 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
3095 // current cell is outside the selection
3097 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
3099 if (nCellPosFlags & CellPosFlag::Upper)
3100 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
3101 else if (nCellPosFlags & CellPosFlag::Lower)
3102 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
3104 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
3106 if (nCellPosFlags & CellPosFlag::Before)
3107 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
3108 else if (nCellPosFlags & CellPosFlag::After)
3109 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
3112 // NOTE: inner distances for cells outside the selected range
3113 // are not relevant -> we ignore them.
3115 else
3117 // current cell is inside the selection
3119 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
3120 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
3121 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
3122 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
3124 lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
3125 lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
3126 lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
3127 lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
3133 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
3135 if( !mxTable.is() )
3136 return;
3138 const sal_Int32 nRowCount = mxTable->getRowCount();
3139 const sal_Int32 nColCount = mxTable->getColumnCount();
3140 if( !(nRowCount && nColCount) )
3141 return;
3143 CellPos aStart, aEnd;
3144 const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
3146 // We are adding one more row/column around the block of selected cells.
3147 // We will be checking the adjoining border of these too.
3148 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
3149 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
3151 rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
3152 LinesState aLinesState( rBoxItem, rBoxInfoItem );
3154 /* Here we go through all the selected cells (enhanced by
3155 * the adjoining row/column on each side) and determine the
3156 * lines for presentation. The algorithm is simple:
3157 * 1. if a border or inner line is set (or unset) in all
3158 * cells to the same value, it will be used.
3159 * 2. if a border or inner line is set only in some cells,
3160 * it will be set to indeterminate state (SetValid() on
3161 * rBoxInfoItem).
3163 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
3165 CellPosFlag nRowFlags = CellPosFlag::NONE;
3166 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
3167 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
3168 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
3169 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
3171 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
3173 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
3174 if( !xCell.is() )
3175 continue;
3177 CellPosFlag nCellPosFlags = nRowFlags;
3178 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
3179 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
3180 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
3181 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
3183 const SfxItemSet& rSet = xCell->GetItemSet();
3184 SvxBoxItem aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
3185 lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
3189 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
3190 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
3191 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
3192 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
3193 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
3194 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
3195 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
3196 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
3197 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
3198 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
3199 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
3200 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
3202 if (!aLinesState.bDistanceIndeterminate)
3204 if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
3205 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
3206 if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
3207 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
3208 if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
3209 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
3210 if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
3211 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
3212 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
3216 bool SvxTableController::selectRow( sal_Int32 row )
3218 if( !mxTable.is() )
3219 return false;
3220 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3221 StartSelection( aEnd );
3222 gotoCell( aStart, true, nullptr );
3223 return true;
3226 bool SvxTableController::selectColumn( sal_Int32 column )
3228 if( !mxTable.is() )
3229 return false;
3230 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3231 StartSelection( aEnd );
3232 gotoCell( aStart, true, nullptr );
3233 return true;
3236 bool SvxTableController::deselectRow( sal_Int32 row )
3238 if( !mxTable.is() )
3239 return false;
3240 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3241 StartSelection( aEnd );
3242 gotoCell( aStart, false, nullptr );
3243 return true;
3246 bool SvxTableController::deselectColumn( sal_Int32 column )
3248 if( !mxTable.is() )
3249 return false;
3250 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3251 StartSelection( aEnd );
3252 gotoCell( aStart, false, nullptr );
3253 return true;
3256 bool SvxTableController::isRowSelected( sal_Int32 nRow )
3258 if( hasSelectedCells() )
3260 CellPos aFirstPos, aLastPos;
3261 getSelectedCells( aFirstPos, aLastPos );
3262 if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
3263 return true;
3265 return false;
3268 bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
3270 if( hasSelectedCells() )
3272 CellPos aFirstPos, aLastPos;
3273 getSelectedCells( aFirstPos, aLastPos );
3274 if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
3275 return true;
3277 return false;
3280 bool SvxTableController::isRowHeader()
3282 if(!checkTableObject())
3283 return false;
3285 SdrTableObj& rTableObj(*mxTableObj);
3286 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3288 return aSettings.mbUseFirstRow;
3291 bool SvxTableController::isColumnHeader()
3293 if(!checkTableObject())
3294 return false;
3296 SdrTableObj& rTableObj(*mxTableObj);
3297 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3299 return aSettings.mbUseFirstColumn;
3302 bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
3304 if (mxTableObj->GetObjIdentifier() != OBJ_TABLE)
3305 return false;
3307 SdrTableObj* pTableObj = mxTableObj.get();
3308 CellPos aCellPos;
3309 if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
3311 // Position is a table cell.
3312 if (mbCellSelectionMode)
3314 // We have a table selection already: adjust the point or the mark.
3315 if (bPoint)
3316 setSelectedCells(maCursorFirstPos, aCellPos);
3317 else
3318 setSelectedCells(aCellPos, maCursorLastPos);
3319 return true;
3321 else if (aCellPos != maMouseDownPos)
3323 // No selection, but rPosition is at another cell: start table selection.
3324 StartSelection(maMouseDownPos);
3325 // Update graphic selection, should be hidden now.
3326 mrView.AdjustMarkHdl();
3330 return false;
3335 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */