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