1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <algorithm>
21 
22 #include <svx/sdr/table/tablecontroller.hxx>
23 #include <tablemodel.hxx>
24 
25 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
26 #include <com/sun/star/container/XIndexAccess.hpp>
27 
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/table/XMergeableCellRange.hpp>
30 #include <com/sun/star/table/XMergeableCell.hpp>
31 
32 #include <sal/config.h>
33 #include <sal/log.hxx>
34 
35 #include <vcl/svapp.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/window.hxx>
38 
39 #include <svl/whiter.hxx>
40 #include <svl/stritem.hxx>
41 
42 #include <sfx2/request.hxx>
43 
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 <editeng/outlobj.hxx>
49 #include <svx/svdoutl.hxx>
50 #include <svx/svdpagv.hxx>
51 #include <svx/svdetc.hxx>
52 #include <svx/selectioncontroller.hxx>
53 #include <svx/svdmodel.hxx>
54 #include <svx/sdrpaintwindow.hxx>
55 #include <svx/svxdlg.hxx>
56 #include <editeng/boxitem.hxx>
57 #include <cell.hxx>
58 #include <editeng/borderline.hxx>
59 #include <editeng/colritem.hxx>
60 #include <editeng/lineitem.hxx>
61 #include <svx/strings.hrc>
62 #include <svx/dialmgr.hxx>
63 #include <svx/svdpage.hxx>
64 #include <svx/sdmetitm.hxx>
65 #include <svx/sdtditm.hxx>
66 #include "tableundo.hxx"
67 #include "tablelayouter.hxx"
68 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
69 #include <memory>
70 #include <o3tl/enumarray.hxx>
71 #include <o3tl/enumrange.hxx>
72 #include <cppuhelper/implbase.hxx>
73 #include <comphelper/lok.hxx>
74 #include <sfx2/viewsh.hxx>
75 #include <editeng/editview.hxx>
76 #include <tools/UnitConversion.hxx>
77 #include <tools/diagnose_ex.h>
78 
79 using ::editeng::SvxBorderLine;
80 using namespace sdr::table;
81 using namespace ::com::sun::star;
82 using namespace ::com::sun::star::uno;
83 using namespace ::com::sun::star::table;
84 using namespace ::com::sun::star::beans;
85 using namespace ::com::sun::star::container;
86 using namespace ::com::sun::star::text;
87 using namespace ::com::sun::star::style;
88 
89 namespace {
90 
91 enum class CellPosFlag  // signals the relative position of a cell to a selection
92 {
93     NONE   = 0x0000, // not set or inside
94     // row
95     Before = 0x0001,
96     Left   = 0x0002,
97     Right  = 0x0004,
98     After  = 0x0008,
99     // column
100     Upper  = 0x0010,
101     Top    = 0x0020,
102     Bottom = 0x0040,
103     Lower  = 0x0080
104 };
105 
106 }
107 
108 namespace o3tl
109 { template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
110 
111 namespace sdr::table {
112 
113 namespace {
114 
115 class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
116 {
117 public:
SvxTableControllerModifyListener(SvxTableController * pController)118     explicit SvxTableControllerModifyListener( SvxTableController* pController )
119         : mpController( pController ) {}
120 
121     // XModifyListener
122     virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
123 
124     // XEventListener
125     virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
126 
127     SvxTableController* mpController;
128 };
129 
130 }
131 
132 // XModifyListener
133 
134 
modified(const css::lang::EventObject &)135 void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject&  )
136 {
137     if( mpController )
138         mpController->onTableModified();
139 }
140 
141 
142 // XEventListener
143 
144 
disposing(const css::lang::EventObject &)145 void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject&  )
146 {
147     mpController = nullptr;
148 }
149 
150 
151 
152 
CreateTableController(SdrView & rView,const SdrTableObj & rObj,const rtl::Reference<sdr::SelectionController> & xRefController)153 rtl::Reference< sdr::SelectionController > CreateTableController(
154     SdrView& rView,
155     const SdrTableObj& rObj,
156     const rtl::Reference< sdr::SelectionController >& xRefController )
157 {
158     return SvxTableController::create(rView, rObj, xRefController);
159 }
160 
161 
create(SdrView & rView,const SdrTableObj & rObj,const rtl::Reference<sdr::SelectionController> & xRefController)162 rtl::Reference< sdr::SelectionController > SvxTableController::create(
163     SdrView& rView,
164     const SdrTableObj& rObj,
165     const rtl::Reference< sdr::SelectionController >& xRefController )
166 {
167     if( xRefController.is() )
168     {
169         SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
170 
171         if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
172         {
173             return xRefController;
174         }
175     }
176 
177     return new SvxTableController(rView, rObj);
178 }
179 
180 
SvxTableController(SdrView & rView,const SdrTableObj & rObj)181 SvxTableController::SvxTableController(
182     SdrView& rView,
183     const SdrTableObj& rObj)
184 :   mbCellSelectionMode(false)
185     ,mbHasJustMerged(false)
186     ,mbLeftButtonDown(false)
187     ,mrView(rView)
188     ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
189     ,mnUpdateEvent( nullptr )
190 {
191     mxTableObj->getActiveCellPos( maCursorFirstPos );
192     maCursorLastPos = maCursorFirstPos;
193 
194     Reference< XTable > xTable( mxTableObj->getTable() );
195     if( xTable.is() )
196     {
197         mxModifyListener = new SvxTableControllerModifyListener( this );
198         xTable->addModifyListener( mxModifyListener );
199 
200         mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
201     }
202 }
203 
~SvxTableController()204 SvxTableController::~SvxTableController()
205 {
206     if( mnUpdateEvent )
207     {
208         Application::RemoveUserEvent( mnUpdateEvent );
209     }
210 
211     if( mxModifyListener.is() && mxTableObj )
212     {
213         Reference< XTable > xTable( mxTableObj->getTable() );
214         if( xTable.is() )
215         {
216             xTable->removeModifyListener( mxModifyListener );
217             mxModifyListener.clear();
218         }
219     }
220 }
221 
onKeyInput(const KeyEvent & rKEvt,vcl::Window * pWindow)222 bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
223 {
224     if(!checkTableObject())
225         return false;
226 
227     SdrTableObj& rTableObj(*mxTableObj);
228     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
229 
230     // check if we are read only
231     if( rModel.IsReadOnly())
232     {
233         switch( rKEvt.GetKeyCode().GetCode() )
234         {
235         case awt::Key::DOWN:
236         case awt::Key::UP:
237         case awt::Key::LEFT:
238         case awt::Key::RIGHT:
239         case awt::Key::TAB:
240         case awt::Key::HOME:
241         case awt::Key::END:
242         case awt::Key::NUM2:
243         case awt::Key::NUM4:
244         case awt::Key::NUM6:
245         case awt::Key::NUM8:
246         case awt::Key::ESCAPE:
247         case awt::Key::F2:
248             break;
249         default:
250             // tell the view we eat the event, no further processing needed
251             return true;
252         }
253     }
254 
255     TblAction nAction = getKeyboardAction(rKEvt);
256 
257     return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
258 }
259 
260 namespace {
261 
pixelToLogic(const Point & rPoint,vcl::Window const * pWindow)262 Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
263 {
264     if (!pWindow)
265         return rPoint;
266 
267     return pWindow->PixelToLogic(rPoint);
268 }
269 
270 }
271 
onMouseButtonDown(const MouseEvent & rMEvt,vcl::Window * pWindow)272 bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
273 {
274     if (comphelper::LibreOfficeKit::isActive() && !pWindow)
275     {
276         // Tiled rendering: get the window that has the disabled map mode.
277         if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
278         {
279             if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
280                 pWindow = pOutputDevice->GetOwnerWindow();
281         }
282     }
283 
284     if( !pWindow || !checkTableObject() )
285         return false;
286 
287     SdrViewEvent aVEvt;
288     if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
289         return false;
290 
291     TableHitKind eHit = mxTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
292 
293     mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
294 
295     if( eHit == TableHitKind::Cell )
296     {
297         StartSelection( maMouseDownPos );
298         return true;
299     }
300 
301     if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
302         return true; // right click will become context menu
303 
304     // for cell selection with the mouse remember our first hit
305     if( mbLeftButtonDown )
306     {
307         RemoveSelection();
308 
309         SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
310 
311         if( pHdl )
312         {
313             mbLeftButtonDown = false;
314         }
315         else
316         {
317             sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
318 
319             if (!pTableObj || eHit == TableHitKind::NONE)
320             {
321                 mbLeftButtonDown = false;
322             }
323         }
324     }
325 
326     if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
327     {
328         bool bEmptyOutliner = false;
329         if (Outliner* pOutliner = mrView.GetTextEditOutliner())
330         {
331             if (pOutliner->GetParagraphCount() == 1)
332             {
333                 if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
334                     bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
335             }
336         }
337         if (bEmptyOutliner)
338         {
339             // Tiled rendering: a left double-click in an empty cell: select it.
340             StartSelection(maMouseDownPos);
341             setSelectedCells(maMouseDownPos, maMouseDownPos);
342             // Update graphic selection, should be hidden now.
343             mrView.AdjustMarkHdl();
344             return true;
345         }
346     }
347 
348     return false;
349 }
350 
351 
onMouseButtonUp(const MouseEvent & rMEvt,vcl::Window *)352 bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
353 {
354     if( !checkTableObject() )
355         return false;
356 
357     mbLeftButtonDown = false;
358 
359     return rMEvt.GetClicks() == 2;
360 }
361 
362 
onMouseMove(const MouseEvent & rMEvt,vcl::Window * pWindow)363 bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
364 {
365     if( !checkTableObject() )
366         return false;
367 
368     SdrTableObj* pTableObj = mxTableObj.get();
369     CellPos aPos;
370     if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
371     {
372         if(aPos != maMouseDownPos)
373         {
374             if( mbCellSelectionMode )
375             {
376                 setSelectedCells( maMouseDownPos, aPos );
377                 return true;
378             }
379             else
380             {
381                 StartSelection( maMouseDownPos );
382             }
383         }
384         else if( mbCellSelectionMode )
385         {
386             UpdateSelection( aPos );
387             return true;
388         }
389     }
390     return false;
391 }
392 
393 
onSelectionHasChanged()394 void SvxTableController::onSelectionHasChanged()
395 {
396     bool bSelected = false;
397 
398     SdrTableObj* pTableObj = mxTableObj.get();
399     if( pTableObj && pTableObj->IsTextEditActive() )
400     {
401         pTableObj->getActiveCellPos( maCursorFirstPos );
402         maCursorLastPos = maCursorFirstPos;
403         mbCellSelectionMode = false;
404     }
405     else
406     {
407         const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
408         if( rMarkList.GetMarkCount() == 1 )
409             bSelected = mxTableObj.get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
410     }
411 
412     if( bSelected )
413     {
414         updateSelectionOverlay();
415     }
416     else
417     {
418         destroySelectionOverlay();
419     }
420 }
onSelectAll()421 void SvxTableController::onSelectAll()
422 {
423     sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
424     if ( pTableObj && !pTableObj->IsTextEditActive())
425     {
426         selectAll();
427     }
428 }
429 
430 
GetState(SfxItemSet & rSet)431 void SvxTableController::GetState( SfxItemSet& rSet )
432 {
433     if(!mxTable.is() || !mxTableObj.is())
434         return;
435 
436     SdrTableObj& rTableObj(*mxTableObj);
437     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
438     std::unique_ptr<SfxItemSet> xSet;
439     bool bVertDone(false);
440 
441     // Iterate over all requested items in the set.
442     SfxWhichIter aIter( rSet );
443     sal_uInt16 nWhich = aIter.FirstWhich();
444     while (nWhich)
445     {
446         switch (nWhich)
447         {
448             case SID_TABLE_VERT_BOTTOM:
449             case SID_TABLE_VERT_CENTER:
450             case SID_TABLE_VERT_NONE:
451                 {
452                     if(!bVertDone)
453                     {
454                         if (!xSet)
455                         {
456                             xSet.reset(new SfxItemSet(rModel.GetItemPool()));
457                             MergeAttrFromSelectedCells(*xSet, false);
458                         }
459 
460                         SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
461 
462                         if (xSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
463                             eAdj = xSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
464 
465                         rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
466                         rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
467                         rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
468                         bVertDone = true;
469                     }
470                     break;
471                 }
472             case SID_TABLE_DELETE_ROW:
473                 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
474                     rSet.DisableItem(SID_TABLE_DELETE_ROW);
475                 break;
476             case SID_TABLE_DELETE_COL:
477                 if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
478                     rSet.DisableItem(SID_TABLE_DELETE_COL);
479                 break;
480             case SID_TABLE_DELETE_TABLE:
481                 if( !mxTable.is() )
482                     rSet.DisableItem(SID_TABLE_DELETE_TABLE);
483                 break;
484             case SID_TABLE_MERGE_CELLS:
485                 if( !mxTable.is() || !hasSelectedCells() )
486                     rSet.DisableItem(SID_TABLE_MERGE_CELLS);
487                 break;
488             case SID_TABLE_SPLIT_CELLS:
489                 if( !hasSelectedCells() || !mxTable.is() )
490                     rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
491                 break;
492 
493             case SID_TABLE_OPTIMAL_ROW_HEIGHT:
494             case SID_TABLE_DISTRIBUTE_COLUMNS:
495             case SID_TABLE_DISTRIBUTE_ROWS:
496             {
497                 bool bDistributeColumns = false;
498                 bool bDistributeRows = false;
499                 if( mxTable.is() )
500                 {
501                     CellPos aStart, aEnd;
502                     getSelectedCells( aStart, aEnd );
503 
504                     bDistributeColumns = aStart.mnCol != aEnd.mnCol;
505                     bDistributeRows = aStart.mnRow != aEnd.mnRow;
506                 }
507                 if( !bDistributeColumns )
508                     rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
509                 if( !bDistributeRows )
510                 {
511                     rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
512                     rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
513                 }
514                 break;
515             }
516 
517             default:
518                 break;
519         }
520         nWhich = aIter.NextWhich();
521     }
522 }
523 
524 
onInsert(sal_uInt16 nSId,const SfxItemSet * pArgs)525 void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
526 {
527     if(!checkTableObject())
528         return;
529 
530     SdrTableObj& rTableObj(*mxTableObj);
531     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
532     bool bInsertAfter = true;
533     sal_uInt16 nCount = 0;
534 
535     if( pArgs )
536     {
537         const SfxPoolItem* pItem = nullptr;
538         pArgs->GetItemState(nSId, false, &pItem);
539         if (pItem)
540         {
541             nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
542             if(SfxItemState::SET == pArgs->GetItemState(SID_TABLE_PARAM_INSERT_AFTER, true, &pItem))
543                 bInsertAfter = static_cast<const SfxBoolItem*>(pItem)->GetValue();
544         }
545     }
546 
547     CellPos aStart, aEnd;
548     if( hasSelectedCells() )
549     {
550         getSelectedCells( aStart, aEnd );
551     }
552     else
553     {
554         if( bInsertAfter )
555         {
556             aStart.mnCol = mxTable->getColumnCount() - 1;
557             aStart.mnRow = mxTable->getRowCount() - 1;
558             aEnd = aStart;
559         }
560     }
561 
562     if( rTableObj.IsTextEditActive() )
563         mrView.SdrEndTextEdit(true);
564 
565     RemoveSelection();
566 
567     static const OUStringLiteral sSize( u"Size" );
568     const bool bUndo(rModel.IsUndoEnabled());
569 
570     switch( nSId )
571     {
572     case SID_TABLE_INSERT_COL:
573     {
574         TableModelNotifyGuard aGuard( mxTable.get() );
575 
576         if( bUndo )
577         {
578             rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
579             rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
580         }
581 
582         Reference< XTableColumns > xCols( mxTable->getColumns() );
583         const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
584         const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
585         xCols->insertByIndex( nNewStartColumn, nNewColumns );
586 
587         for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
588         {
589             // Resolves fdo#61540
590             // On Insert before, the reference column whose size is going to be
591             // used for newly created column(s) is wrong. As the new columns are
592             // inserted before the reference column, the reference column moved
593             // to the new position by no., of new columns i.e (earlier+newcolumns).
594             Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
595                 setPropertyValue( sSize,
596                     Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
597                         getPropertyValue( sSize ) );
598         }
599 
600         // Copy cell properties
601         sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
602         sal_Int32 nRowSpan = 0;
603         bool bNewSpan = false;
604 
605         for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
606         {
607             CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
608 
609             // When we insert new COLUMNs, we want to copy ROW spans.
610             if (xSourceCell.is() && nRowSpan == 0)
611             {
612                 // we are not in a span yet. Let's find out if the current cell is in a span.
613                 sal_Int32 nColSpan = sal_Int32();
614                 sal_Int32 nSpanInfoCol = sal_Int32();
615 
616                 if( xSourceCell->getRowSpan() > 1 )
617                 {
618                     // The current cell is the top-left cell in a span.
619                     // Get the span info and propagate it to the target.
620                     nRowSpan = xSourceCell->getRowSpan();
621                     nColSpan = xSourceCell->getColumnSpan();
622                     nSpanInfoCol = nPropSrcCol;
623                 }
624                 else if( xSourceCell->isMerged() )
625                 {
626                     // The current cell is a middle cell in a 2D span.
627                     // Look for the top-left cell in the span.
628                     for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
629                     {
630                         CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
631                         if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
632                         {
633                             nRowSpan = xMergeInfoCell->getRowSpan();
634                             nColSpan = xMergeInfoCell->getColumnSpan();
635                             break;
636                         }
637                     }
638                     if( nRowSpan == 1 )
639                         nRowSpan = 0;
640                 }
641 
642                 // The target columns are outside the span; Start a new span.
643                 if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
644                     bNewSpan = true;
645             }
646 
647             // Now copy the properties from the source to the targets
648             for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
649             {
650                 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
651                 if( xTargetCell.is() )
652                 {
653                     if( nRowSpan > 0 )
654                     {
655                         if( bNewSpan )
656                             xTargetCell->merge( 1, nRowSpan );
657                         else
658                             xTargetCell->setMerged();
659                     }
660                     xTargetCell->copyFormatFrom( xSourceCell );
661                 }
662             }
663 
664             if( nRowSpan > 0 )
665             {
666                 --nRowSpan;
667                 bNewSpan = false;
668             }
669         }
670 
671         if( bUndo )
672             rModel.EndUndo();
673 
674         aStart.mnCol = nNewStartColumn;
675         aStart.mnRow = 0;
676         aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
677         aEnd.mnRow = mxTable->getRowCount() - 1;
678         break;
679     }
680 
681     case SID_TABLE_INSERT_ROW:
682     {
683         TableModelNotifyGuard aGuard( mxTable.get() );
684 
685         if( bUndo )
686         {
687             rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
688             rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
689         }
690 
691         Reference< XTableRows > xRows( mxTable->getRows() );
692         const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
693         const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
694         xRows->insertByIndex( nNewRowStart, nNewRows );
695 
696         for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
697         {
698             Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
699                 setPropertyValue( sSize,
700                     Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
701                         getPropertyValue( sSize ) );
702         }
703 
704         // Copy the cell properties
705         sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
706         sal_Int32 nColSpan = 0;
707         bool bNewSpan = false;
708 
709         for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
710         {
711             CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
712 
713             if (!xSourceCell.is())
714                 continue;
715 
716             // When we insert new ROWs, we want to copy COLUMN spans.
717             if( nColSpan == 0 )
718             {
719                 // we are not in a span yet. Let's find out if the current cell is in a span.
720                 sal_Int32 nRowSpan = sal_Int32();
721                 sal_Int32 nSpanInfoRow = sal_Int32();
722 
723                 if( xSourceCell->getColumnSpan() > 1 )
724                 {
725                     // The current cell is the top-left cell in a span.
726                     // Get the span info and propagate it to the target.
727                     nColSpan = xSourceCell->getColumnSpan();
728                     nRowSpan = xSourceCell->getRowSpan();
729                     nSpanInfoRow = nPropSrcRow;
730                 }
731                 else if( xSourceCell->isMerged() )
732                 {
733                     // The current cell is a middle cell in a 2D span.
734                     // Look for the top-left cell in the span.
735                     for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
736                     {
737                         CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
738                         if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
739                         {
740                             nColSpan = xMergeInfoCell->getColumnSpan();
741                             nRowSpan = xMergeInfoCell->getRowSpan();
742                             break;
743                         }
744                     }
745                     if( nColSpan == 1 )
746                         nColSpan = 0;
747                 }
748 
749                 // Inserted rows are outside the span; Start a new span.
750                 if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
751                     bNewSpan = true;
752             }
753 
754             // Now copy the properties from the source to the targets
755             for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
756             {
757                 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
758                 if( xTargetCell.is() )
759                 {
760                     if( nColSpan > 0 )
761                     {
762                         if( bNewSpan )
763                             xTargetCell->merge( nColSpan, 1 );
764                         else
765                             xTargetCell->setMerged();
766                     }
767                     xTargetCell->copyFormatFrom( xSourceCell );
768                 }
769             }
770 
771             if( nColSpan > 0 )
772             {
773                 --nColSpan;
774                 bNewSpan = false;
775             }
776         }
777 
778         if( bUndo )
779             rModel.EndUndo();
780 
781         aStart.mnCol = 0;
782         aStart.mnRow = nNewRowStart;
783         aEnd.mnCol = mxTable->getColumnCount() - 1;
784         aEnd.mnRow = aStart.mnRow + nNewRows - 1;
785         break;
786     }
787     }
788 
789     StartSelection( aStart );
790     UpdateSelection( aEnd );
791 }
792 
793 
onDelete(sal_uInt16 nSId)794 void SvxTableController::onDelete( sal_uInt16 nSId )
795 {
796     sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
797     if( !pTableObj || !mxTable.is() )
798         return;
799 
800     if( nSId == SID_TABLE_DELETE_TABLE )
801     {
802         if( pTableObj->IsTextEditActive() )
803             mrView.SdrEndTextEdit(true);
804 
805         mrView.DeleteMarkedObj();
806     }
807     else if( hasSelectedCells() )
808     {
809         CellPos aStart, aEnd;
810         getSelectedCells( aStart, aEnd );
811 
812         if( pTableObj->IsTextEditActive() )
813             mrView.SdrEndTextEdit(true);
814 
815         RemoveSelection();
816 
817         bool bDeleteTable = false;
818         switch( nSId )
819         {
820         case SID_TABLE_DELETE_COL:
821         {
822             const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
823             if( nRemovedColumns == mxTable->getColumnCount() )
824             {
825                 bDeleteTable = true;
826             }
827             else
828             {
829                 Reference< XTableColumns > xCols( mxTable->getColumns() );
830                 xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
831             }
832             break;
833         }
834 
835         case SID_TABLE_DELETE_ROW:
836         {
837             const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
838             if( nRemovedRows == mxTable->getRowCount() )
839             {
840                 bDeleteTable = true;
841             }
842             else
843             {
844                 Reference< XTableRows > xRows( mxTable->getRows() );
845                 xRows->removeByIndex( aStart.mnRow, nRemovedRows );
846             }
847             break;
848         }
849         }
850 
851         if( bDeleteTable )
852             mrView.DeleteMarkedObj();
853         else
854             UpdateTableShape();
855     }
856 }
857 
858 
onSelect(sal_uInt16 nSId)859 void SvxTableController::onSelect( sal_uInt16 nSId )
860 {
861     if( !mxTable.is() )
862         return;
863 
864     const sal_Int32 nRowCount = mxTable->getRowCount();
865     const sal_Int32 nColCount = mxTable->getColumnCount();
866     if( !(nRowCount && nColCount) )
867         return;
868 
869     CellPos aStart, aEnd;
870     getSelectedCells( aStart, aEnd );
871 
872     switch( nSId )
873     {
874     case SID_TABLE_SELECT_ALL:
875         aEnd.mnCol = 0; aEnd.mnRow = 0;
876         aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
877         break;
878     case SID_TABLE_SELECT_COL:
879         aEnd.mnRow = nRowCount - 1;
880         aStart.mnRow = 0;
881         break;
882     case SID_TABLE_SELECT_ROW:
883         aEnd.mnCol = nColCount - 1;
884         aStart.mnCol = 0;
885         break;
886     }
887 
888     StartSelection( aEnd );
889     gotoCell( aStart, true, nullptr );
890 }
891 
892 namespace
893 {
mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet & rAttrSet)894     SvxBoxItem mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet& rAttrSet)
895     {
896         // merge drawing layer text distance items into SvxBoxItem used by the dialog
897         SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
898         aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
899         aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
900         aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
901         aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
902         return aBoxItem;
903     }
904 }
905 
onFormatTable(const SfxRequest & rReq)906 void SvxTableController::onFormatTable(const SfxRequest& rReq)
907 {
908     if(!mxTableObj.is())
909         return;
910 
911     SdrTableObj& rTableObj(*mxTableObj);
912     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
913     const SfxItemSet* pArgs = rReq.GetArgs();
914 
915     if(pArgs)
916         return;
917 
918     SfxItemSet aNewAttr(rModel.GetItemPool());
919 
920     // merge drawing layer text distance items into SvxBoxItem used by the dialog
921     SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
922 
923     SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
924 
925     MergeAttrFromSelectedCells(aNewAttr, false);
926     FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
927     aNewAttr.Put( aBoxItem );
928     aNewAttr.Put( aBoxInfoItem );
929 
930     // Fill in shadow properties.
931     const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
932     for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
933     {
934         if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
935         {
936             continue;
937         }
938 
939         aNewAttr.Put(rTableItemSet.Get(nWhich));
940     }
941 
942     SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
943     VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
944         rReq.GetFrameWeld(),
945         &aNewAttr,
946         rModel) );
947 
948     // Even Cancel Button is returning positive(101) value,
949     xDlg->StartExecuteAsync([xDlg, this, aBoxItem, aBoxInfoItem](int nResult){
950         if (nResult == RET_OK)
951         {
952             SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
953 
954             //Only properties that were unchanged by the dialog appear in this
955             //itemset.  We had constructed these two properties from other
956             //ones, so if they were not changed, then forcible set them back to
957             //their originals in the new result set so we can decompose that
958             //unchanged state back to their input properties
959             if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
960             {
961                 aNewSet.Put(aBoxItem);
962             }
963             if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
964             {
965                 aNewSet.Put(aBoxInfoItem);
966             }
967 
968             SvxBoxItem aNewBoxItem( aNewSet.Get( SDRATTR_TABLE_BORDER ) );
969 
970             if( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) != aBoxItem.GetDistance( SvxBoxItemLine::LEFT ) )
971                 aNewSet.Put(makeSdrTextLeftDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) ) );
972 
973             if( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) != aBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) )
974                 aNewSet.Put(makeSdrTextRightDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) ) );
975 
976             if( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) != aBoxItem.GetDistance( SvxBoxItemLine::TOP ) )
977                 aNewSet.Put(makeSdrTextUpperDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) ) );
978 
979             if( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) != aBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
980                 aNewSet.Put(makeSdrTextLowerDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) ) );
981 
982             if (checkTableObject() && mxTable.is())
983             {
984                 // Create a single undo action when applying the result of the dialog.
985                 SdrTableObj& rTableObject(*mxTableObj);
986                 SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
987                 bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
988                 if (bUndo)
989                 {
990                     rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
991                 }
992 
993                 this->SetAttrToSelectedCells(aNewSet, false);
994 
995                 this->SetAttrToSelectedShape(aNewSet);
996 
997                 if (bUndo)
998                 {
999                     rSdrModel.EndUndo();
1000                 }
1001             }
1002         }
1003 
1004         xDlg->disposeOnce();
1005     });
1006 }
1007 
Execute(SfxRequest & rReq)1008 void SvxTableController::Execute( SfxRequest& rReq )
1009 {
1010     const sal_uInt16 nSId = rReq.GetSlot();
1011     switch( nSId )
1012     {
1013     case SID_TABLE_INSERT_ROW:
1014     case SID_TABLE_INSERT_COL:
1015         onInsert( nSId, rReq.GetArgs() );
1016         break;
1017     case SID_TABLE_DELETE_ROW:
1018     case SID_TABLE_DELETE_COL:
1019     case SID_TABLE_DELETE_TABLE:
1020         onDelete( nSId );
1021         break;
1022     case SID_TABLE_SELECT_ALL:
1023     case SID_TABLE_SELECT_COL:
1024     case SID_TABLE_SELECT_ROW:
1025         onSelect( nSId );
1026         break;
1027     case SID_FORMAT_TABLE_DLG:
1028         onFormatTable( rReq );
1029         break;
1030 
1031     case SID_FRAME_LINESTYLE:
1032     case SID_FRAME_LINECOLOR:
1033     case SID_ATTR_BORDER:
1034         {
1035             const SfxItemSet* pArgs = rReq.GetArgs();
1036             if( pArgs )
1037                 ApplyBorderAttr( *pArgs );
1038         }
1039         break;
1040 
1041     case SID_ATTR_FILL_STYLE:
1042         {
1043             const SfxItemSet* pArgs = rReq.GetArgs();
1044             if( pArgs )
1045                 SetAttributes( *pArgs, false );
1046         }
1047         break;
1048 
1049     case SID_TABLE_MERGE_CELLS:
1050         MergeMarkedCells();
1051         break;
1052 
1053     case SID_TABLE_SPLIT_CELLS:
1054         SplitMarkedCells(rReq);
1055         break;
1056 
1057     case SID_TABLE_MINIMAL_COLUMN_WIDTH:
1058         DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
1059         break;
1060 
1061     case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
1062         DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
1063         break;
1064 
1065     case SID_TABLE_DISTRIBUTE_COLUMNS:
1066         DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
1067         break;
1068 
1069     case SID_TABLE_MINIMAL_ROW_HEIGHT:
1070         DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
1071         break;
1072 
1073     case SID_TABLE_OPTIMAL_ROW_HEIGHT:
1074         DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
1075         break;
1076 
1077     case SID_TABLE_DISTRIBUTE_ROWS:
1078         DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
1079         break;
1080 
1081     case SID_TABLE_VERT_BOTTOM:
1082     case SID_TABLE_VERT_CENTER:
1083     case SID_TABLE_VERT_NONE:
1084         SetVertical( nSId );
1085         break;
1086 
1087     case SID_AUTOFORMAT:
1088     case SID_TABLE_SORT_DIALOG:
1089     case SID_TABLE_AUTOSUM:
1090     default:
1091         break;
1092 
1093     case SID_TABLE_STYLE:
1094         SetTableStyle( rReq.GetArgs() );
1095         break;
1096 
1097     case SID_TABLE_STYLE_SETTINGS:
1098         SetTableStyleSettings( rReq.GetArgs() );
1099         break;
1100     case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
1101         changeTableEdge(rReq);
1102         break;
1103     }
1104 }
1105 
SetTableStyle(const SfxItemSet * pArgs)1106 void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
1107 {
1108     if(!checkTableObject())
1109         return;
1110 
1111     SdrTableObj& rTableObj(*mxTableObj);
1112     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1113 
1114     if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
1115         return;
1116 
1117     const SfxStringItem* pArg = dynamic_cast< const SfxStringItem* >( &pArgs->Get( SID_TABLE_STYLE ) );
1118     if( !(pArg && mxTable.is()) )
1119         return;
1120 
1121     try
1122     {
1123         Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
1124         Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
1125         Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
1126 
1127         if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
1128         {
1129             // found table style with the same name
1130             Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
1131 
1132             const bool bUndo = rModel.IsUndoEnabled();
1133 
1134             if( bUndo )
1135             {
1136                 rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
1137                 rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1138             }
1139 
1140             rTableObj.setTableStyle( xNewTableStyle );
1141 
1142             const sal_Int32 nRowCount = mxTable->getRowCount();
1143             const sal_Int32 nColCount = mxTable->getColumnCount();
1144             for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1145             {
1146                 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
1147                 {
1148                     CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1149                     if( xCell.is() )
1150                     {
1151                         SfxItemSet aSet( xCell->GetItemSet() );
1152                         bool bChanges = false;
1153                         SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
1154                         SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
1155                         if (pStyleSheet)
1156                         {
1157                             const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
1158 
1159                             for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
1160                             {
1161                                 if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
1162                                 {
1163                                     aSet.ClearItem( nWhich );
1164                                     bChanges = true;
1165                                 }
1166                             }
1167                         }
1168 
1169                         if( bChanges )
1170                         {
1171                             if( bUndo )
1172                                 xCell->AddUndo();
1173 
1174                             xCell->SetMergedItemSetAndBroadcast( aSet, true );
1175                         }
1176                     }
1177                 }
1178                 catch( Exception& )
1179                 {
1180                     TOOLS_WARN_EXCEPTION("svx.table", "");
1181                 }
1182             }
1183 
1184             if( bUndo )
1185                 rModel.EndUndo();
1186         }
1187     }
1188     catch( Exception& )
1189     {
1190         TOOLS_WARN_EXCEPTION("svx.table", "");
1191     }
1192 }
1193 
SetTableStyleSettings(const SfxItemSet * pArgs)1194 void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
1195 {
1196     if(!checkTableObject())
1197         return;
1198 
1199     SdrTableObj& rTableObj(*mxTableObj);
1200     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1201 
1202     TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
1203     const SfxPoolItem *pPoolItem=nullptr;
1204 
1205     if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTROWSTYLE, false,&pPoolItem) )
1206         aSettings.mbUseFirstRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1207 
1208     if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTROWSTYLE, false,&pPoolItem) )
1209         aSettings.mbUseLastRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1210 
1211     if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGROWSTYLE, false,&pPoolItem) )
1212         aSettings.mbUseRowBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1213 
1214     if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTCOLUMNSTYLE, false,&pPoolItem) )
1215         aSettings.mbUseFirstColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1216 
1217     if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTCOLUMNSTYLE, false,&pPoolItem) )
1218         aSettings.mbUseLastColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1219 
1220     if( SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGCOLUMNSTYLE, false,&pPoolItem) )
1221         aSettings.mbUseColumnBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1222 
1223     if( aSettings == rTableObj.getTableStyleSettings() )
1224         return;
1225 
1226     const bool bUndo(rModel.IsUndoEnabled());
1227 
1228     if( bUndo )
1229     {
1230         rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
1231         rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
1232     }
1233 
1234     rTableObj.setTableStyleSettings( aSettings );
1235 
1236     if( bUndo )
1237         rModel.EndUndo();
1238 }
1239 
SetVertical(sal_uInt16 nSId)1240 void SvxTableController::SetVertical( sal_uInt16 nSId )
1241 {
1242     if(!checkTableObject())
1243         return;
1244 
1245     SdrTableObj& rTableObj(*mxTableObj);
1246     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1247 
1248     TableModelNotifyGuard aGuard( mxTable.get() );
1249     const bool bUndo(rModel.IsUndoEnabled());
1250 
1251     if (bUndo)
1252     {
1253         rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
1254         rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
1255     }
1256 
1257     CellPos aStart, aEnd;
1258     getSelectedCells( aStart, aEnd );
1259 
1260     SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
1261 
1262     switch( nSId )
1263     {
1264         case SID_TABLE_VERT_BOTTOM:
1265             eAdj = SDRTEXTVERTADJUST_BOTTOM;
1266             break;
1267         case SID_TABLE_VERT_CENTER:
1268             eAdj = SDRTEXTVERTADJUST_CENTER;
1269             break;
1270         //case SID_TABLE_VERT_NONE:
1271         default:
1272             break;
1273     }
1274 
1275     SdrTextVertAdjustItem aItem( eAdj );
1276 
1277     for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1278     {
1279         for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1280         {
1281             CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1282             if( xCell.is() )
1283             {
1284                 if (bUndo)
1285                     xCell->AddUndo();
1286                 SfxItemSet aSet(xCell->GetItemSet());
1287                 aSet.Put(aItem);
1288                 xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
1289             }
1290         }
1291     }
1292 
1293     UpdateTableShape();
1294 
1295     if (bUndo)
1296         rModel.EndUndo();
1297 }
1298 
MergeMarkedCells()1299 void SvxTableController::MergeMarkedCells()
1300 {
1301     CellPos aStart, aEnd;
1302     getSelectedCells( aStart, aEnd );
1303     SdrTableObj* pTableObj = mxTableObj.get();
1304     if( pTableObj )
1305     {
1306         if( pTableObj->IsTextEditActive() )
1307             mrView.SdrEndTextEdit(true);
1308 
1309         TableModelNotifyGuard aGuard( mxTable.get() );
1310         MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
1311     }
1312 }
1313 
SplitMarkedCells(const SfxRequest & rReq)1314 void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
1315 {
1316     if(!checkTableObject() || !mxTable.is())
1317         return;
1318 
1319     SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1320     VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
1321 
1322     xDlg->StartExecuteAsync([xDlg, this](int) {
1323         const sal_Int32 nCount = xDlg->GetCount() - 1;
1324 
1325         if( nCount < 1 )
1326             return;
1327 
1328         CellPos aStart, aEnd;
1329         getSelectedCells( aStart, aEnd );
1330         Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
1331         const sal_Int32 nRowCount = mxTable->getRowCount();
1332         const sal_Int32 nColCount = mxTable->getColumnCount();
1333         SdrTableObj& rTableObj(*mxTableObj);
1334 
1335         if( rTableObj.IsTextEditActive() )
1336             mrView.SdrEndTextEdit(true);
1337 
1338         TableModelNotifyGuard aGuard( mxTable.get() );
1339         SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1340         const bool bUndo(rModel.IsUndoEnabled());
1341 
1342         if( bUndo )
1343         {
1344             rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
1345             rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1346         }
1347 
1348         if( xDlg->IsHorizontal() )
1349         {
1350             xRange->split( 0, nCount );
1351         }
1352         else
1353         {
1354             xRange->split( nCount, 0 );
1355         }
1356 
1357         if( bUndo )
1358             rModel.EndUndo();
1359 
1360         aEnd.mnRow += mxTable->getRowCount() - nRowCount;
1361         aEnd.mnCol += mxTable->getColumnCount() - nColCount;
1362 
1363         setSelectedCells( aStart, aEnd );
1364 
1365         xDlg->disposeOnce();
1366     });
1367 }
1368 
DistributeColumns(const bool bOptimize,const bool bMinimize)1369 void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
1370 {
1371     if(!checkTableObject())
1372         return;
1373 
1374     SdrTableObj& rTableObj(*mxTableObj);
1375     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1376     const bool bUndo(rModel.IsUndoEnabled());
1377 
1378     if( bUndo )
1379     {
1380         rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
1381         rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1382     }
1383 
1384     CellPos aStart, aEnd;
1385     getSelectedCells( aStart, aEnd );
1386     rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
1387 
1388     if( bUndo )
1389         rModel.EndUndo();
1390 }
1391 
DistributeRows(const bool bOptimize,const bool bMinimize)1392 void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
1393 {
1394     if(!checkTableObject())
1395         return;
1396 
1397     SdrTableObj& rTableObj(*mxTableObj);
1398     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1399     const bool bUndo(rModel.IsUndoEnabled());
1400 
1401     if( bUndo )
1402     {
1403         rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
1404         rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1405     }
1406 
1407     CellPos aStart, aEnd;
1408     getSelectedCells( aStart, aEnd );
1409     rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
1410 
1411     if( bUndo )
1412         rModel.EndUndo();
1413 }
1414 
HasMarked() const1415 bool SvxTableController::HasMarked() const
1416 {
1417     return mbCellSelectionMode && mxTable.is();
1418 }
1419 
DeleteMarked()1420 bool SvxTableController::DeleteMarked()
1421 {
1422     if(!checkTableObject() || !HasMarked())
1423         return false;
1424 
1425     SdrTableObj& rTableObj(*mxTableObj);
1426     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1427     const bool bUndo(rModel.IsUndoEnabled());
1428     bool bDeleteTable = false;
1429 
1430     if (bUndo)
1431         rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
1432 
1433     CellPos aStart, aEnd;
1434     getSelectedCells( aStart, aEnd );
1435     const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
1436     const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
1437     if( nRemovedColumns == mxTable->getColumnCount() && nRemovedRows == mxTable->getRowCount())
1438     {
1439         bDeleteTable = true;
1440     }
1441     else
1442     {
1443         for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1444         {
1445             for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1446             {
1447                 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1448                 if (xCell.is() && xCell->hasText())
1449                 {
1450                     if (bUndo)
1451                         xCell->AddUndo();
1452                     xCell->SetOutlinerParaObject(nullptr);
1453                 }
1454             }
1455         }
1456     }
1457 
1458     if (bDeleteTable)
1459         mrView.DeleteMarkedObj();
1460 
1461     if (bUndo)
1462         rModel.EndUndo();
1463 
1464     if (!bDeleteTable)
1465         UpdateTableShape();
1466     return true;
1467 }
1468 
GetStyleSheet(SfxStyleSheet * & rpStyleSheet) const1469 bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
1470 {
1471     if( hasSelectedCells() )
1472     {
1473         rpStyleSheet = nullptr;
1474 
1475         if( mxTable.is() )
1476         {
1477             SfxStyleSheet* pRet=nullptr;
1478             bool b1st=true;
1479 
1480             CellPos aStart, aEnd;
1481             const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
1482 
1483             for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1484             {
1485                 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1486                 {
1487                     CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1488                     if( xCell.is() )
1489                     {
1490                         SfxStyleSheet* pSS=xCell->GetStyleSheet();
1491                         if(b1st)
1492                         {
1493                             pRet=pSS;
1494                         }
1495                         else if(pRet != pSS)
1496                         {
1497                             return true;
1498                         }
1499                         b1st=false;
1500                     }
1501                 }
1502             }
1503             rpStyleSheet = pRet;
1504             return true;
1505         }
1506     }
1507     return false;
1508 }
1509 
SetStyleSheet(SfxStyleSheet * pStyleSheet,bool bDontRemoveHardAttr)1510 bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
1511 {
1512     if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
1513     {
1514         if( mxTable.is() )
1515         {
1516             CellPos aStart, aEnd;
1517             getSelectedCells( aStart, aEnd );
1518 
1519             for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1520             {
1521                 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1522                 {
1523                     CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1524                     if( xCell.is() )
1525                         xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
1526                 }
1527             }
1528 
1529             UpdateTableShape();
1530             return true;
1531         }
1532     }
1533     return false;
1534 }
1535 
changeTableEdge(const SfxRequest & rReq)1536 void SvxTableController::changeTableEdge(const SfxRequest& rReq)
1537 {
1538     if (!checkTableObject())
1539         return;
1540 
1541     const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
1542     const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
1543     const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
1544 
1545     if (!(pType && pIndex && pOffset))
1546         return;
1547 
1548     const OUString sType = pType->GetValue();
1549     const sal_uInt16 nIndex = pIndex->GetValue();
1550     const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
1551 
1552     SdrTableObj& rTableObj(*mxTableObj);
1553 
1554     sal_Int32 nEdgeIndex = -1;
1555     bool bHorizontal = sType.startsWith("row");
1556 
1557     if (sType == "column-left" || sType == "row-left")
1558     {
1559         nEdgeIndex = 0;
1560     }
1561     else if (sType == "column-right")
1562     {
1563         // Number of edges = number of columns + 1
1564         nEdgeIndex = rTableObj.getColumnCount();
1565     }
1566     else if (sType == "row-right")
1567     {
1568         // Number of edges = number of rows + 1
1569         nEdgeIndex = rTableObj.getRowCount();
1570     }
1571     else if (sType == "column-middle" || sType == "row-middle")
1572     {
1573         nEdgeIndex = nIndex + 1;
1574     }
1575 
1576     if (nEdgeIndex < 0)
1577         return;
1578 
1579     TableModelNotifyGuard aGuard(mxTable.get());
1580     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1581     const bool bUndo(rModel.IsUndoEnabled());
1582     if (bUndo)
1583     {
1584         auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
1585         rModel.BegUndo(pUndoObject->GetComment());
1586 
1587         auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
1588         if (pGeoUndo)
1589             pGeoUndo->SetSkipChangeLayout(true);
1590 
1591         rModel.AddUndo(std::move(pUndoObject));
1592     }
1593     tools::Rectangle aBoundRect;
1594     if (rTableObj.GetUserCall())
1595         aBoundRect = rTableObj.GetLastBoundRect();
1596     rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
1597     rTableObj.SetChanged();
1598     rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
1599     if (bUndo)
1600         rModel.EndUndo();
1601 }
1602 
1603 // internals
1604 
1605 
checkTableObject()1606 bool SvxTableController::checkTableObject()
1607 {
1608     return mxTableObj.is();
1609 }
1610 
1611 
getKeyboardAction(const KeyEvent & rKEvt)1612 SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
1613 {
1614     const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
1615     const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
1616     const bool bTextEdit = mrView.IsTextEdit();
1617 
1618     TblAction nAction = TblAction::HandledByView;
1619 
1620     sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
1621     if( !pTableObj )
1622         return nAction;
1623 
1624     // handle special keys
1625     const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
1626     switch( nCode )
1627     {
1628     case awt::Key::ESCAPE:          // handle escape
1629     {
1630         if( bTextEdit )
1631         {
1632             // escape during text edit ends text edit
1633             nAction = TblAction::StopTextEdit;
1634         }
1635         if( mbCellSelectionMode )
1636         {
1637             // escape with selected cells removes selection
1638             nAction = TblAction::RemoveSelection;
1639         }
1640         break;
1641     }
1642     case awt::Key::RETURN:      // handle return
1643     {
1644         if( !bMod1 && !bMod2 && !bTextEdit )
1645         {
1646             // when not already editing, return starts text edit
1647             setSelectionStart( SdrTableObj::getFirstCell() );
1648             nAction = TblAction::EditCell;
1649         }
1650         break;
1651     }
1652     case awt::Key::F2:          // f2 toggles text edit
1653     {
1654         if( bMod1 || bMod2 )    // f2 with modifiers is handled by the view
1655         {
1656         }
1657         else if( bTextEdit )
1658         {
1659             // f2 during text edit stops text edit
1660             nAction = TblAction::StopTextEdit;
1661         }
1662         else if( mbCellSelectionMode )
1663         {
1664             // f2 with selected cells removes selection
1665             nAction = TblAction::RemoveSelection;
1666         }
1667         else
1668         {
1669             // f2 with no selection and no text edit starts text edit
1670             setSelectionStart( SdrTableObj::getFirstCell() );
1671             nAction = TblAction::EditCell;
1672         }
1673         break;
1674     }
1675     case awt::Key::HOME:
1676     case awt::Key::NUM7:
1677     {
1678         if( (bMod1 ||  bMod2) && (bTextEdit || mbCellSelectionMode) )
1679         {
1680             if( bMod1 && !bMod2 )
1681             {
1682                 // ctrl + home jumps to first cell
1683                 nAction = TblAction::GotoFirstCell;
1684             }
1685             else if( !bMod1 && bMod2 )
1686             {
1687                 // alt + home jumps to first column
1688                 nAction = TblAction::GotoFirstColumn;
1689             }
1690         }
1691         break;
1692     }
1693     case awt::Key::END:
1694     case awt::Key::NUM1:
1695     {
1696         if( (bMod1 ||  bMod2) && (bTextEdit || mbCellSelectionMode) )
1697         {
1698             if( bMod1 && !bMod2 )
1699             {
1700                 // ctrl + end jumps to last cell
1701                 nAction = TblAction::GotoLastCell;
1702             }
1703             else if( !bMod1 && bMod2 )
1704             {
1705                 // alt + home jumps to last column
1706                 nAction = TblAction::GotoLastColumn;
1707             }
1708         }
1709         break;
1710     }
1711 
1712     case awt::Key::TAB:
1713     {
1714         if( bTextEdit || mbCellSelectionMode )
1715             nAction = TblAction::Tab;
1716         break;
1717     }
1718 
1719     case awt::Key::UP:
1720     case awt::Key::NUM8:
1721     case awt::Key::DOWN:
1722     case awt::Key::NUM2:
1723     case awt::Key::LEFT:
1724     case awt::Key::NUM4:
1725     case awt::Key::RIGHT:
1726     case awt::Key::NUM6:
1727     {
1728 
1729         if( !bMod1 && bMod2 )
1730         {
1731             if(bTextEdit || mbCellSelectionMode)
1732             {
1733                 if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
1734                 {
1735                     nAction = TblAction::GotoLeftCell;
1736                     break;
1737                 }
1738                 else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
1739                 {
1740                     nAction = TblAction::GotoRightCell;
1741                     break;
1742                 }
1743             }
1744         }
1745 
1746         bool bTextMove = false;
1747         OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
1748         if( pOLV )
1749         {
1750             RemoveSelection();
1751             // during text edit, check if we navigate out of the cell
1752             ESelection aOldSelection = pOLV->GetSelection();
1753             pOLV->PostKeyEvent(rKEvt);
1754             bTextMove = aOldSelection == pOLV->GetSelection();
1755             if( !bTextMove )
1756             {
1757                 nAction = TblAction::NONE;
1758             }
1759         }
1760 
1761         if( mbCellSelectionMode || bTextMove )
1762         {
1763             // no text edit, navigate in cells if selection active
1764             switch( nCode )
1765             {
1766             case awt::Key::LEFT:
1767             case awt::Key::NUM4:
1768                 nAction = TblAction::GotoLeftCell;
1769                 break;
1770             case awt::Key::RIGHT:
1771             case awt::Key::NUM6:
1772                 nAction = TblAction::GotoRightCell;
1773                 break;
1774             case awt::Key::DOWN:
1775             case awt::Key::NUM2:
1776                 nAction = TblAction::GotoDownCell;
1777                 break;
1778             case awt::Key::UP:
1779             case awt::Key::NUM8:
1780                 nAction = TblAction::GotoUpCell;
1781                 break;
1782             }
1783         }
1784         break;
1785     }
1786     case awt::Key::PAGEUP:
1787         if( bMod2 )
1788             nAction = TblAction::GotoFirstRow;
1789         break;
1790 
1791     case awt::Key::PAGEDOWN:
1792         if( bMod2 )
1793             nAction = TblAction::GotoLastRow;
1794         break;
1795     }
1796     return nAction;
1797 }
1798 
executeAction(TblAction nAction,bool bSelect,vcl::Window * pWindow)1799 bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
1800 {
1801     sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
1802     if( !pTableObj )
1803         return false;
1804 
1805     switch( nAction )
1806     {
1807     case TblAction::GotoFirstCell:
1808     {
1809         gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
1810         break;
1811     }
1812 
1813     case TblAction::GotoLeftCell:
1814     {
1815         gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
1816         break;
1817     }
1818 
1819     case TblAction::GotoRightCell:
1820     {
1821         gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
1822         break;
1823     }
1824 
1825     case TblAction::GotoLastCell:
1826     {
1827         gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
1828         break;
1829     }
1830 
1831     case TblAction::GotoFirstColumn:
1832     {
1833         CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
1834         gotoCell( aPos, bSelect, pWindow, nAction );
1835         break;
1836     }
1837 
1838     case TblAction::GotoLastColumn:
1839     {
1840         CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
1841         gotoCell( aPos, bSelect, pWindow, nAction );
1842         break;
1843     }
1844 
1845     case TblAction::GotoFirstRow:
1846     {
1847         CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
1848         gotoCell( aPos, bSelect, pWindow, nAction );
1849         break;
1850     }
1851 
1852     case TblAction::GotoUpCell:
1853     {
1854         gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1855         break;
1856     }
1857 
1858     case TblAction::GotoDownCell:
1859     {
1860         gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1861         break;
1862     }
1863 
1864     case TblAction::GotoLastRow:
1865     {
1866         CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
1867         gotoCell( aPos, bSelect, pWindow, nAction );
1868         break;
1869     }
1870 
1871     case TblAction::EditCell:
1872         EditCell( getSelectionStart(), pWindow, nAction );
1873         break;
1874 
1875     case TblAction::StopTextEdit:
1876         StopTextEdit();
1877         break;
1878 
1879     case TblAction::RemoveSelection:
1880         RemoveSelection();
1881         break;
1882 
1883     case TblAction::Tab:
1884     {
1885         if( bSelect )
1886             gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
1887         else
1888         {
1889             CellPos aSelectionEnd( getSelectionEnd() );
1890             CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
1891             if( aSelectionEnd == aNextCell )
1892             {
1893                 onInsert( SID_TABLE_INSERT_ROW );
1894                 aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
1895             }
1896             gotoCell( aNextCell, false, pWindow, nAction );
1897         }
1898         break;
1899     }
1900     default:
1901         break;
1902     }
1903 
1904     return nAction != TblAction::HandledByView;
1905 }
1906 
1907 
gotoCell(const CellPos & rPos,bool bSelect,vcl::Window * pWindow,TblAction nAction)1908 void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
1909 {
1910     if( mxTableObj.is() && mxTableObj->IsTextEditActive() )
1911         mrView.SdrEndTextEdit(true);
1912 
1913     if( bSelect )
1914     {
1915         maCursorLastPos = rPos;
1916         if( mxTableObj.is() )
1917             mxTableObj->setActiveCell( rPos );
1918 
1919         if( !mbCellSelectionMode )
1920         {
1921             setSelectedCells( maCursorFirstPos, rPos );
1922         }
1923         else
1924         {
1925             UpdateSelection( rPos );
1926         }
1927     }
1928     else
1929     {
1930         RemoveSelection();
1931         EditCell( rPos, pWindow, nAction );
1932     }
1933 }
1934 
1935 
getSelectionStart()1936 const CellPos& SvxTableController::getSelectionStart()
1937 {
1938     checkCell( maCursorFirstPos );
1939     return maCursorFirstPos;
1940 }
1941 
1942 
setSelectionStart(const CellPos & rPos)1943 void SvxTableController::setSelectionStart( const CellPos& rPos )
1944 {
1945     maCursorFirstPos = rPos;
1946 }
1947 
1948 
getSelectionEnd()1949 const CellPos& SvxTableController::getSelectionEnd()
1950 {
1951     checkCell( maCursorLastPos );
1952     return maCursorLastPos;
1953 }
1954 
1955 
MergeRange(sal_Int32 nFirstCol,sal_Int32 nFirstRow,sal_Int32 nLastCol,sal_Int32 nLastRow)1956 void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
1957 {
1958     if(!checkTableObject() || !mxTable.is())
1959         return;
1960 
1961     try
1962     {
1963         Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
1964 
1965         if( xRange->isMergeable() )
1966         {
1967             SdrTableObj& rTableObj(*mxTableObj);
1968             SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
1969             const bool bUndo(rModel.IsUndoEnabled());
1970 
1971             if( bUndo )
1972             {
1973                 rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
1974                 rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
1975             }
1976 
1977             xRange->merge();
1978             mbHasJustMerged = true;
1979             setSelectedCells( maCursorFirstPos, maCursorFirstPos );
1980 
1981             if( bUndo )
1982                 rModel.EndUndo();
1983         }
1984     }
1985     catch( Exception& )
1986     {
1987         TOOLS_WARN_EXCEPTION( "svx.table", "" );
1988     }
1989 }
1990 
1991 
checkCell(CellPos & rPos)1992 void SvxTableController::checkCell( CellPos& rPos )
1993 {
1994     if( !mxTable.is() )
1995         return;
1996 
1997     try
1998     {
1999         if( rPos.mnCol >= mxTable->getColumnCount() )
2000             rPos.mnCol = mxTable->getColumnCount()-1;
2001 
2002         if( rPos.mnRow >= mxTable->getRowCount() )
2003             rPos.mnRow = mxTable->getRowCount()-1;
2004     }
2005     catch( Exception& )
2006     {
2007         TOOLS_WARN_EXCEPTION("svx.table", "");
2008     }
2009 }
2010 
2011 
findMergeOrigin(CellPos & rPos)2012 void SvxTableController::findMergeOrigin( CellPos& rPos )
2013 {
2014     if( !mxTable.is() )
2015         return;
2016 
2017     try
2018     {
2019         Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
2020         if( xCell->isMerged() )
2021         {
2022             ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
2023         }
2024     }
2025     catch( Exception& )
2026     {
2027         TOOLS_WARN_EXCEPTION("svx.table", "");
2028     }
2029 }
2030 
2031 
EditCell(const CellPos & rPos,vcl::Window * pWindow,TblAction nAction)2032 void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
2033 {
2034     SdrPageView* pPV(mrView.GetSdrPageView());
2035 
2036     if(nullptr == pPV || !checkTableObject())
2037         return;
2038 
2039     SdrTableObj& rTableObj(*mxTableObj);
2040 
2041     if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
2042         return;
2043 
2044     bool bEmptyOutliner = false;
2045 
2046     if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
2047     {
2048         ::Outliner* pOutl = mrView.GetTextEditOutliner();
2049         sal_Int32 nParaCnt = pOutl->GetParagraphCount();
2050         Paragraph* p1stPara = pOutl->GetParagraph( 0 );
2051 
2052         if(nParaCnt==1 && p1stPara)
2053         {
2054             // with only one paragraph
2055             if (pOutl->GetText(p1stPara).isEmpty())
2056             {
2057                 bEmptyOutliner = true;
2058             }
2059         }
2060     }
2061 
2062     CellPos aPos( rPos );
2063     findMergeOrigin( aPos );
2064 
2065     if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
2066         return;
2067 
2068     if( rTableObj.IsTextEditActive() )
2069         mrView.SdrEndTextEdit(true);
2070 
2071     rTableObj.setActiveCell( aPos );
2072 
2073     // create new outliner, owner will be the SdrObjEditView
2074     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2075     std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
2076 
2077     if (pOutl && rTableObj.IsVerticalWriting())
2078         pOutl->SetVertical( true );
2079 
2080     if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
2081         return;
2082 
2083     maCursorLastPos = maCursorFirstPos = rPos;
2084 
2085     OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
2086 
2087     // Move cursor to end of text
2088     ESelection aNewSelection;
2089 
2090     const WritingMode eMode = rTableObj.GetWritingMode();
2091     if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
2092     {
2093         const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
2094                              ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
2095 
2096         if( bLast )
2097             aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
2098     }
2099     pOLV->SetSelection(aNewSelection);
2100 }
2101 
2102 
StopTextEdit()2103 void SvxTableController::StopTextEdit()
2104 {
2105     if(mrView.IsTextEdit())
2106     {
2107         mrView.SdrEndTextEdit();
2108         mrView.SetCurrentObj(OBJ_TABLE);
2109         mrView.SetEditMode(SdrViewEditMode::Edit);
2110     }
2111 }
2112 
2113 
getSelectedCells(CellPos & rFirst,CellPos & rLast)2114 void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
2115 {
2116     if( mbCellSelectionMode )
2117     {
2118         checkCell( maCursorFirstPos );
2119         checkCell( maCursorLastPos );
2120 
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 );
2125 
2126         bool bExt = false;
2127         if( mxTable.is() ) do
2128         {
2129             bExt = false;
2130             for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
2131             {
2132                 for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
2133                 {
2134                     Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
2135                     if( !xCell.is() )
2136                         continue;
2137 
2138                     if( xCell->isMerged() )
2139                     {
2140                         CellPos aPos( nCol, nRow );
2141                         findMergeOrigin( aPos );
2142                         if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
2143                         {
2144                             rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
2145                             rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
2146                             bExt = true;
2147                         }
2148                     }
2149                     else
2150                     {
2151                         if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
2152                         {
2153                             rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
2154                             rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
2155                             bExt = true;
2156                         }
2157                     }
2158                 }
2159             }
2160         }
2161         while(bExt);
2162     }
2163     else if(mrView.IsTextEdit())
2164     {
2165         rFirst = getSelectionStart();
2166         findMergeOrigin( rFirst );
2167         rLast = rFirst;
2168 
2169         if( mxTable.is() )
2170         {
2171             Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
2172             if( xCell.is() )
2173             {
2174                 rLast.mnCol += xCell->getColumnSpan() - 1;
2175                 rLast.mnRow += xCell->getRowSpan() - 1;
2176             }
2177         }
2178     }
2179     else
2180     {
2181         rFirst.mnCol = 0;
2182         rFirst.mnRow = 0;
2183         if( mxTable.is() )
2184         {
2185             rLast.mnRow = mxTable->getRowCount()-1;
2186             rLast.mnCol = mxTable->getColumnCount()-1;
2187         }
2188         else
2189         {
2190             rLast.mnRow = 0;
2191             rLast.mnCol = 0;
2192         }
2193     }
2194 }
2195 
2196 
StartSelection(const CellPos & rPos)2197 void SvxTableController::StartSelection( const CellPos& rPos )
2198 {
2199     StopTextEdit();
2200     mbCellSelectionMode = true;
2201     maCursorLastPos = maCursorFirstPos = rPos;
2202     mrView.MarkListHasChanged();
2203 }
2204 
2205 
setSelectedCells(const CellPos & rStart,const CellPos & rEnd)2206 void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
2207 {
2208     StopTextEdit();
2209     mbCellSelectionMode = true;
2210     maCursorFirstPos = rStart;
2211     UpdateSelection( rEnd );
2212 }
2213 
2214 
ChangeFontSize(bool bGrow,const FontList * pFontList)2215 bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
2216 {
2217     if(!checkTableObject() || !mxTable.is())
2218         return false;
2219 
2220     SdrTableObj& rTableObj(*mxTableObj);
2221     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2222 
2223     if (mrView.IsTextEdit())
2224         return true;
2225 
2226     CellPos aStart, aEnd;
2227 
2228     if(hasSelectedCells())
2229     {
2230         getSelectedCells(aStart, aEnd);
2231     }
2232     else
2233     {
2234         aStart.mnRow = 0;
2235         aStart.mnCol = 0;
2236         aEnd.mnRow = mxTable->getRowCount() - 1;
2237         aEnd.mnCol = mxTable->getColumnCount() - 1;
2238     }
2239 
2240     for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
2241     {
2242         for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
2243         {
2244             CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
2245             if (xCell.is())
2246             {
2247                 if (rModel.IsUndoEnabled())
2248                     xCell->AddUndo();
2249 
2250                 SfxItemSet aCellSet(xCell->GetItemSet());
2251                 if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
2252                 {
2253                     xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
2254                 }
2255             }
2256         }
2257     }
2258 
2259     UpdateTableShape();
2260 
2261     return true;
2262 }
2263 
2264 
UpdateSelection(const CellPos & rPos)2265 void SvxTableController::UpdateSelection( const CellPos& rPos )
2266 {
2267     maCursorLastPos = rPos;
2268     mrView.MarkListHasChanged();
2269 }
2270 
2271 
clearSelection()2272 void SvxTableController::clearSelection()
2273 {
2274     RemoveSelection();
2275 }
2276 
2277 
selectAll()2278 void SvxTableController::selectAll()
2279 {
2280     if( mxTable.is() )
2281     {
2282         CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
2283         if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
2284         {
2285             CellPos aPos1;
2286             setSelectedCells( aPos1, aPos2 );
2287         }
2288     }
2289 }
2290 
2291 
RemoveSelection()2292 void SvxTableController::RemoveSelection()
2293 {
2294     if( mbCellSelectionMode )
2295     {
2296         mbCellSelectionMode = false;
2297         mrView.MarkListHasChanged();
2298     }
2299 }
2300 
2301 
onTableModified()2302 void SvxTableController::onTableModified()
2303 {
2304     if( mnUpdateEvent == nullptr )
2305         mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
2306 }
2307 
2308 
updateSelectionOverlay()2309 void SvxTableController::updateSelectionOverlay()
2310 {
2311     // There is no need to update selection overlay after merging cells
2312     // since the selection overlay should remain the same
2313     if ( mbHasJustMerged )
2314         return;
2315 
2316     destroySelectionOverlay();
2317     if( !mbCellSelectionMode )
2318         return;
2319 
2320     sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
2321     if( !pTableObj )
2322         return;
2323 
2324     sdr::overlay::OverlayObjectCell::RangeVector aRanges;
2325 
2326     tools::Rectangle aStartRect, aEndRect;
2327     CellPos aStart,aEnd;
2328     getSelectedCells( aStart, aEnd );
2329     pTableObj->getCellBounds( aStart, aStartRect );
2330 
2331     basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
2332     a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
2333 
2334     findMergeOrigin( aEnd );
2335     pTableObj->getCellBounds( aEnd, aEndRect );
2336     a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
2337     a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
2338     aRanges.push_back( a2DRange );
2339 
2340     ::Color aHighlight( COL_BLUE );
2341     OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
2342     if( pOutDev )
2343         aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
2344 
2345     const sal_uInt32 nCount = mrView.PaintWindowCount();
2346     for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
2347     {
2348         SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
2349         if( pPaintWindow )
2350         {
2351             const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
2352             if( xOverlayManager.is() )
2353             {
2354                 std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, aRanges ));
2355 
2356                 xOverlayManager->add(*pOverlay);
2357                 mpSelectionOverlay.reset(new sdr::overlay::OverlayObjectList);
2358                 mpSelectionOverlay->append(std::move(pOverlay));
2359             }
2360         }
2361     }
2362 
2363     // If tiled rendering, emit callbacks for sdr table selection.
2364     if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
2365         return;
2366 
2367     tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
2368 
2369     if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
2370         aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
2371 
2372     if(SfxViewShell* pViewShell = SfxViewShell::Current())
2373     {
2374         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString().getStr());
2375         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString().getStr());
2376     }
2377 }
2378 
2379 
destroySelectionOverlay()2380 void SvxTableController::destroySelectionOverlay()
2381 {
2382     if( !mpSelectionOverlay )
2383         return;
2384 
2385     mpSelectionOverlay.reset();
2386 
2387     if (comphelper::LibreOfficeKit::isActive())
2388     {
2389         // Clear the LOK text selection so far provided by this table.
2390         if(SfxViewShell* pViewShell = SfxViewShell::Current())
2391         {
2392             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY");
2393             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
2394             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
2395             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
2396         }
2397     }
2398 }
2399 
2400 
MergeAttrFromSelectedCells(SfxItemSet & rAttr,bool bOnlyHardAttr) const2401 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
2402 {
2403     if( !mxTable.is() )
2404         return;
2405 
2406     CellPos aStart, aEnd;
2407     const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
2408 
2409     for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2410     {
2411         for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2412         {
2413             CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2414             if( xCell.is() && !xCell->isMerged() )
2415             {
2416                 const SfxItemSet& rSet = xCell->GetItemSet();
2417                 SfxWhichIter aIter(rSet);
2418                 sal_uInt16 nWhich(aIter.FirstWhich());
2419                 while(nWhich)
2420                 {
2421                     if(!bOnlyHardAttr)
2422                     {
2423                         if(SfxItemState::DONTCARE == rSet.GetItemState(nWhich, false))
2424                             rAttr.InvalidateItem(nWhich);
2425                         else
2426                             rAttr.MergeValue(rSet.Get(nWhich), true);
2427                     }
2428                     else if(SfxItemState::SET == rSet.GetItemState(nWhich, false))
2429                     {
2430                         const SfxPoolItem& rItem = rSet.Get(nWhich);
2431                         rAttr.MergeValue(rItem, true);
2432                     }
2433 
2434                     nWhich = aIter.NextWhich();
2435                 }
2436             }
2437         }
2438     }
2439 }
2440 
2441 
ImplSetLinePreserveColor(SvxBoxItem & rNewFrame,const SvxBorderLine * pNew,SvxBoxItemLine nLine)2442 static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
2443 {
2444     if( pNew )
2445     {
2446         const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
2447         if( pOld )
2448         {
2449             SvxBorderLine aNewLine( *pNew );
2450             aNewLine.SetColor( pOld->GetColor() );
2451             rNewFrame.SetLine( &aNewLine, nLine );
2452             return;
2453         }
2454     }
2455     rNewFrame.SetLine( pNew, nLine );
2456 }
2457 
2458 
ImplApplyBoxItem(CellPosFlag nCellPosFlags,const SvxBoxItem * pBoxItem,const SvxBoxInfoItem * pBoxInfoItem,SvxBoxItem & rNewFrame)2459 static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
2460 {
2461     if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2462     {
2463         // current cell is outside the selection
2464 
2465         if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2466         {
2467             if (nCellPosFlags & CellPosFlag::Upper)
2468             {
2469                 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
2470                     rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
2471             }
2472             else if (nCellPosFlags & CellPosFlag::Lower)
2473             {
2474                 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
2475                     rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
2476             }
2477         }
2478         else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2479         {
2480             if (nCellPosFlags & CellPosFlag::Before)
2481             {
2482                 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
2483                     rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
2484             }
2485             else if (nCellPosFlags & CellPosFlag::After)
2486             {
2487                 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
2488                     rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
2489             }
2490         }
2491     }
2492     else
2493     {
2494         // current cell is inside the selection
2495 
2496         if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
2497                                           : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
2498             rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
2499 
2500         if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2501             rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
2502 
2503         if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2504             rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
2505 
2506         if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2507             rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
2508 
2509         // apply distance to borders
2510         if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
2511             for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
2512                 rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
2513     }
2514 }
2515 
2516 
ImplSetLineColor(SvxBoxItem & rNewFrame,SvxBoxItemLine nLine,const Color & rColor)2517 static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
2518 {
2519     const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
2520     if( pSourceLine )
2521     {
2522         SvxBorderLine aLine( *pSourceLine );
2523         aLine.SetColor( rColor );
2524         rNewFrame.SetLine( &aLine, nLine );
2525     }
2526 }
2527 
2528 
ImplApplyLineColorItem(CellPosFlag nCellPosFlags,const SvxColorItem * pLineColorItem,SvxBoxItem & rNewFrame)2529 static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
2530 {
2531     const Color aColor( pLineColorItem->GetValue() );
2532 
2533     if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
2534         ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
2535 
2536     if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
2537         ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
2538 
2539     if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
2540         ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
2541 
2542     if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
2543         ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
2544 }
2545 
2546 
ImplApplyBorderLineItem(CellPosFlag nCellPosFlags,const SvxBorderLine * pBorderLineItem,SvxBoxItem & rNewFrame)2547 static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
2548 {
2549     if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
2550     {
2551         if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
2552         {
2553             if (nCellPosFlags & CellPosFlag::Upper)
2554             {
2555                 if( rNewFrame.GetBottom() )
2556                     ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2557             }
2558             else if (nCellPosFlags & CellPosFlag::Lower)
2559             {
2560                 if( rNewFrame.GetTop() )
2561                     ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2562             }
2563         }
2564         else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
2565         {
2566             if (nCellPosFlags & CellPosFlag::Before)
2567             {
2568                 if( rNewFrame.GetRight() )
2569                     ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2570             }
2571             else if (nCellPosFlags & CellPosFlag::After)
2572             {
2573                 if( rNewFrame.GetLeft() )
2574                     ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2575             }
2576         }
2577     }
2578     else
2579     {
2580         if( rNewFrame.GetBottom() )
2581             ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2582         if( rNewFrame.GetTop() )
2583             ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2584         if( rNewFrame.GetRight() )
2585             ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2586         if( rNewFrame.GetLeft() )
2587             ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2588     }
2589 }
2590 
2591 
ApplyBorderAttr(const SfxItemSet & rAttr)2592 void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
2593 {
2594     if( !mxTable.is() )
2595         return;
2596 
2597     const sal_Int32 nRowCount = mxTable->getRowCount();
2598     const sal_Int32 nColCount = mxTable->getColumnCount();
2599     if( !(nRowCount && nColCount) )
2600         return;
2601 
2602     const SvxBoxItem* pBoxItem = nullptr;
2603     if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
2604         pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
2605 
2606     const SvxBoxInfoItem* pBoxInfoItem = nullptr;
2607     if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
2608         pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
2609 
2610     const SvxColorItem* pLineColorItem = nullptr;
2611     if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
2612         pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
2613 
2614     const SvxBorderLine* pBorderLineItem = nullptr;
2615     if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
2616         pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
2617 
2618     if( pBoxInfoItem && !pBoxItem )
2619     {
2620         const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
2621         pBoxItem = &gaEmptyBoxItem;
2622     }
2623     else if( pBoxItem && !pBoxInfoItem )
2624     {
2625         const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
2626         pBoxInfoItem = &gaEmptyBoxInfoItem;
2627     }
2628 
2629     CellPos aStart, aEnd;
2630     getSelectedCells( aStart, aEnd );
2631 
2632     const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
2633     const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
2634 
2635     for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
2636     {
2637         CellPosFlag nRowFlags = CellPosFlag::NONE;
2638         nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
2639         nRowFlags |= (nRow == aEnd.mnRow)   ? CellPosFlag::Bottom : CellPosFlag::NONE;
2640         nRowFlags |= (nRow < aStart.mnRow)  ? CellPosFlag::Upper : CellPosFlag::NONE;
2641         nRowFlags |= (nRow > aEnd.mnRow)    ? CellPosFlag::Lower : CellPosFlag::NONE;
2642 
2643         for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
2644         {
2645             CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2646             if( !xCell.is() )
2647                 continue;
2648 
2649             const SfxItemSet& rSet = xCell->GetItemSet();
2650             const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
2651 
2652             SvxBoxItem aNewFrame( *pOldOuter );
2653 
2654             CellPosFlag nCellPosFlags = nRowFlags;
2655             nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
2656             nCellPosFlags |= (nCol == aEnd.mnCol)   ? CellPosFlag::Right : CellPosFlag::NONE;
2657             nCellPosFlags |= (nCol < aStart.mnCol)  ? CellPosFlag::Before : CellPosFlag::NONE;
2658             nCellPosFlags |= (nCol > aEnd.mnCol)    ? CellPosFlag::After : CellPosFlag::NONE;
2659 
2660             if( pBoxItem && pBoxInfoItem )
2661                 ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
2662 
2663             if( pLineColorItem )
2664                 ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
2665 
2666             if( pBorderLineItem )
2667                 ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
2668 
2669             if (aNewFrame != *pOldOuter)
2670             {
2671                 SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
2672                 aAttr.Put(aNewFrame);
2673                 xCell->SetMergedItemSetAndBroadcast( aAttr, false );
2674             }
2675         }
2676     }
2677 }
2678 
2679 
UpdateTableShape()2680 void SvxTableController::UpdateTableShape()
2681 {
2682     SdrObject* pTableObj = mxTableObj.get();
2683     if( pTableObj )
2684     {
2685         pTableObj->ActionChanged();
2686         pTableObj->BroadcastObjectChange();
2687     }
2688     updateSelectionOverlay();
2689 }
2690 
2691 
SetAttrToSelectedCells(const SfxItemSet & rAttr,bool bReplaceAll)2692 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
2693 {
2694     if(!checkTableObject() || !mxTable.is())
2695         return;
2696 
2697     SdrTableObj& rTableObj(*mxTableObj);
2698     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2699     const bool bUndo(rModel.IsUndoEnabled());
2700 
2701     if( bUndo )
2702         rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
2703 
2704     CellPos aStart, aEnd;
2705     getSelectedCells( aStart, aEnd );
2706 
2707     SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
2708     aAttr.Put(rAttr);
2709 
2710     const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2711 
2712     if( bFrame )
2713     {
2714         aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2715         aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2716     }
2717 
2718     for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2719     {
2720         for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2721         {
2722             CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2723             if( xCell.is() )
2724             {
2725                 if( bUndo )
2726                     xCell->AddUndo();
2727                 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2728             }
2729         }
2730     }
2731 
2732     if( bFrame )
2733     {
2734         ApplyBorderAttr( rAttr );
2735     }
2736 
2737     UpdateTableShape();
2738 
2739     if( bUndo )
2740         rModel.EndUndo();
2741 }
2742 
SetAttrToSelectedShape(const SfxItemSet & rAttr)2743 void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
2744 {
2745     if (!checkTableObject() || !mxTable.is())
2746         return;
2747 
2748     // Filter out non-shadow items from rAttr.
2749     SfxItemSet aSet(*rAttr.GetPool(), svl::Items<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST>{});
2750     aSet.Put(rAttr);
2751 
2752     // Set shadow items on the marked shape.
2753     mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
2754 }
2755 
GetAttributes(SfxItemSet & rTargetSet,bool bOnlyHardAttr) const2756 bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2757 {
2758     if( mxTableObj.is() && hasSelectedCells() )
2759     {
2760         MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
2761 
2762         if( mrView.IsTextEdit() )
2763         {
2764             OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
2765             if(pTextEditOutlinerView)
2766             {
2767                 // FALSE= consider InvalidItems not as the default, but as "holes"
2768                 rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
2769             }
2770         }
2771 
2772         return true;
2773     }
2774     else
2775     {
2776         return false;
2777     }
2778 }
2779 
2780 
SetAttributes(const SfxItemSet & rSet,bool bReplaceAll)2781 bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2782 {
2783     if( mbCellSelectionMode || mrView.IsTextEdit()  )
2784     {
2785         SetAttrToSelectedCells( rSet, bReplaceAll );
2786         return true;
2787     }
2788     return false;
2789 }
2790 
GetMarkedSdrObjClone(SdrModel & rTargetModel)2791 SdrObject* SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
2792 {
2793     SdrTableObj* pRetval(nullptr);
2794     sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
2795 
2796     if(nullptr == pCurrentSdrTableObj)
2797     {
2798         return pRetval;
2799     }
2800 
2801     if(!mxTableObj.is())
2802     {
2803         return pRetval;
2804     }
2805 
2806     // get selection and create full selection
2807     CellPos aStart, aEnd;
2808     const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
2809 
2810     getSelectedCells(aStart, aEnd);
2811 
2812     // compare to see if we have a partial selection
2813     if(aStart != aFullStart || aEnd != aFullEnd)
2814     {
2815         // create full clone
2816         pRetval = pCurrentSdrTableObj->CloneSdrObject(rTargetModel);
2817 
2818         // limit SdrObject's TableModel to partial selection
2819         pRetval->CropTableModelToSelection(aStart, aEnd);
2820     }
2821 
2822     return pRetval;
2823 }
2824 
PasteObjModel(const SdrModel & rModel)2825 bool SvxTableController::PasteObjModel( const SdrModel& rModel )
2826 {
2827     if( mxTableObj.is() && (rModel.GetPageCount() >= 1) )
2828     {
2829         const SdrPage* pPastePage = rModel.GetPage(0);
2830         if( pPastePage && pPastePage->GetObjCount() == 1 )
2831         {
2832             SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
2833             if( pPasteTableObj )
2834             {
2835                 return PasteObject( pPasteTableObj );
2836             }
2837         }
2838     }
2839 
2840     return false;
2841 }
2842 
2843 
PasteObject(SdrTableObj const * pPasteTableObj)2844 bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
2845 {
2846     if( !pPasteTableObj )
2847         return false;
2848 
2849     Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
2850     if( !xPasteTable.is() )
2851         return false;
2852 
2853     if( !mxTable.is() )
2854         return false;
2855 
2856     sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
2857     sal_Int32 nPasteRows = xPasteTable->getRowCount();
2858 
2859     CellPos aStart, aEnd;
2860     getSelectedCells( aStart, aEnd );
2861 
2862     if( mrView.IsTextEdit() )
2863         mrView.SdrEndTextEdit(true);
2864 
2865     sal_Int32 nColumns = mxTable->getColumnCount();
2866     sal_Int32 nRows = mxTable->getRowCount();
2867 
2868     const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
2869     if( nMissing > 0 )
2870     {
2871         Reference< XTableRows > xRows( mxTable->getRows() );
2872         xRows->insertByIndex( nRows, nMissing );
2873         nRows = mxTable->getRowCount();
2874     }
2875 
2876     nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
2877     nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
2878 
2879     // copy cell contents
2880     for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
2881     {
2882         for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
2883         {
2884             CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
2885             if( xTargetCell.is() && !xTargetCell->isMerged() )
2886             {
2887                 CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
2888                 if (xSourceCell.is())
2889                 {
2890                     xTargetCell->AddUndo();
2891                     // Prevent cell span exceeding the pasting range.
2892                     if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
2893                         xTargetCell->replaceContentAndFormatting(xSourceCell);
2894                     else
2895                         xTargetCell->cloneFrom(xSourceCell);
2896 
2897                     nCol += xSourceCell->getColumnSpan() - 1;
2898                     nTargetCol += xTargetCell->getColumnSpan();
2899                 }
2900             }
2901         }
2902     }
2903 
2904     UpdateTableShape();
2905 
2906     return true;
2907 }
2908 
ApplyFormatPaintBrush(SfxItemSet & rFormatSet,bool bNoCharacterFormats,bool bNoParagraphFormats)2909 bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
2910 {
2911     if(!mbCellSelectionMode)
2912     {
2913         return false;
2914     }
2915 
2916     if(!checkTableObject())
2917         return false;
2918 
2919     SdrTableObj& rTableObj(*mxTableObj);
2920     SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
2921     const bool bUndo(rModel.IsUndoEnabled());
2922 
2923     if( bUndo )
2924         rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
2925 
2926     CellPos aStart, aEnd;
2927     getSelectedCells( aStart, aEnd );
2928     const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2929 
2930     for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2931     {
2932         for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2933         {
2934             CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2935             if( xCell.is() )
2936             {
2937                 if (bUndo)
2938                     xCell->AddUndo();
2939                 SdrText* pText = xCell.get();
2940                 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
2941             }
2942         }
2943     }
2944 
2945     if( bFrame )
2946     {
2947         ApplyBorderAttr( rFormatSet );
2948     }
2949 
2950     UpdateTableShape();
2951 
2952     if( bUndo )
2953         rModel.EndUndo();
2954 
2955     return true;
2956 }
2957 
2958 
IMPL_LINK_NOARG(SvxTableController,UpdateHdl,void *,void)2959 IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
2960 {
2961     mnUpdateEvent = nullptr;
2962 
2963     if( mbCellSelectionMode )
2964     {
2965         CellPos aStart( maCursorFirstPos );
2966         CellPos aEnd( maCursorLastPos );
2967         checkCell(aStart);
2968         checkCell(aEnd);
2969         if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
2970         {
2971             setSelectedCells( aStart, aEnd );
2972         }
2973     }
2974 
2975     updateSelectionOverlay();
2976     mbHasJustMerged = false;
2977 }
2978 
2979 namespace
2980 {
2981 
2982 struct LinesState
2983 {
LinesStatesdr::table::__anon356e5d810711::LinesState2984     LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
2985         : rBoxItem(rBoxItem_)
2986         , rBoxInfoItem(rBoxInfoItem_)
2987         , bDistanceIndeterminate(false)
2988     {
2989         aBorderSet.fill(false);
2990         aInnerLineSet.fill(false);
2991         aBorderIndeterminate.fill(false);
2992         aInnerLineIndeterminate.fill(false);
2993         aDistanceSet.fill(false);
2994         aDistance.fill(0);
2995     }
2996 
2997     SvxBoxItem& rBoxItem;
2998     SvxBoxInfoItem& rBoxInfoItem;
2999     o3tl::enumarray<SvxBoxItemLine, bool>       aBorderSet;
3000     o3tl::enumarray<SvxBoxInfoItemLine, bool>   aInnerLineSet;
3001     o3tl::enumarray<SvxBoxItemLine, bool>       aBorderIndeterminate;
3002     o3tl::enumarray<SvxBoxInfoItemLine, bool>   aInnerLineIndeterminate;
3003     o3tl::enumarray<SvxBoxItemLine, bool>       aDistanceSet;
3004     o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
3005     bool bDistanceIndeterminate;
3006 };
3007 
3008 class BoxItemWrapper
3009 {
3010 public:
3011     BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
3012 
3013     const SvxBorderLine* getLine() const;
3014     void setLine(const SvxBorderLine* pLine);
3015 
3016 private:
3017     SvxBoxItem& m_rBoxItem;
3018     SvxBoxInfoItem& m_rBoxInfoItem;
3019     const SvxBoxItemLine m_nBorderLine;
3020     const SvxBoxInfoItemLine m_nInnerLine;
3021     const bool m_bBorder;
3022 };
3023 
BoxItemWrapper(SvxBoxItem & rBoxItem,SvxBoxInfoItem & rBoxInfoItem,const SvxBoxItemLine nBorderLine,const SvxBoxInfoItemLine nInnerLine,const bool bBorder)3024 BoxItemWrapper::BoxItemWrapper(
3025         SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
3026         const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
3027     : m_rBoxItem(rBoxItem)
3028     , m_rBoxInfoItem(rBoxInfoItem)
3029     , m_nBorderLine(nBorderLine)
3030     , m_nInnerLine(nInnerLine)
3031     , m_bBorder(bBorder)
3032 {
3033 }
3034 
getLine() const3035 const SvxBorderLine* BoxItemWrapper::getLine() const
3036 {
3037     if (m_bBorder)
3038         return m_rBoxItem.GetLine(m_nBorderLine);
3039     else
3040         return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
3041 }
3042 
setLine(const SvxBorderLine * pLine)3043 void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
3044 {
3045     if (m_bBorder)
3046         m_rBoxItem.SetLine(pLine, m_nBorderLine);
3047     else
3048         m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
3049 }
3050 
lcl_MergeBorderLine(LinesState & rLinesState,const SvxBorderLine * const pLine,const SvxBoxItemLine nLine,SvxBoxInfoItemValidFlags nValidFlag,const bool bBorder=true)3051 void lcl_MergeBorderLine(
3052         LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3053         SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
3054 {
3055     const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
3056     BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
3057     bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
3058 
3059     if (rbSet)
3060     {
3061         bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
3062         if (!rbIndeterminate)
3063         {
3064             const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
3065             if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
3066             {
3067                 aBoxItem.setLine(nullptr);
3068                 rbIndeterminate = true;
3069             }
3070         }
3071     }
3072     else
3073     {
3074         aBoxItem.setLine(pLine);
3075         rbSet = true;
3076     }
3077 }
3078 
lcl_MergeBorderOrInnerLine(LinesState & rLinesState,const SvxBorderLine * const pLine,const SvxBoxItemLine nLine,SvxBoxInfoItemValidFlags nValidFlag,const bool bBorder)3079 void lcl_MergeBorderOrInnerLine(
3080         LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
3081         SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
3082 {
3083     if (bBorder)
3084         lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
3085     else
3086     {
3087         const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
3088         lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
3089     }
3090 }
3091 
lcl_MergeDistance(LinesState & rLinesState,const SvxBoxItemLine nIndex,const sal_uInt16 nDistance)3092 void lcl_MergeDistance(
3093         LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
3094 {
3095     if (rLinesState.aDistanceSet[nIndex])
3096     {
3097         if (!rLinesState.bDistanceIndeterminate)
3098             rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
3099     }
3100     else
3101     {
3102         rLinesState.aDistance[nIndex] = nDistance;
3103         rLinesState.aDistanceSet[nIndex] = true;
3104     }
3105 }
3106 
lcl_MergeCommonBorderAttr(LinesState & rLinesState,const SvxBoxItem & rCellBoxItem,const CellPosFlag nCellPosFlags)3107 void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
3108 {
3109     if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
3110     {
3111         // current cell is outside the selection
3112 
3113         if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
3114         {
3115             if (nCellPosFlags & CellPosFlag::Upper)
3116                 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
3117             else if (nCellPosFlags & CellPosFlag::Lower)
3118                 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
3119         }
3120         else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
3121         {
3122             if (nCellPosFlags & CellPosFlag::Before)
3123                 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
3124             else if (nCellPosFlags & CellPosFlag::After)
3125                 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
3126         }
3127 
3128         // NOTE: inner distances for cells outside the selected range
3129         // are not relevant -> we ignore them.
3130     }
3131     else
3132     {
3133         // current cell is inside the selection
3134 
3135         lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
3136         lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
3137         lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
3138         lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
3139 
3140         lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
3141         lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
3142         lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
3143         lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
3144     }
3145 }
3146 
3147 }
3148 
FillCommonBorderAttrFromSelectedCells(SvxBoxItem & rBoxItem,SvxBoxInfoItem & rBoxInfoItem) const3149 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
3150 {
3151     if( !mxTable.is() )
3152         return;
3153 
3154     const sal_Int32 nRowCount = mxTable->getRowCount();
3155     const sal_Int32 nColCount = mxTable->getColumnCount();
3156     if( !(nRowCount && nColCount) )
3157         return;
3158 
3159     CellPos aStart, aEnd;
3160     const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
3161 
3162     // We are adding one more row/column around the block of selected cells.
3163     // We will be checking the adjoining border of these too.
3164     const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
3165     const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
3166 
3167     rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
3168     LinesState aLinesState( rBoxItem, rBoxInfoItem );
3169 
3170     /* Here we go through all the selected cells (enhanced by
3171      * the adjoining row/column on each side) and determine the
3172      * lines for presentation. The algorithm is simple:
3173      * 1. if a border or inner line is set (or unset) in all
3174      *    cells to the same value, it will be used.
3175      * 2. if a border or inner line is set only in some cells,
3176      *    it will be set to indeterminate state (SetValid() on
3177      *    rBoxInfoItem).
3178      */
3179     for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
3180     {
3181         CellPosFlag nRowFlags = CellPosFlag::NONE;
3182         nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
3183         nRowFlags |= (nRow == aEnd.mnRow)   ? CellPosFlag::Bottom : CellPosFlag::NONE;
3184         nRowFlags |= (nRow < aStart.mnRow)  ? CellPosFlag::Upper : CellPosFlag::NONE;
3185         nRowFlags |= (nRow > aEnd.mnRow)    ? CellPosFlag::Lower : CellPosFlag::NONE;
3186 
3187         for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
3188         {
3189             CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
3190             if( !xCell.is() )
3191                 continue;
3192 
3193             CellPosFlag nCellPosFlags = nRowFlags;
3194             nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
3195             nCellPosFlags |= (nCol == aEnd.mnCol)   ? CellPosFlag::Right : CellPosFlag::NONE;
3196             nCellPosFlags |= (nCol < aStart.mnCol)  ? CellPosFlag::Before : CellPosFlag::NONE;
3197             nCellPosFlags |= (nCol > aEnd.mnCol)    ? CellPosFlag::After : CellPosFlag::NONE;
3198 
3199             const SfxItemSet& rSet = xCell->GetItemSet();
3200             SvxBoxItem aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
3201             lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
3202         }
3203     }
3204 
3205     if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
3206         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
3207     if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
3208         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
3209     if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
3210         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
3211     if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
3212         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
3213     if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
3214         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
3215     if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
3216         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
3217 
3218     if (!aLinesState.bDistanceIndeterminate)
3219     {
3220         if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
3221             aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
3222         if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
3223             aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
3224         if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
3225             aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
3226         if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
3227             aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
3228         aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
3229     }
3230 }
3231 
selectRow(sal_Int32 row)3232 bool SvxTableController::selectRow( sal_Int32 row )
3233 {
3234     if( !mxTable.is() )
3235         return false;
3236     CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3237     StartSelection( aEnd );
3238     gotoCell( aStart, true, nullptr );
3239     return true;
3240 }
3241 
selectColumn(sal_Int32 column)3242 bool SvxTableController::selectColumn( sal_Int32 column )
3243 {
3244     if( !mxTable.is() )
3245         return false;
3246     CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3247     StartSelection( aEnd );
3248     gotoCell( aStart, true, nullptr );
3249     return true;
3250 }
3251 
deselectRow(sal_Int32 row)3252 bool SvxTableController::deselectRow( sal_Int32 row )
3253 {
3254     if( !mxTable.is() )
3255         return false;
3256     CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3257     StartSelection( aEnd );
3258     gotoCell( aStart, false, nullptr );
3259     return true;
3260 }
3261 
deselectColumn(sal_Int32 column)3262 bool SvxTableController::deselectColumn( sal_Int32 column )
3263 {
3264     if( !mxTable.is() )
3265         return false;
3266     CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3267     StartSelection( aEnd );
3268     gotoCell( aStart, false, nullptr );
3269     return true;
3270 }
3271 
isRowSelected(sal_Int32 nRow)3272 bool SvxTableController::isRowSelected( sal_Int32 nRow )
3273 {
3274     if( hasSelectedCells() )
3275     {
3276         CellPos aFirstPos, aLastPos;
3277         getSelectedCells( aFirstPos, aLastPos );
3278         if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
3279             return true;
3280     }
3281     return false;
3282 }
3283 
isColumnSelected(sal_Int32 nColumn)3284 bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
3285 {
3286     if( hasSelectedCells() )
3287     {
3288         CellPos aFirstPos, aLastPos;
3289         getSelectedCells( aFirstPos, aLastPos );
3290         if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
3291             return true;
3292     }
3293     return false;
3294 }
3295 
isRowHeader()3296 bool SvxTableController::isRowHeader()
3297 {
3298     if(!checkTableObject())
3299         return false;
3300 
3301     SdrTableObj& rTableObj(*mxTableObj);
3302     TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3303 
3304     return aSettings.mbUseFirstRow;
3305 }
3306 
isColumnHeader()3307 bool SvxTableController::isColumnHeader()
3308 {
3309     if(!checkTableObject())
3310         return false;
3311 
3312     SdrTableObj& rTableObj(*mxTableObj);
3313     TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
3314 
3315     return aSettings.mbUseFirstColumn;
3316 }
3317 
setCursorLogicPosition(const Point & rPosition,bool bPoint)3318 bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
3319 {
3320     if (mxTableObj->GetObjIdentifier() != OBJ_TABLE)
3321         return false;
3322 
3323     SdrTableObj* pTableObj = mxTableObj.get();
3324     CellPos aCellPos;
3325     if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
3326     {
3327         // Position is a table cell.
3328         if (mbCellSelectionMode)
3329         {
3330             // We have a table selection already: adjust the point or the mark.
3331             if (bPoint)
3332                 setSelectedCells(maCursorFirstPos, aCellPos);
3333             else
3334                 setSelectedCells(aCellPos, maCursorLastPos);
3335             return true;
3336         }
3337         else if (aCellPos != maMouseDownPos)
3338         {
3339             // No selection, but rPosition is at another cell: start table selection.
3340             StartSelection(maMouseDownPos);
3341             // Update graphic selection, should be hidden now.
3342             mrView.AdjustMarkHdl();
3343         }
3344     }
3345 
3346     return false;
3347 }
3348 
3349 }
3350 
3351 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3352