tdf#144694 In direct SQL dialog, activate options 'Run SQL command
[LibreOffice.git] / svx / source / table / tablecontroller.cxx
blobee32304f2d93b5f149df8ed124f5ecefe68acb3f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
22 #include <svx/sdr/table/tablecontroller.hxx>
23 #include <tablemodel.hxx>
25 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
26 #include <com/sun/star/container/XIndexAccess.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/table/XMergeableCellRange.hpp>
30 #include <com/sun/star/table/XMergeableCell.hpp>
32 #include <sal/config.h>
33 #include <sal/log.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/window.hxx>
39 #include <svl/whiter.hxx>
40 #include <svl/stritem.hxx>
42 #include <sfx2/request.hxx>
44 #include <svx/svdotable.hxx>
45 #include <sdr/overlay/overlayobjectcell.hxx>
46 #include <svx/sdr/overlay/overlaymanager.hxx>
47 #include <svx/svxids.hrc>
48 #include <svx/svdoutl.hxx>
49 #include <svx/svdpagv.hxx>
50 #include <svx/svdetc.hxx>
51 #include <svx/selectioncontroller.hxx>
52 #include <svx/svdmodel.hxx>
53 #include <svx/sdrpaintwindow.hxx>
54 #include <svx/svxdlg.hxx>
55 #include <editeng/boxitem.hxx>
56 #include <cell.hxx>
57 #include <editeng/borderline.hxx>
58 #include <editeng/colritem.hxx>
59 #include <editeng/lineitem.hxx>
60 #include <svx/strings.hrc>
61 #include <svx/dialmgr.hxx>
62 #include <svx/svdpage.hxx>
63 #include <svx/sdmetitm.hxx>
64 #include <svx/sdtditm.hxx>
65 #include "tableundo.hxx"
66 #include "tablelayouter.hxx"
67 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
68 #include <memory>
69 #include <o3tl/enumarray.hxx>
70 #include <o3tl/enumrange.hxx>
71 #include <cppuhelper/implbase.hxx>
72 #include <comphelper/lok.hxx>
73 #include <sfx2/viewsh.hxx>
74 #include <editeng/editview.hxx>
75 #include <tools/UnitConversion.hxx>
76 #include <comphelper/diagnose_ex.hxx>
78 using ::editeng::SvxBorderLine;
79 using namespace sdr::table;
80 using namespace ::com::sun::star;
81 using namespace ::com::sun::star::uno;
82 using namespace ::com::sun::star::table;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::container;
85 using namespace ::com::sun::star::text;
86 using namespace ::com::sun::star::style;
88 namespace {
90 enum class CellPosFlag // signals the relative position of a cell to a selection
92 NONE = 0x0000, // not set or inside
93 // row
94 Before = 0x0001,
95 Left = 0x0002,
96 Right = 0x0004,
97 After = 0x0008,
98 // column
99 Upper = 0x0010,
100 Top = 0x0020,
101 Bottom = 0x0040,
102 Lower = 0x0080
107 namespace o3tl
108 { template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
110 namespace sdr::table {
112 class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
114 public:
115 explicit SvxTableControllerModifyListener( SvxTableController* pController )
116 : mpController( pController ) {}
118 // XModifyListener
119 virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
121 // XEventListener
122 virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
124 SvxTableController* mpController;
127 // XModifyListener
130 void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
132 if( mpController )
133 mpController->onTableModified();
137 // XEventListener
140 void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
142 mpController = nullptr;
148 rtl::Reference< sdr::SelectionController > CreateTableController(
149 SdrView& rView,
150 const SdrTableObj& rObj,
151 const rtl::Reference< sdr::SelectionController >& xRefController )
153 return SvxTableController::create(rView, rObj, xRefController);
157 rtl::Reference< sdr::SelectionController > SvxTableController::create(
158 SdrView& rView,
159 const SdrTableObj& rObj,
160 const rtl::Reference< sdr::SelectionController >& xRefController )
162 if( xRefController.is() )
164 SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
166 if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
168 return xRefController;
172 return new SvxTableController(rView, rObj);
176 SvxTableController::SvxTableController(
177 SdrView& rView,
178 const SdrTableObj& rObj)
179 : mbCellSelectionMode(false)
180 ,mbHasJustMerged(false)
181 ,mbLeftButtonDown(false)
182 ,mrView(rView)
183 ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
184 ,mnUpdateEvent( nullptr )
186 rObj.getActiveCellPos( maCursorFirstPos );
187 maCursorLastPos = maCursorFirstPos;
189 mxTable = mxTableObj.get()->getUnoTable();
190 if( mxTable )
192 mxModifyListener = new SvxTableControllerModifyListener( this );
193 mxTable->addModifyListener( mxModifyListener );
197 SvxTableController::~SvxTableController()
199 if( mnUpdateEvent )
201 Application::RemoveUserEvent( mnUpdateEvent );
204 if( mxModifyListener.is() && mxTableObj.get() )
206 rtl::Reference< TableModel > xTable( mxTableObj.get()->getUnoTable() );
207 if( xTable.is() )
209 xTable->removeModifyListener( mxModifyListener );
210 mxModifyListener.clear();
215 bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
217 if(!checkTableObject())
218 return false;
220 SdrTableObj& rTableObj(*mxTableObj.get());
221 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
223 // check if we are read only
224 if( rModel.IsReadOnly())
226 switch( rKEvt.GetKeyCode().GetCode() )
228 case awt::Key::DOWN:
229 case awt::Key::UP:
230 case awt::Key::LEFT:
231 case awt::Key::RIGHT:
232 case awt::Key::TAB:
233 case awt::Key::HOME:
234 case awt::Key::END:
235 case awt::Key::NUM2:
236 case awt::Key::NUM4:
237 case awt::Key::NUM6:
238 case awt::Key::NUM8:
239 case awt::Key::ESCAPE:
240 case awt::Key::F2:
241 break;
242 default:
243 // tell the view we eat the event, no further processing needed
244 return true;
248 TblAction nAction = getKeyboardAction(rKEvt);
250 return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
253 namespace {
255 Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
257 if (!pWindow)
258 return rPoint;
260 return pWindow->PixelToLogic(rPoint);
265 bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
267 if (comphelper::LibreOfficeKit::isActive() && !pWindow)
269 // Tiled rendering: get the window that has the disabled map mode.
270 if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
272 if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
273 pWindow = pOutputDevice->GetOwnerWindow();
277 if( !pWindow || !checkTableObject() )
278 return false;
280 SdrViewEvent aVEvt;
281 if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
282 return false;
284 TableHitKind eHit = mxTableObj.get()->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
286 mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
288 if( eHit == TableHitKind::Cell )
290 StartSelection( maMouseDownPos );
291 return true;
294 if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
295 return true; // right click will become context menu
297 // for cell selection with the mouse remember our first hit
298 if( mbLeftButtonDown )
300 RemoveSelection();
302 SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
304 if( pHdl )
306 mbLeftButtonDown = false;
308 else
310 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
312 if (!pTableObj || eHit == TableHitKind::NONE)
314 mbLeftButtonDown = false;
319 if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
321 bool bEmptyOutliner = false;
322 if (Outliner* pOutliner = mrView.GetTextEditOutliner())
324 if (pOutliner->GetParagraphCount() == 1)
326 if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
327 bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
330 if (bEmptyOutliner)
332 // Tiled rendering: a left double-click in an empty cell: select it.
333 StartSelection(maMouseDownPos);
334 setSelectedCells(maMouseDownPos, maMouseDownPos);
335 // Update graphic selection, should be hidden now.
336 mrView.AdjustMarkHdl();
337 return true;
341 return false;
345 bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
347 if( !checkTableObject() )
348 return false;
350 mbLeftButtonDown = false;
352 return rMEvt.GetClicks() == 2;
356 bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
358 if( !checkTableObject() )
359 return false;
361 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
362 CellPos aPos;
363 if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
365 if(aPos != maMouseDownPos)
367 if( mbCellSelectionMode )
369 setSelectedCells( maMouseDownPos, aPos );
370 return true;
372 else
374 StartSelection( maMouseDownPos );
377 else if( mbCellSelectionMode )
379 UpdateSelection( aPos );
380 return true;
383 return false;
387 void SvxTableController::onSelectionHasChanged()
389 bool bSelected = false;
391 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
392 if( pTableObj && pTableObj->IsTextEditActive() )
394 pTableObj->getActiveCellPos( maCursorFirstPos );
395 maCursorLastPos = maCursorFirstPos;
396 mbCellSelectionMode = false;
398 else
400 const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
401 if( rMarkList.GetMarkCount() == 1 )
402 bSelected = mxTableObj.get().get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
405 if( bSelected )
407 updateSelectionOverlay();
409 else
411 destroySelectionOverlay();
414 void SvxTableController::onSelectAll()
416 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
417 if ( pTableObj && !pTableObj->IsTextEditActive())
419 selectAll();
424 void SvxTableController::GetState( SfxItemSet& rSet )
426 if(!mxTable.is() || !mxTableObj.get().is())
427 return;
429 SdrTableObj& rTableObj(*mxTableObj.get());
430 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
431 std::optional<SfxItemSet> oSet;
432 bool bVertDone(false);
434 // Iterate over all requested items in the set.
435 SfxWhichIter aIter( rSet );
436 sal_uInt16 nWhich = aIter.FirstWhich();
437 while (nWhich)
439 switch (nWhich)
441 case SID_TABLE_VERT_BOTTOM:
442 case SID_TABLE_VERT_CENTER:
443 case SID_TABLE_VERT_NONE:
445 if(!bVertDone)
447 if (!oSet)
449 oSet.emplace(rModel.GetItemPool());
450 MergeAttrFromSelectedCells(*oSet, false);
453 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
455 if (oSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::INVALID)
456 eAdj = oSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
458 rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
459 rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
460 rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
461 bVertDone = true;
463 break;
465 case SID_TABLE_DELETE_ROW:
466 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
467 rSet.DisableItem(SID_TABLE_DELETE_ROW);
468 break;
469 case SID_TABLE_DELETE_COL:
470 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
471 rSet.DisableItem(SID_TABLE_DELETE_COL);
472 break;
473 case SID_TABLE_DELETE_TABLE:
474 if( !mxTable.is() )
475 rSet.DisableItem(SID_TABLE_DELETE_TABLE);
476 break;
477 case SID_TABLE_MERGE_CELLS:
478 if( !mxTable.is() || !hasSelectedCells() )
479 rSet.DisableItem(SID_TABLE_MERGE_CELLS);
480 break;
481 case SID_TABLE_SPLIT_CELLS:
482 if( !hasSelectedCells() || !mxTable.is() )
483 rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
484 break;
486 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
487 case SID_TABLE_DISTRIBUTE_COLUMNS:
488 case SID_TABLE_DISTRIBUTE_ROWS:
490 bool bDistributeColumns = false;
491 bool bDistributeRows = false;
492 if( mxTable.is() )
494 CellPos aStart, aEnd;
495 getSelectedCells( aStart, aEnd );
497 bDistributeColumns = aStart.mnCol != aEnd.mnCol;
498 bDistributeRows = aStart.mnRow != aEnd.mnRow;
500 if( !bDistributeColumns )
501 rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
502 if( !bDistributeRows )
504 rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
505 rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
507 break;
510 default:
511 break;
513 nWhich = aIter.NextWhich();
518 void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
520 if(!checkTableObject())
521 return;
523 SdrTableObj& rTableObj(*mxTableObj.get());
524 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
525 bool bInsertAfter = true;
526 sal_uInt16 nCount = 0;
528 if( pArgs )
530 const SfxPoolItem* pItem = nullptr;
531 pArgs->GetItemState(nSId, false, &pItem);
532 if (pItem)
534 nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
535 if(const SfxBoolItem* pItem2 = pArgs->GetItemIfSet(SID_TABLE_PARAM_INSERT_AFTER))
536 bInsertAfter = pItem2->GetValue();
540 CellPos aStart, aEnd;
541 if( hasSelectedCells() )
543 getSelectedCells( aStart, aEnd );
545 else
547 if( bInsertAfter )
549 aStart.mnCol = mxTable->getColumnCount() - 1;
550 aStart.mnRow = mxTable->getRowCount() - 1;
551 aEnd = aStart;
555 if( rTableObj.IsTextEditActive() )
556 mrView.SdrEndTextEdit(true);
558 RemoveSelection();
560 static constexpr OUString sSize( u"Size"_ustr );
561 const bool bUndo(rModel.IsUndoEnabled());
563 switch( nSId )
565 case SID_TABLE_INSERT_COL:
567 TableModelNotifyGuard aGuard( mxTable.get() );
569 if( bUndo )
571 rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
572 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
575 Reference< XTableColumns > xCols( mxTable->getColumns() );
576 const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
577 const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
578 xCols->insertByIndex( nNewStartColumn, nNewColumns );
580 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
582 // Resolves fdo#61540
583 // On Insert before, the reference column whose size is going to be
584 // used for newly created column(s) is wrong. As the new columns are
585 // inserted before the reference column, the reference column moved
586 // to the new position by no., of new columns i.e (earlier+newcolumns).
587 Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
588 setPropertyValue( sSize,
589 Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
590 getPropertyValue( sSize ) );
593 // Copy cell properties
594 sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
595 sal_Int32 nRowSpan = 0;
596 bool bNewSpan = false;
598 for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
600 CellRef xSourceCell( mxTable->getCell( nPropSrcCol, nRow ) );
602 // When we insert new COLUMNs, we want to copy ROW spans.
603 if (xSourceCell.is() && nRowSpan == 0)
605 // we are not in a span yet. Let's find out if the current cell is in a span.
606 sal_Int32 nColSpan = sal_Int32();
607 sal_Int32 nSpanInfoCol = sal_Int32();
609 if( xSourceCell->getRowSpan() > 1 )
611 // The current cell is the top-left cell in a span.
612 // Get the span info and propagate it to the target.
613 nRowSpan = xSourceCell->getRowSpan();
614 nColSpan = xSourceCell->getColumnSpan();
615 nSpanInfoCol = nPropSrcCol;
617 else if( xSourceCell->isMerged() )
619 // The current cell is a middle cell in a 2D span.
620 // Look for the top-left cell in the span.
621 for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
623 CellRef xMergeInfoCell( mxTable->getCell( nSpanInfoCol, nRow ) );
624 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
626 nRowSpan = xMergeInfoCell->getRowSpan();
627 nColSpan = xMergeInfoCell->getColumnSpan();
628 break;
631 if( nRowSpan == 1 )
632 nRowSpan = 0;
635 // The target columns are outside the span; Start a new span.
636 if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
637 bNewSpan = true;
640 // Now copy the properties from the source to the targets
641 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
643 CellRef xTargetCell( mxTable->getCell( nNewStartColumn + nOffset, nRow ) );
644 if( xTargetCell.is() )
646 if( nRowSpan > 0 )
648 if( bNewSpan )
649 xTargetCell->merge( 1, nRowSpan );
650 else
651 xTargetCell->setMerged();
653 xTargetCell->copyFormatFrom( xSourceCell );
657 if( nRowSpan > 0 )
659 --nRowSpan;
660 bNewSpan = false;
664 if( bUndo )
665 rModel.EndUndo();
667 aStart.mnCol = nNewStartColumn;
668 aStart.mnRow = 0;
669 aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
670 aEnd.mnRow = mxTable->getRowCount() - 1;
671 break;
674 case SID_TABLE_INSERT_ROW:
676 TableModelNotifyGuard aGuard( mxTable.get() );
678 if( bUndo )
680 rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
681 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
684 Reference< XTableRows > xRows( mxTable->getRows() );
685 const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
686 const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
687 xRows->insertByIndex( nNewRowStart, nNewRows );
689 for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
691 Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
692 setPropertyValue( sSize,
693 Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
694 getPropertyValue( sSize ) );
697 // Copy the cell properties
698 sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
699 sal_Int32 nColSpan = 0;
700 bool bNewSpan = false;
702 for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
704 CellRef xSourceCell( mxTable->getCell( nCol, nPropSrcRow ) );
706 if (!xSourceCell.is())
707 continue;
709 // When we insert new ROWs, we want to copy COLUMN spans.
710 if( nColSpan == 0 )
712 // we are not in a span yet. Let's find out if the current cell is in a span.
713 sal_Int32 nRowSpan = sal_Int32();
714 sal_Int32 nSpanInfoRow = sal_Int32();
716 if( xSourceCell->getColumnSpan() > 1 )
718 // The current cell is the top-left cell in a span.
719 // Get the span info and propagate it to the target.
720 nColSpan = xSourceCell->getColumnSpan();
721 nRowSpan = xSourceCell->getRowSpan();
722 nSpanInfoRow = nPropSrcRow;
724 else if( xSourceCell->isMerged() )
726 // The current cell is a middle cell in a 2D span.
727 // Look for the top-left cell in the span.
728 for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
730 CellRef xMergeInfoCell( mxTable->getCell( nCol, nSpanInfoRow ) );
731 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
733 nColSpan = xMergeInfoCell->getColumnSpan();
734 nRowSpan = xMergeInfoCell->getRowSpan();
735 break;
738 if( nColSpan == 1 )
739 nColSpan = 0;
742 // Inserted rows are outside the span; Start a new span.
743 if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
744 bNewSpan = true;
747 // Now copy the properties from the source to the targets
748 for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
750 CellRef xTargetCell( mxTable->getCell( nCol, nNewRowStart + nOffset ) );
751 if( xTargetCell.is() )
753 if( nColSpan > 0 )
755 if( bNewSpan )
756 xTargetCell->merge( nColSpan, 1 );
757 else
758 xTargetCell->setMerged();
760 xTargetCell->copyFormatFrom( xSourceCell );
764 if( nColSpan > 0 )
766 --nColSpan;
767 bNewSpan = false;
771 if( bUndo )
772 rModel.EndUndo();
774 aStart.mnCol = 0;
775 aStart.mnRow = nNewRowStart;
776 aEnd.mnCol = mxTable->getColumnCount() - 1;
777 aEnd.mnRow = aStart.mnRow + nNewRows - 1;
778 break;
782 StartSelection( aStart );
783 UpdateSelection( aEnd );
787 void SvxTableController::onDelete( sal_uInt16 nSId )
789 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
790 if( !pTableObj || !mxTable.is() )
791 return;
793 if( nSId == SID_TABLE_DELETE_TABLE )
795 if( pTableObj->IsTextEditActive() )
796 mrView.SdrEndTextEdit(true);
798 mrView.DeleteMarkedObj();
800 else if( hasSelectedCells() )
802 CellPos aStart, aEnd;
803 getSelectedCells( aStart, aEnd );
805 if( pTableObj->IsTextEditActive() )
806 mrView.SdrEndTextEdit(true);
808 RemoveSelection();
810 bool bDeleteTable = false;
811 switch( nSId )
813 case SID_TABLE_DELETE_COL:
815 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
816 if( nRemovedColumns == mxTable->getColumnCount() )
818 bDeleteTable = true;
820 else
822 Reference< XTableColumns > xCols( mxTable->getColumns() );
823 xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
824 EditCell(aStart, nullptr, TblAction::NONE);
826 break;
829 case SID_TABLE_DELETE_ROW:
831 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
832 if( nRemovedRows == mxTable->getRowCount() )
834 bDeleteTable = true;
836 else
838 Reference< XTableRows > xRows( mxTable->getRows() );
839 xRows->removeByIndex( aStart.mnRow, nRemovedRows );
840 EditCell(aStart, nullptr, TblAction::NONE);
842 break;
846 if( bDeleteTable )
847 mrView.DeleteMarkedObj();
848 else
849 UpdateTableShape();
854 void SvxTableController::onSelect( sal_uInt16 nSId )
856 if( !mxTable.is() )
857 return;
859 const sal_Int32 nRowCount = mxTable->getRowCount();
860 const sal_Int32 nColCount = mxTable->getColumnCount();
861 if( !(nRowCount && nColCount) )
862 return;
864 CellPos aStart, aEnd;
865 getSelectedCells( aStart, aEnd );
867 switch( nSId )
869 case SID_TABLE_SELECT_ALL:
870 aEnd.mnCol = 0; aEnd.mnRow = 0;
871 aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
872 break;
873 case SID_TABLE_SELECT_COL:
874 aEnd.mnRow = nRowCount - 1;
875 aStart.mnRow = 0;
876 break;
877 case SID_TABLE_SELECT_ROW:
878 aEnd.mnCol = nColCount - 1;
879 aStart.mnCol = 0;
880 break;
883 StartSelection( aEnd );
884 gotoCell( aStart, true, nullptr );
887 SvxBoxItem SvxTableController::TextDistancesToSvxBoxItem(const SfxItemSet& rAttrSet)
889 // merge drawing layer text distance items into SvxBoxItem used by the dialog
890 SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
891 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
892 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
893 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
894 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
895 return aBoxItem;
898 void SvxTableController::SvxBoxItemToTextDistances(const SvxBoxItem& pOriginalItem, SfxItemSet& rAttrSet)
900 const SvxBoxItem* pNewItem( rAttrSet.GetItemIfSet( SDRATTR_TABLE_BORDER ) );
901 if ( !pNewItem )
902 return;
904 if( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) != pOriginalItem.GetDistance( SvxBoxItemLine::LEFT ) )
905 rAttrSet.Put(makeSdrTextLeftDistItem( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) ) );
907 if( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) != pOriginalItem.GetDistance( SvxBoxItemLine::RIGHT ) )
908 rAttrSet.Put(makeSdrTextRightDistItem( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) ) );
910 if( pNewItem->GetDistance( SvxBoxItemLine::TOP ) != pOriginalItem.GetDistance( SvxBoxItemLine::TOP ) )
911 rAttrSet.Put(makeSdrTextUpperDistItem( pNewItem->GetDistance( SvxBoxItemLine::TOP ) ) );
913 if( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) != pOriginalItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
914 rAttrSet.Put(makeSdrTextLowerDistItem( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) ) );
917 void SvxTableController::onFormatTable(const SfxRequest& rReq)
919 if(!mxTableObj.get().is())
920 return;
922 SdrTableObj& rTableObj(*mxTableObj.get());
923 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
924 const SfxItemSet* pArgs = rReq.GetArgs();
926 if(pArgs)
927 return;
929 SfxItemSet aNewAttr(rModel.GetItemPool());
931 // merge drawing layer text distance items into SvxBoxItem used by the dialog
932 auto xBoxItem(std::make_shared<SvxBoxItem>(TextDistancesToSvxBoxItem(aNewAttr)));
933 auto xBoxInfoItem(std::make_shared<SvxBoxInfoItem>(aNewAttr.Get(SDRATTR_TABLE_BORDER_INNER)));
935 MergeAttrFromSelectedCells(aNewAttr, false);
936 FillCommonBorderAttrFromSelectedCells(*xBoxItem, *xBoxInfoItem);
937 aNewAttr.Put(*xBoxItem);
938 aNewAttr.Put(*xBoxInfoItem);
940 // Fill in shadow properties.
941 const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
942 for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
944 if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
946 continue;
949 aNewAttr.Put(rTableItemSet.Get(nWhich));
952 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
953 VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
954 rReq.GetFrameWeld(),
955 aNewAttr,
956 rModel, false) );
958 // Even Cancel Button is returning positive(101) value,
959 xDlg->StartExecuteAsync([xDlg, this, xBoxItem=std::move(xBoxItem),
960 xBoxInfoItem=std::move(xBoxInfoItem)](int nResult){
961 if (nResult == RET_OK)
963 SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
965 //Only properties that were unchanged by the dialog appear in this
966 //itemset. We had constructed these two properties from other
967 //ones, so if they were not changed, then forcible set them back to
968 //their originals in the new result set so we can decompose that
969 //unchanged state back to their input properties
970 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
972 aNewSet.Put(*xBoxItem);
974 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
976 aNewSet.Put(*xBoxInfoItem);
979 SvxBoxItemToTextDistances(*xBoxItem, aNewSet);
981 if (checkTableObject() && mxTable.is())
983 // Create a single undo action when applying the result of the dialog.
984 SdrTableObj& rTableObject(*mxTableObj.get());
985 SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
986 bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
987 if (bUndo)
989 rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
992 this->SetAttrToSelectedCells(aNewSet, false);
994 this->SetAttrToSelectedShape(aNewSet);
996 if (bUndo)
998 rSdrModel.EndUndo();
1003 xDlg->disposeOnce();
1007 void SvxTableController::Execute( SfxRequest& rReq )
1009 const sal_uInt16 nSId = rReq.GetSlot();
1010 switch( nSId )
1012 case SID_TABLE_INSERT_ROW:
1013 case SID_TABLE_INSERT_COL:
1014 onInsert( nSId, rReq.GetArgs() );
1015 break;
1016 case SID_TABLE_DELETE_ROW:
1017 case SID_TABLE_DELETE_COL:
1018 case SID_TABLE_DELETE_TABLE:
1019 onDelete( nSId );
1020 break;
1021 case SID_TABLE_SELECT_ALL:
1022 case SID_TABLE_SELECT_COL:
1023 case SID_TABLE_SELECT_ROW:
1024 onSelect( nSId );
1025 break;
1026 case SID_FORMAT_TABLE_DLG:
1027 onFormatTable( rReq );
1028 break;
1030 case SID_FRAME_LINESTYLE:
1031 case SID_FRAME_LINECOLOR:
1032 case SID_ATTR_BORDER:
1034 const SfxItemSet* pArgs = rReq.GetArgs();
1035 if( pArgs )
1036 ApplyBorderAttr( *pArgs );
1038 break;
1040 case SID_ATTR_FILL_STYLE:
1042 const SfxItemSet* pArgs = rReq.GetArgs();
1043 if( pArgs )
1044 SetAttributes( *pArgs, false );
1046 break;
1048 case SID_TABLE_MERGE_CELLS:
1049 MergeMarkedCells();
1050 break;
1052 case SID_TABLE_SPLIT_CELLS:
1053 SplitMarkedCells(rReq);
1054 break;
1056 case SID_TABLE_MINIMAL_COLUMN_WIDTH:
1057 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
1058 break;
1060 case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
1061 DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
1062 break;
1064 case SID_TABLE_DISTRIBUTE_COLUMNS:
1065 DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
1066 break;
1068 case SID_TABLE_MINIMAL_ROW_HEIGHT:
1069 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
1070 break;
1072 case SID_TABLE_OPTIMAL_ROW_HEIGHT:
1073 DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
1074 break;
1076 case SID_TABLE_DISTRIBUTE_ROWS:
1077 DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
1078 break;
1080 case SID_TABLE_VERT_BOTTOM:
1081 case SID_TABLE_VERT_CENTER:
1082 case SID_TABLE_VERT_NONE:
1083 SetVertical( nSId );
1084 break;
1086 case SID_AUTOFORMAT:
1087 case SID_TABLE_SORT_DIALOG:
1088 case SID_TABLE_AUTOSUM:
1089 default:
1090 break;
1092 case SID_TABLE_STYLE:
1093 SetTableStyle( rReq.GetArgs() );
1094 break;
1096 case SID_TABLE_STYLE_SETTINGS:
1097 SetTableStyleSettings( rReq.GetArgs() );
1098 break;
1099 case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
1100 changeTableEdge(rReq);
1101 break;
1105 void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
1107 if(!checkTableObject())
1108 return;
1110 SdrTableObj& rTableObj(*mxTableObj.get());
1111 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1113 if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
1114 return;
1116 const SfxStringItem* pArg = &pArgs->Get( SID_TABLE_STYLE );
1117 if( !(pArg && mxTable.is()) )
1118 return;
1122 Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
1123 Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
1124 Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( u"table"_ustr ), UNO_QUERY_THROW );
1126 if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
1128 // found table style with the same name
1129 Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
1131 const bool bUndo = rModel.IsUndoEnabled();
1133 if( bUndo )
1135 rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
1136 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1139 rTableObj.setTableStyle( xNewTableStyle );
1141 const sal_Int32 nRowCount = mxTable->getRowCount();
1142 const sal_Int32 nColCount = mxTable->getColumnCount();
1143 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1145 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
1147 CellRef xCell( mxTable->getCell( nCol, nRow ) );
1148 if( xCell.is() )
1150 SfxItemSet aSet( xCell->GetItemSet() );
1151 bool bChanges = false;
1152 SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
1153 SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
1154 if (pStyleSheet)
1156 const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
1158 for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
1160 if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
1162 aSet.ClearItem( nWhich );
1163 bChanges = true;
1168 if( bChanges )
1170 if( bUndo )
1171 xCell->AddUndo();
1173 xCell->SetMergedItemSetAndBroadcast( aSet, true );
1177 catch( Exception& )
1179 TOOLS_WARN_EXCEPTION("svx.table", "");
1183 if( bUndo )
1184 rModel.EndUndo();
1187 catch( Exception& )
1189 TOOLS_WARN_EXCEPTION("svx.table", "");
1193 void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
1195 if(!checkTableObject())
1196 return;
1198 SdrTableObj& rTableObj(*mxTableObj.get());
1199 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1201 TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
1202 const SfxBoolItem *pPoolItem=nullptr;
1204 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTROWSTYLE, false)) )
1205 aSettings.mbUseFirstRow = pPoolItem->GetValue();
1207 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTROWSTYLE, false)) )
1208 aSettings.mbUseLastRow = pPoolItem->GetValue();
1210 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGROWSTYLE, false)) )
1211 aSettings.mbUseRowBanding = pPoolItem->GetValue();
1213 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTCOLUMNSTYLE, false)) )
1214 aSettings.mbUseFirstColumn = pPoolItem->GetValue();
1216 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTCOLUMNSTYLE, false)) )
1217 aSettings.mbUseLastColumn = pPoolItem->GetValue();
1219 if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGCOLUMNSTYLE, false)) )
1220 aSettings.mbUseColumnBanding = pPoolItem->GetValue();
1222 if( aSettings == rTableObj.getTableStyleSettings() )
1223 return;
1225 const bool bUndo(rModel.IsUndoEnabled());
1227 if( bUndo )
1229 rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
1230 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1233 rTableObj.setTableStyleSettings( aSettings );
1235 if( bUndo )
1236 rModel.EndUndo();
1239 void SvxTableController::SetVertical( sal_uInt16 nSId )
1241 if(!checkTableObject())
1242 return;
1244 SdrTableObj& rTableObj(*mxTableObj.get());
1245 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1247 TableModelNotifyGuard aGuard( mxTable.get() );
1248 const bool bUndo(rModel.IsUndoEnabled());
1250 if (bUndo)
1252 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
1253 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
1256 CellPos aStart, aEnd;
1257 getSelectedCells( aStart, aEnd );
1259 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
1261 switch( nSId )
1263 case SID_TABLE_VERT_BOTTOM:
1264 eAdj = SDRTEXTVERTADJUST_BOTTOM;
1265 break;
1266 case SID_TABLE_VERT_CENTER:
1267 eAdj = SDRTEXTVERTADJUST_CENTER;
1268 break;
1269 //case SID_TABLE_VERT_NONE:
1270 default:
1271 break;
1274 SdrTextVertAdjustItem aItem( eAdj );
1276 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1278 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1280 CellRef xCell( mxTable->getCell( nCol, nRow ) );
1281 if( xCell.is() )
1283 if (bUndo)
1284 xCell->AddUndo();
1285 SfxItemSet aSet(xCell->GetItemSet());
1286 aSet.Put(aItem);
1287 xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
1292 UpdateTableShape();
1294 if (bUndo)
1295 rModel.EndUndo();
1298 void SvxTableController::MergeMarkedCells()
1300 CellPos aStart, aEnd;
1301 getSelectedCells( aStart, aEnd );
1302 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
1303 if( pTableObj )
1305 if( pTableObj->IsTextEditActive() )
1306 mrView.SdrEndTextEdit(true);
1308 TableModelNotifyGuard aGuard( mxTable.get() );
1309 MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
1313 void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
1315 if(!checkTableObject() || !mxTable.is())
1316 return;
1318 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1319 VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
1321 xDlg->StartExecuteAsync([xDlg, this](int) {
1322 const sal_Int32 nCount = xDlg->GetCount() - 1;
1324 if( nCount < 1 )
1325 return;
1327 CellPos aStart, aEnd;
1328 getSelectedCells( aStart, aEnd );
1329 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
1330 const sal_Int32 nRowCount = mxTable->getRowCount();
1331 const sal_Int32 nColCount = mxTable->getColumnCount();
1332 SdrTableObj& rTableObj(*mxTableObj.get());
1334 if( rTableObj.IsTextEditActive() )
1335 mrView.SdrEndTextEdit(true);
1337 TableModelNotifyGuard aGuard( mxTable.get() );
1338 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1339 const bool bUndo(rModel.IsUndoEnabled());
1341 if( bUndo )
1343 rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
1344 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1347 if( xDlg->IsHorizontal() )
1349 xRange->split( 0, nCount );
1351 else
1353 xRange->split( nCount, 0 );
1356 if( bUndo )
1357 rModel.EndUndo();
1359 aEnd.mnRow += mxTable->getRowCount() - nRowCount;
1360 aEnd.mnCol += mxTable->getColumnCount() - nColCount;
1362 setSelectedCells( aStart, aEnd );
1364 xDlg->disposeOnce();
1368 void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
1370 if(!checkTableObject())
1371 return;
1373 SdrTableObj& rTableObj(*mxTableObj.get());
1374 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1375 const bool bUndo(rModel.IsUndoEnabled());
1377 if( bUndo )
1379 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
1380 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1383 CellPos aStart, aEnd;
1384 getSelectedCells( aStart, aEnd );
1385 rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
1387 if( bUndo )
1388 rModel.EndUndo();
1391 void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
1393 if(!checkTableObject())
1394 return;
1396 SdrTableObj& rTableObj(*mxTableObj.get());
1397 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1398 const bool bUndo(rModel.IsUndoEnabled());
1400 if( bUndo )
1402 rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
1403 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1406 CellPos aStart, aEnd;
1407 getSelectedCells( aStart, aEnd );
1408 rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
1410 if( bUndo )
1411 rModel.EndUndo();
1414 bool SvxTableController::HasMarked() const
1416 return mbCellSelectionMode && mxTable.is();
1419 bool SvxTableController::DeleteMarked()
1421 if(!checkTableObject() || !HasMarked())
1422 return false;
1424 SdrTableObj& rTableObj(*mxTableObj.get());
1425 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1426 const bool bUndo(rModel.IsUndoEnabled());
1427 bool bDeleteTable = false;
1429 if (bUndo)
1430 rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
1432 CellPos aStart, aEnd;
1433 getSelectedCells( aStart, aEnd );
1434 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
1435 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
1436 if( nRemovedColumns == mxTable->getColumnCount() && nRemovedRows == mxTable->getRowCount())
1438 bDeleteTable = true;
1440 else
1442 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1444 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1446 CellRef xCell( mxTable->getCell( nCol, nRow ) );
1447 if (xCell.is() && xCell->hasText())
1449 if (bUndo)
1450 xCell->AddUndo();
1451 xCell->SetOutlinerParaObject(std::nullopt);
1457 if (bDeleteTable)
1458 mrView.DeleteMarkedObj();
1460 if (bUndo)
1461 rModel.EndUndo();
1463 if (!bDeleteTable)
1464 UpdateTableShape();
1465 return true;
1468 bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
1470 if( hasSelectedCells() )
1472 rpStyleSheet = nullptr;
1474 if( mxTable.is() )
1476 SfxStyleSheet* pRet=nullptr;
1477 bool b1st=true;
1479 CellPos aStart, aEnd;
1480 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
1482 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1484 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1486 CellRef xCell( mxTable->getCell( nCol, nRow ) );
1487 if( xCell.is() )
1489 SfxStyleSheet* pSS=xCell->GetStyleSheet();
1490 if(b1st)
1492 pRet=pSS;
1494 else if(pRet != pSS)
1496 return true;
1498 b1st=false;
1502 rpStyleSheet = pRet;
1503 return true;
1506 return false;
1509 bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
1511 if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
1513 if( mxTable.is() )
1515 CellPos aStart, aEnd;
1516 getSelectedCells( aStart, aEnd );
1518 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1520 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1522 CellRef xCell( mxTable->getCell( nCol, nRow ) );
1523 if( xCell.is() )
1524 xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
1528 UpdateTableShape();
1529 return true;
1532 return false;
1535 void SvxTableController::changeTableEdge(const SfxRequest& rReq)
1537 if (!checkTableObject())
1538 return;
1540 const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
1541 const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
1542 const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
1544 if (!(pType && pIndex && pOffset))
1545 return;
1547 const OUString sType = pType->GetValue();
1548 const sal_uInt16 nIndex = pIndex->GetValue();
1549 const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
1551 SdrTableObj& rTableObj(*mxTableObj.get());
1553 sal_Int32 nEdgeIndex = -1;
1554 bool bHorizontal = sType.startsWith("row");
1556 if (sType == "column-left" || sType == "row-left")
1558 nEdgeIndex = 0;
1560 else if (sType == "column-right")
1562 // Number of edges = number of columns + 1
1563 nEdgeIndex = rTableObj.getColumnCount();
1565 else if (sType == "row-right")
1567 // Number of edges = number of rows + 1
1568 nEdgeIndex = rTableObj.getRowCount();
1570 else if (sType == "column-middle" || sType == "row-middle")
1572 nEdgeIndex = nIndex + 1;
1575 if (nEdgeIndex < 0)
1576 return;
1578 TableModelNotifyGuard aGuard(mxTable.get());
1579 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1580 const bool bUndo(rModel.IsUndoEnabled());
1581 if (bUndo)
1583 auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
1584 rModel.BegUndo(pUndoObject->GetComment());
1586 auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
1587 if (pGeoUndo)
1588 pGeoUndo->SetSkipChangeLayout(true);
1590 rModel.AddUndo(std::move(pUndoObject));
1592 tools::Rectangle aBoundRect;
1593 if (rTableObj.GetUserCall())
1594 aBoundRect = rTableObj.GetLastBoundRect();
1595 rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
1596 rTableObj.SetChanged();
1597 rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
1598 if (bUndo)
1599 rModel.EndUndo();
1602 // internals
1605 bool SvxTableController::checkTableObject()
1607 return mxTableObj.get().is();
1611 SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
1613 const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
1614 const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
1615 const bool bTextEdit = mrView.IsTextEdit();
1617 TblAction nAction = TblAction::HandledByView;
1619 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
1620 if( !pTableObj )
1621 return nAction;
1623 // handle special keys
1624 const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
1625 switch( nCode )
1627 case awt::Key::ESCAPE: // handle escape
1629 if( bTextEdit )
1631 // escape during text edit ends text edit
1632 nAction = TblAction::StopTextEdit;
1634 if( mbCellSelectionMode )
1636 // escape with selected cells removes selection
1637 nAction = TblAction::RemoveSelection;
1639 break;
1641 case awt::Key::RETURN: // handle return
1643 if( !bMod1 && !bMod2 && !bTextEdit )
1645 // when not already editing, return starts text edit
1646 setSelectionStart( SdrTableObj::getFirstCell() );
1647 nAction = TblAction::EditCell;
1649 break;
1651 case awt::Key::F2: // f2 toggles text edit
1653 if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
1656 else if( bTextEdit )
1658 // f2 during text edit stops text edit
1659 nAction = TblAction::StopTextEdit;
1661 else if( mbCellSelectionMode )
1663 // f2 with selected cells removes selection
1664 nAction = TblAction::RemoveSelection;
1666 else
1668 // f2 with no selection and no text edit starts text edit
1669 setSelectionStart( SdrTableObj::getFirstCell() );
1670 nAction = TblAction::EditCell;
1672 break;
1674 case awt::Key::HOME:
1675 case awt::Key::NUM7:
1677 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1679 if( bMod1 && !bMod2 )
1681 // ctrl + home jumps to first cell
1682 nAction = TblAction::GotoFirstCell;
1684 else if( !bMod1 && bMod2 )
1686 // alt + home jumps to first column
1687 nAction = TblAction::GotoFirstColumn;
1690 break;
1692 case awt::Key::END:
1693 case awt::Key::NUM1:
1695 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1697 if( bMod1 && !bMod2 )
1699 // ctrl + end jumps to last cell
1700 nAction = TblAction::GotoLastCell;
1702 else if( !bMod1 && bMod2 )
1704 // alt + home jumps to last column
1705 nAction = TblAction::GotoLastColumn;
1708 break;
1711 case awt::Key::TAB:
1713 if( bTextEdit || mbCellSelectionMode )
1714 nAction = TblAction::Tab;
1715 break;
1718 case awt::Key::UP:
1719 case awt::Key::NUM8:
1720 case awt::Key::DOWN:
1721 case awt::Key::NUM2:
1722 case awt::Key::LEFT:
1723 case awt::Key::NUM4:
1724 case awt::Key::RIGHT:
1725 case awt::Key::NUM6:
1728 if( !bMod1 && bMod2 )
1730 if(bTextEdit || mbCellSelectionMode)
1732 if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
1734 nAction = TblAction::GotoLeftCell;
1735 break;
1737 else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
1739 nAction = TblAction::GotoRightCell;
1740 break;
1745 bool bTextMove = false;
1746 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
1747 if( pOLV )
1749 RemoveSelection();
1750 // during text edit, check if we navigate out of the cell
1751 ESelection aOldSelection = pOLV->GetSelection();
1752 pOLV->PostKeyEvent(rKEvt);
1753 bTextMove = aOldSelection == pOLV->GetSelection();
1754 if( !bTextMove )
1756 nAction = TblAction::NONE;
1760 if( mbCellSelectionMode || bTextMove )
1762 // no text edit, navigate in cells if selection active
1763 switch( nCode )
1765 case awt::Key::LEFT:
1766 case awt::Key::NUM4:
1767 nAction = TblAction::GotoLeftCell;
1768 break;
1769 case awt::Key::RIGHT:
1770 case awt::Key::NUM6:
1771 nAction = TblAction::GotoRightCell;
1772 break;
1773 case awt::Key::DOWN:
1774 case awt::Key::NUM2:
1775 nAction = TblAction::GotoDownCell;
1776 break;
1777 case awt::Key::UP:
1778 case awt::Key::NUM8:
1779 nAction = TblAction::GotoUpCell;
1780 break;
1783 break;
1785 case awt::Key::PAGEUP:
1786 if( bMod2 )
1787 nAction = TblAction::GotoFirstRow;
1788 break;
1790 case awt::Key::PAGEDOWN:
1791 if( bMod2 )
1792 nAction = TblAction::GotoLastRow;
1793 break;
1795 return nAction;
1798 bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
1800 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
1801 if( !pTableObj )
1802 return false;
1804 switch( nAction )
1806 case TblAction::GotoFirstCell:
1808 gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
1809 break;
1812 case TblAction::GotoLeftCell:
1814 gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
1815 break;
1818 case TblAction::GotoRightCell:
1820 gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
1821 break;
1824 case TblAction::GotoLastCell:
1826 gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
1827 break;
1830 case TblAction::GotoFirstColumn:
1832 CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
1833 gotoCell( aPos, bSelect, pWindow, nAction );
1834 break;
1837 case TblAction::GotoLastColumn:
1839 CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
1840 gotoCell( aPos, bSelect, pWindow, nAction );
1841 break;
1844 case TblAction::GotoFirstRow:
1846 CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
1847 gotoCell( aPos, bSelect, pWindow, nAction );
1848 break;
1851 case TblAction::GotoUpCell:
1853 gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1854 break;
1857 case TblAction::GotoDownCell:
1859 gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1860 break;
1863 case TblAction::GotoLastRow:
1865 CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
1866 gotoCell( aPos, bSelect, pWindow, nAction );
1867 break;
1870 case TblAction::EditCell:
1871 EditCell( getSelectionStart(), pWindow, nAction );
1872 break;
1874 case TblAction::StopTextEdit:
1875 StopTextEdit();
1876 break;
1878 case TblAction::RemoveSelection:
1879 RemoveSelection();
1880 break;
1882 case TblAction::Tab:
1884 if( bSelect )
1885 gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
1886 else
1888 CellPos aSelectionEnd( getSelectionEnd() );
1889 CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
1890 if( aSelectionEnd == aNextCell )
1892 onInsert( SID_TABLE_INSERT_ROW );
1893 aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
1895 gotoCell( aNextCell, false, pWindow, nAction );
1897 break;
1899 default:
1900 break;
1903 return nAction != TblAction::HandledByView;
1907 void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
1909 auto pTable = mxTableObj.get();
1910 if( pTable && pTable->IsTextEditActive() )
1911 mrView.SdrEndTextEdit(true);
1913 if( bSelect )
1915 maCursorLastPos = rPos;
1916 if( pTable )
1917 pTable->setActiveCell( rPos );
1919 if( !mbCellSelectionMode )
1921 setSelectedCells( maCursorFirstPos, rPos );
1923 else
1925 UpdateSelection( rPos );
1928 else
1930 RemoveSelection();
1931 EditCell( rPos, pWindow, nAction );
1936 const CellPos& SvxTableController::getSelectionStart()
1938 checkCell( maCursorFirstPos );
1939 return maCursorFirstPos;
1943 void SvxTableController::setSelectionStart( const CellPos& rPos )
1945 maCursorFirstPos = rPos;
1949 const CellPos& SvxTableController::getSelectionEnd()
1951 checkCell( maCursorLastPos );
1952 return maCursorLastPos;
1956 void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
1958 if(!checkTableObject() || !mxTable.is())
1959 return;
1963 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
1965 if( xRange->isMergeable() )
1967 SdrTableObj& rTableObj(*mxTableObj.get());
1968 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1969 const bool bUndo(rModel.IsUndoEnabled());
1971 if( bUndo )
1973 rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
1974 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1977 xRange->merge();
1978 mbHasJustMerged = true;
1979 setSelectedCells( maCursorFirstPos, maCursorFirstPos );
1981 if( bUndo )
1982 rModel.EndUndo();
1985 catch( Exception& )
1987 TOOLS_WARN_EXCEPTION( "svx.table", "" );
1992 void SvxTableController::checkCell( CellPos& rPos ) const
1994 if( !mxTable.is() )
1995 return;
1999 if( rPos.mnCol >= mxTable->getColumnCount() )
2000 rPos.mnCol = mxTable->getColumnCount()-1;
2002 if( rPos.mnRow >= mxTable->getRowCount() )
2003 rPos.mnRow = mxTable->getRowCount()-1;
2005 catch( Exception& )
2007 TOOLS_WARN_EXCEPTION("svx.table", "");
2012 void SvxTableController::findMergeOrigin( CellPos& rPos )
2014 if( !mxTable.is() )
2015 return;
2019 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
2020 if( xCell->isMerged() )
2022 ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
2025 catch( Exception& )
2027 TOOLS_WARN_EXCEPTION("svx.table", "");
2032 void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
2034 SdrPageView* pPV(mrView.GetSdrPageView());
2036 if(nullptr == pPV || !checkTableObject())
2037 return;
2039 SdrTableObj& rTableObj(*mxTableObj.get());
2041 if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
2042 return;
2044 bool bEmptyOutliner = false;
2046 if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
2048 ::Outliner* pOutl = mrView.GetTextEditOutliner();
2049 sal_Int32 nParaCnt = pOutl->GetParagraphCount();
2050 Paragraph* p1stPara = pOutl->GetParagraph( 0 );
2052 if(nParaCnt==1 && p1stPara)
2054 // with only one paragraph
2055 if (pOutl->GetText(p1stPara).isEmpty())
2057 bEmptyOutliner = true;
2062 CellPos aPos( rPos );
2063 findMergeOrigin( aPos );
2065 if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
2066 return;
2068 if( rTableObj.IsTextEditActive() )
2069 mrView.SdrEndTextEdit(true);
2071 rTableObj.setActiveCell( aPos );
2073 // create new outliner, owner will be the SdrObjEditView
2074 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2075 std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
2077 if (pOutl && rTableObj.IsVerticalWriting())
2078 pOutl->SetVertical( true );
2080 if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
2081 return;
2083 maCursorLastPos = maCursorFirstPos = rPos;
2085 OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
2087 // Move cursor to end of text
2088 ESelection aNewSelection;
2090 const WritingMode eMode = rTableObj.GetWritingMode();
2091 if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
2093 const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
2094 ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
2096 if( bLast )
2097 aNewSelection = ESelection::AtEnd();
2099 pOLV->SetSelection(aNewSelection);
2103 void SvxTableController::StopTextEdit()
2105 if(mrView.IsTextEdit())
2107 mrView.SdrEndTextEdit();
2108 mrView.SetCurrentObj(SdrObjKind::Table);
2109 mrView.SetEditMode(SdrViewEditMode::Edit);
2114 void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
2116 if( mbCellSelectionMode )
2118 checkCell( maCursorFirstPos );
2119 checkCell( maCursorLastPos );
2121 rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2122 rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2123 rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
2124 rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
2126 if( !mxTable.is() )
2127 return;
2129 bool bExt = false;
2132 bExt = false;
2133 for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
2135 for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
2137 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
2138 if( !xCell.is() )
2139 continue;
2141 if( xCell->isMerged() )
2143 CellPos aPos( nCol, nRow );
2144 findMergeOrigin( aPos );
2145 if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
2147 rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
2148 rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
2149 bExt = true;
2152 else
2154 if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
2156 rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
2157 rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
2158 bExt = true;
2164 while(bExt);
2166 else if(mrView.IsTextEdit())
2168 rFirst = getSelectionStart();
2169 findMergeOrigin( rFirst );
2170 rLast = rFirst;
2172 if( mxTable.is() )
2174 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
2175 if( xCell.is() )
2177 rLast.mnCol += xCell->getColumnSpan() - 1;
2178 rLast.mnRow += xCell->getRowSpan() - 1;
2182 else
2184 rFirst.mnCol = 0;
2185 rFirst.mnRow = 0;
2186 if( mxTable.is() )
2188 rLast.mnRow = mxTable->getRowCount()-1;
2189 rLast.mnCol = mxTable->getColumnCount()-1;
2191 else
2193 rLast.mnRow = 0;
2194 rLast.mnCol = 0;
2200 void SvxTableController::StartSelection( const CellPos& rPos )
2202 StopTextEdit();
2203 mbCellSelectionMode = true;
2204 maCursorLastPos = maCursorFirstPos = rPos;
2205 mrView.MarkListHasChanged();
2209 void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
2211 StopTextEdit();
2212 mbCellSelectionMode = true;
2213 maCursorFirstPos = rStart;
2214 UpdateSelection( rEnd );
2218 bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
2220 if(!checkTableObject() || !mxTable.is())
2221 return false;
2223 SdrTableObj& rTableObj(*mxTableObj.get());
2224 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2226 if (mrView.IsTextEdit())
2227 return true;
2229 CellPos aStart, aEnd;
2231 if(hasSelectedCells())
2233 getSelectedCells(aStart, aEnd);
2235 else
2237 aStart.mnRow = 0;
2238 aStart.mnCol = 0;
2239 aEnd.mnRow = mxTable->getRowCount() - 1;
2240 aEnd.mnCol = mxTable->getColumnCount() - 1;
2243 for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
2245 for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
2247 CellRef xCell(mxTable->getCell(nCol, nRow));
2248 if (xCell.is())
2250 if (rModel.IsUndoEnabled())
2251 xCell->AddUndo();
2253 SfxItemSet aCellSet(xCell->GetItemSet());
2254 if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
2256 xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
2262 UpdateTableShape();
2264 return true;
2268 void SvxTableController::UpdateSelection( const CellPos& rPos )
2270 maCursorLastPos = rPos;
2271 mrView.MarkListHasChanged();
2275 void SvxTableController::clearSelection()
2277 RemoveSelection();
2281 void SvxTableController::selectAll()
2283 if( mxTable.is() )
2285 CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
2286 if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
2288 CellPos aPos1;
2289 setSelectedCells( aPos1, aPos2 );
2295 void SvxTableController::RemoveSelection()
2297 if( mbCellSelectionMode )
2299 mbCellSelectionMode = false;
2300 mrView.MarkListHasChanged();
2305 void SvxTableController::onTableModified()
2307 if( mnUpdateEvent == nullptr )
2308 mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
2312 void SvxTableController::updateSelectionOverlay()
2314 // There is no need to update selection overlay after merging cells
2315 // since the selection overlay should remain the same
2316 if ( mbHasJustMerged )
2317 return;
2319 destroySelectionOverlay();
2320 if( !mbCellSelectionMode )
2321 return;
2323 rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
2324 if( !pTableObj )
2325 return;
2327 sdr::overlay::OverlayObjectCell::RangeVector aRanges;
2329 tools::Rectangle aStartRect, aEndRect;
2330 CellPos aStart,aEnd;
2331 getSelectedCells( aStart, aEnd );
2332 pTableObj->getCellBounds( aStart, aStartRect );
2334 basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
2335 a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
2337 findMergeOrigin( aEnd );
2338 pTableObj->getCellBounds( aEnd, aEndRect );
2339 a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
2340 a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
2341 aRanges.push_back( a2DRange );
2343 ::Color aHighlight( COL_BLUE );
2344 OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
2345 if( pOutDev )
2346 aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
2348 const sal_uInt32 nCount = mrView.PaintWindowCount();
2349 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
2351 SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
2352 if( pPaintWindow )
2354 const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
2355 if( xOverlayManager.is() )
2357 std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, std::vector(aRanges) ));
2359 xOverlayManager->add(*pOverlay);
2360 mpSelectionOverlay.emplace();
2361 mpSelectionOverlay->append(std::move(pOverlay));
2366 // If tiled rendering, emit callbacks for sdr table selection.
2367 if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
2368 return;
2370 tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
2372 if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2373 aSelection = o3tl::toTwips(aSelection, o3tl::Length::mm100);
2375 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2377 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString());
2378 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString());
2383 void SvxTableController::destroySelectionOverlay()
2385 if( !mpSelectionOverlay )
2386 return;
2388 mpSelectionOverlay.reset();
2390 if (comphelper::LibreOfficeKit::isActive())
2392 // Clear the LOK text selection so far provided by this table.
2393 if(SfxViewShell* pViewShell = SfxViewShell::Current())
2395 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY"_ostr);
2396 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY"_ostr);
2397 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY"_ostr);
2398 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY"_ostr);
2404 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
2406 if( !mxTable.is() )
2407 return;
2409 CellPos aStart, aEnd;
2410 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
2412 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2414 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2416 CellRef xCell( mxTable->getCell( nCol, nRow ) );
2417 if( xCell.is() && !xCell->isMerged() )
2419 const SfxItemSet& rSet = xCell->GetItemSet();
2420 SfxWhichIter aIter(rSet);
2421 sal_uInt16 nWhich(aIter.FirstWhich());
2422 while(nWhich)
2424 SfxItemState nState = aIter.GetItemState(false);
2425 if(!bOnlyHardAttr)
2427 if(SfxItemState::INVALID == nState)
2428 rAttr.InvalidateItem(nWhich);
2429 else
2430 rAttr.MergeValue(rSet.Get(nWhich));
2432 else if(SfxItemState::SET == nState)
2434 const SfxPoolItem& rItem = rSet.Get(nWhich);
2435 rAttr.MergeValue(rItem);
2438 nWhich = aIter.NextWhich();
2446 static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
2448 if( pNew )
2450 const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
2451 if( pOld )
2453 SvxBorderLine aNewLine( *pNew );
2454 aNewLine.SetColor( pOld->GetColor() );
2455 rNewFrame.SetLine( &aNewLine, nLine );
2456 return;
2459 rNewFrame.SetLine( pNew, nLine );
2463 static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
2465 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2467 // current cell is outside the selection
2469 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2471 if (nCellPosFlags & CellPosFlag::Upper)
2473 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
2474 rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
2476 else if (nCellPosFlags & CellPosFlag::Lower)
2478 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
2479 rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
2482 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2484 if (nCellPosFlags & CellPosFlag::Before)
2486 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
2487 rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
2489 else if (nCellPosFlags & CellPosFlag::After)
2491 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
2492 rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
2496 else
2498 // current cell is inside the selection
2500 if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
2501 : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
2502 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
2504 if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2505 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
2507 if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2508 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
2510 if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2511 rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
2513 // apply distance to borders
2514 if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
2515 for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
2516 rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
2521 static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
2523 const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
2524 if( pSourceLine )
2526 SvxBorderLine aLine( *pSourceLine );
2527 aLine.SetColor( rColor );
2528 rNewFrame.SetLine( &aLine, nLine );
2533 static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
2535 const Color aColor( pLineColorItem->GetValue() );
2537 if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
2538 ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
2540 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
2541 ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
2543 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
2544 ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
2546 if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
2547 ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
2551 static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
2553 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2555 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2557 if (nCellPosFlags & CellPosFlag::Upper)
2559 if( rNewFrame.GetBottom() )
2560 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2562 else if (nCellPosFlags & CellPosFlag::Lower)
2564 if( rNewFrame.GetTop() )
2565 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2568 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2570 if (nCellPosFlags & CellPosFlag::Before)
2572 if( rNewFrame.GetRight() )
2573 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2575 else if (nCellPosFlags & CellPosFlag::After)
2577 if( rNewFrame.GetLeft() )
2578 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2582 else
2584 if( rNewFrame.GetBottom() )
2585 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2586 if( rNewFrame.GetTop() )
2587 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2588 if( rNewFrame.GetRight() )
2589 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2590 if( rNewFrame.GetLeft() )
2591 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2596 void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
2598 if( !mxTable.is() )
2599 return;
2601 const sal_Int32 nRowCount = mxTable->getRowCount();
2602 const sal_Int32 nColCount = mxTable->getColumnCount();
2603 if( !(nRowCount && nColCount) )
2604 return;
2606 const SvxBoxItem* pBoxItem = nullptr;
2607 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
2608 pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
2610 const SvxBoxInfoItem* pBoxInfoItem = nullptr;
2611 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
2612 pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
2614 const SvxColorItem* pLineColorItem = nullptr;
2615 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
2616 pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
2618 const SvxBorderLine* pBorderLineItem = nullptr;
2619 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
2620 pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
2622 if( pBoxInfoItem && !pBoxItem )
2624 const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
2625 pBoxItem = &gaEmptyBoxItem;
2627 else if( pBoxItem && !pBoxInfoItem )
2629 const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
2630 pBoxInfoItem = &gaEmptyBoxInfoItem;
2633 CellPos aStart, aEnd;
2634 getSelectedCells( aStart, aEnd );
2636 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
2637 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
2639 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
2641 CellPosFlag nRowFlags = CellPosFlag::NONE;
2642 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
2643 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
2644 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
2645 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
2647 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
2649 CellRef xCell( mxTable->getCell( nCol, nRow ) );
2650 if( !xCell.is() )
2651 continue;
2653 const SfxItemSet& rSet = xCell->GetItemSet();
2654 const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
2656 SvxBoxItem aNewFrame( *pOldOuter );
2658 CellPosFlag nCellPosFlags = nRowFlags;
2659 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
2660 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
2661 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
2662 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
2664 if( pBoxItem && pBoxInfoItem )
2665 ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
2667 if( pLineColorItem )
2668 ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
2670 if( pBorderLineItem )
2671 ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
2673 if (aNewFrame != *pOldOuter)
2675 SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
2676 aAttr.Put(aNewFrame);
2677 xCell->SetMergedItemSetAndBroadcast( aAttr, false );
2684 void SvxTableController::UpdateTableShape()
2686 rtl::Reference<SdrObject> pTableObj = mxTableObj.get();
2687 if( pTableObj )
2689 pTableObj->ActionChanged();
2690 pTableObj->BroadcastObjectChange();
2692 updateSelectionOverlay();
2696 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
2698 if(!checkTableObject() || !mxTable.is())
2699 return;
2701 SdrTableObj& rTableObj(*mxTableObj.get());
2702 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2703 const bool bUndo(rModel.IsUndoEnabled());
2705 if( bUndo )
2706 rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
2708 CellPos aStart, aEnd;
2709 getSelectedCells( aStart, aEnd );
2711 SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
2712 aAttr.Put(rAttr);
2714 const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2716 if( bFrame )
2718 aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2719 aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2722 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2724 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2726 CellRef xCell( mxTable->getCell( nCol, nRow ) );
2727 if( xCell.is() )
2729 if( bUndo )
2730 xCell->AddUndo();
2731 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2736 if( bFrame )
2738 ApplyBorderAttr( rAttr );
2741 UpdateTableShape();
2743 if( bUndo )
2744 rModel.EndUndo();
2747 void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
2749 if (!checkTableObject() || !mxTable.is())
2750 return;
2752 // Filter out non-shadow items from rAttr.
2753 SfxItemSetFixed<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST> aSet(*rAttr.GetPool());
2754 aSet.Put(rAttr);
2756 if (!aSet.Count())
2758 // If there are no items to be applied on the shape, then don't set anything, it would
2759 // terminate text edit without a good reason, which affects undo/redo.
2760 return;
2763 // Set shadow items on the marked shape.
2764 mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
2767 bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2769 if( mxTableObj.get().is() && hasSelectedCells() )
2771 MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
2773 if( mrView.IsTextEdit() )
2775 OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
2776 if(pTextEditOutlinerView)
2778 // FALSE= consider InvalidItems not as the default, but as "holes"
2779 rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
2783 return true;
2785 else
2787 return false;
2792 bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2794 if( mbCellSelectionMode || mrView.IsTextEdit() )
2796 SetAttrToSelectedCells( rSet, bReplaceAll );
2797 return true;
2799 return false;
2802 rtl::Reference<SdrObject> SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
2804 rtl::Reference<SdrTableObj> pRetval;
2805 sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
2807 if(nullptr == pCurrentSdrTableObj)
2809 return pRetval;
2812 if(!mxTableObj.get().is())
2814 return pRetval;
2817 // get selection and create full selection
2818 CellPos aStart, aEnd;
2819 const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
2821 getSelectedCells(aStart, aEnd);
2823 // compare to see if we have a partial selection
2824 if(aStart != aFullStart || aEnd != aFullEnd)
2826 // create full clone
2827 pRetval = SdrObject::Clone(*pCurrentSdrTableObj, rTargetModel);
2829 // limit SdrObject's TableModel to partial selection
2830 pRetval->CropTableModelToSelection(aStart, aEnd);
2833 return pRetval;
2836 bool SvxTableController::PasteObjModel( const SdrModel& rModel )
2838 if( mxTableObj.get().is() && (rModel.GetPageCount() >= 1) )
2840 const SdrPage* pPastePage = rModel.GetPage(0);
2841 if( pPastePage && pPastePage->GetObjCount() == 1 )
2843 SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
2844 if( pPasteTableObj )
2846 return PasteObject( pPasteTableObj );
2851 return false;
2855 bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
2857 if( !pPasteTableObj )
2858 return false;
2860 const rtl::Reference< TableModel >& xPasteTable( pPasteTableObj->getUnoTable() );
2861 if( !xPasteTable.is() )
2862 return false;
2864 if( !mxTable.is() )
2865 return false;
2867 sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
2868 sal_Int32 nPasteRows = xPasteTable->getRowCount();
2870 CellPos aStart, aEnd;
2871 getSelectedCells( aStart, aEnd );
2873 if( mrView.IsTextEdit() )
2874 mrView.SdrEndTextEdit(true);
2876 sal_Int32 nColumns = mxTable->getColumnCount();
2877 sal_Int32 nRows = mxTable->getRowCount();
2879 const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
2880 if( nMissing > 0 )
2882 Reference< XTableRows > xRows( mxTable->getRows() );
2883 xRows->insertByIndex( nRows, nMissing );
2884 nRows = mxTable->getRowCount();
2887 nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
2888 nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
2890 // copy cell contents
2891 for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
2893 for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
2895 CellRef xTargetCell( mxTable->getCell( nTargetCol, aStart.mnRow + nRow ) );
2896 if( xTargetCell.is() && !xTargetCell->isMerged() )
2898 CellRef xSourceCell(xPasteTable->getCell(nCol, nRow));
2899 if (xSourceCell.is())
2901 xTargetCell->AddUndo();
2902 // Prevent cell span exceeding the pasting range.
2903 if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
2904 xTargetCell->replaceContentAndFormatting(xSourceCell);
2905 else
2906 xTargetCell->cloneFrom(xSourceCell);
2908 nCol += xSourceCell->getColumnSpan() - 1;
2909 nTargetCol += xTargetCell->getColumnSpan();
2915 UpdateTableShape();
2917 return true;
2920 bool SvxTableController::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, sal_Int16 nDepth,
2921 bool bNoCharacterFormats, bool bNoParagraphFormats)
2923 if(!mbCellSelectionMode)
2925 return false;
2928 if(!checkTableObject())
2929 return false;
2931 SdrTableObj& rTableObj(*mxTableObj.get());
2932 SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2933 const bool bUndo(rModel.IsUndoEnabled());
2935 if( bUndo )
2936 rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
2938 CellPos aStart, aEnd;
2939 getSelectedCells( aStart, aEnd );
2940 const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2942 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2944 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2946 CellRef xCell( mxTable->getCell( nCol, nRow ) );
2947 if( xCell.is() )
2949 if (bUndo)
2950 xCell->AddUndo();
2951 SdrText* pText = xCell.get();
2952 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, nDepth, bNoCharacterFormats, bNoParagraphFormats );
2957 if( bFrame )
2959 ApplyBorderAttr( rFormatSet );
2962 UpdateTableShape();
2964 if( bUndo )
2965 rModel.EndUndo();
2967 return true;
2971 IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
2973 mnUpdateEvent = nullptr;
2975 if( mbCellSelectionMode )
2977 CellPos aStart( maCursorFirstPos );
2978 CellPos aEnd( maCursorLastPos );
2979 checkCell(aStart);
2980 checkCell(aEnd);
2981 if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
2983 setSelectedCells( aStart, aEnd );
2987 updateSelectionOverlay();
2988 mbHasJustMerged = false;
2991 namespace
2994 struct LinesState
2996 LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
2997 : rBoxItem(rBoxItem_)
2998 , rBoxInfoItem(rBoxInfoItem_)
2999 , bDistanceIndeterminate(false)
3001 aBorderSet.fill(false);
3002 aInnerLineSet.fill(false);
3003 aBorderIndeterminate.fill(false);
3004 aInnerLineIndeterminate.fill(false);
3005 aDistanceSet.fill(false);
3006 aDistance.fill(0);
3009 SvxBoxItem& rBoxItem;
3010 SvxBoxInfoItem& rBoxInfoItem;
3011 o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
3012 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
3013 o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
3014 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
3015 o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
3016 o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
3017 bool bDistanceIndeterminate;
3020 class BoxItemWrapper
3022 public:
3023 BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
3025 const SvxBorderLine* getLine() const;
3026 void setLine(const SvxBorderLine* pLine);
3028 private:
3029 SvxBoxItem& m_rBoxItem;
3030 SvxBoxInfoItem& m_rBoxInfoItem;
3031 const SvxBoxItemLine m_nBorderLine;
3032 const SvxBoxInfoItemLine m_nInnerLine;
3033 const bool m_bBorder;
3036 BoxItemWrapper::BoxItemWrapper(
3037 SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
3038 const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
3039 : m_rBoxItem(rBoxItem)
3040 , m_rBoxInfoItem(rBoxInfoItem)
3041 , m_nBorderLine(nBorderLine)
3042 , m_nInnerLine(nInnerLine)
3043 , m_bBorder(bBorder)
3047 const SvxBorderLine* BoxItemWrapper::getLine() const
3049 if (m_bBorder)
3050 return m_rBoxItem.GetLine(m_nBorderLine);
3051 else
3052 return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
3055 void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
3057 if (m_bBorder)
3058 m_rBoxItem.SetLine(pLine, m_nBorderLine);
3059 else
3060 m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
3063 void lcl_MergeBorderLine(
3064 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3065 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
3067 const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
3068 BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
3069 bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
3071 if (rbSet)
3073 bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
3074 if (!rbIndeterminate)
3076 const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
3077 if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
3079 aBoxItem.setLine(nullptr);
3080 rbIndeterminate = true;
3084 else
3086 aBoxItem.setLine(pLine);
3087 rbSet = true;
3091 void lcl_MergeBorderOrInnerLine(
3092 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3093 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
3095 if (bBorder)
3096 lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
3097 else
3099 const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
3100 lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
3104 void lcl_MergeDistance(
3105 LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
3107 if (rLinesState.aDistanceSet[nIndex])
3109 if (!rLinesState.bDistanceIndeterminate)
3110 rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
3112 else
3114 rLinesState.aDistance[nIndex] = nDistance;
3115 rLinesState.aDistanceSet[nIndex] = true;
3119 void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
3121 if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
3123 // current cell is outside the selection
3125 if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
3127 if (nCellPosFlags & CellPosFlag::Upper)
3128 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
3129 else if (nCellPosFlags & CellPosFlag::Lower)
3130 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
3132 else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
3134 if (nCellPosFlags & CellPosFlag::Before)
3135 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
3136 else if (nCellPosFlags & CellPosFlag::After)
3137 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
3140 // NOTE: inner distances for cells outside the selected range
3141 // are not relevant -> we ignore them.
3143 else
3145 // current cell is inside the selection
3147 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
3148 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
3149 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
3150 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
3152 lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
3153 lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
3154 lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
3155 lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
3161 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
3163 if( !mxTable.is() )
3164 return;
3166 const sal_Int32 nRowCount = mxTable->getRowCount();
3167 const sal_Int32 nColCount = mxTable->getColumnCount();
3168 if( !(nRowCount && nColCount) )
3169 return;
3171 CellPos aStart, aEnd;
3172 const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
3174 // We are adding one more row/column around the block of selected cells.
3175 // We will be checking the adjoining border of these too.
3176 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
3177 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
3179 rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
3180 LinesState aLinesState( rBoxItem, rBoxInfoItem );
3182 /* Here we go through all the selected cells (enhanced by
3183 * the adjoining row/column on each side) and determine the
3184 * lines for presentation. The algorithm is simple:
3185 * 1. if a border or inner line is set (or unset) in all
3186 * cells to the same value, it will be used.
3187 * 2. if a border or inner line is set only in some cells,
3188 * it will be set to indeterminate state (SetValid() on
3189 * rBoxInfoItem).
3191 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
3193 CellPosFlag nRowFlags = CellPosFlag::NONE;
3194 nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
3195 nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
3196 nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
3197 nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
3199 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
3201 CellRef xCell( mxTable->getCell( nCol, nRow ) );
3202 if( !xCell.is() )
3203 continue;
3205 CellPosFlag nCellPosFlags = nRowFlags;
3206 nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
3207 nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
3208 nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
3209 nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
3211 const SfxItemSet& rSet = xCell->GetItemSet();
3212 SvxBoxItem aCellBoxItem(TextDistancesToSvxBoxItem(rSet));
3213 lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
3217 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
3218 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
3219 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
3220 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
3221 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
3222 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
3223 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
3224 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
3225 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
3226 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
3227 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
3228 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
3230 if (!aLinesState.bDistanceIndeterminate)
3232 if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
3233 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
3234 if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
3235 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
3236 if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
3237 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
3238 if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
3239 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
3240 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
3244 bool SvxTableController::selectRow( sal_Int32 row )
3246 if( !mxTable.is() )
3247 return false;
3248 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3249 StartSelection( aEnd );
3250 gotoCell( aStart, true, nullptr );
3251 return true;
3254 bool SvxTableController::selectColumn( sal_Int32 column )
3256 if( !mxTable.is() )
3257 return false;
3258 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3259 StartSelection( aEnd );
3260 gotoCell( aStart, true, nullptr );
3261 return true;
3264 bool SvxTableController::deselectRow( sal_Int32 row )
3266 if( !mxTable.is() )
3267 return false;
3268 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3269 StartSelection( aEnd );
3270 gotoCell( aStart, false, nullptr );
3271 return true;
3274 bool SvxTableController::deselectColumn( sal_Int32 column )
3276 if( !mxTable.is() )
3277 return false;
3278 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3279 StartSelection( aEnd );
3280 gotoCell( aStart, false, nullptr );
3281 return true;
3284 bool SvxTableController::isRowSelected( sal_Int32 nRow )
3286 if( hasSelectedCells() )
3288 CellPos aFirstPos, aLastPos;
3289 getSelectedCells( aFirstPos, aLastPos );
3290 if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
3291 return true;
3293 return false;
3296 bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
3298 if( hasSelectedCells() )
3300 CellPos aFirstPos, aLastPos;
3301 getSelectedCells( aFirstPos, aLastPos );
3302 if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
3303 return true;
3305 return false;
3308 bool SvxTableController::isRowHeader()
3310 if(!checkTableObject())
3311 return false;
3313 SdrTableObj& rTableObj(*mxTableObj.get());
3314 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3316 return aSettings.mbUseFirstRow;
3319 bool SvxTableController::isColumnHeader()
3321 if(!checkTableObject())
3322 return false;
3324 SdrTableObj& rTableObj(*mxTableObj.get());
3325 TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3327 return aSettings.mbUseFirstColumn;
3330 bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
3332 rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
3333 if (pTableObj->GetObjIdentifier() != SdrObjKind::Table)
3334 return false;
3336 CellPos aCellPos;
3337 if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
3339 // Position is a table cell.
3340 if (mbCellSelectionMode)
3342 // We have a table selection already: adjust the point or the mark.
3343 if (bPoint)
3344 setSelectedCells(maCursorFirstPos, aCellPos);
3345 else
3346 setSelectedCells(aCellPos, maCursorLastPos);
3347 return true;
3349 else if (aCellPos != maMouseDownPos)
3351 // No selection, but rPosition is at another cell: start table selection.
3352 StartSelection(maMouseDownPos);
3353 // Update graphic selection, should be hidden now.
3354 mrView.AdjustMarkHdl();
3358 return false;
3363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */