bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / table / tablecontroller.cxx
blob64b40da926c2f18bc755866b63acfe5e6586d3ef
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <algorithm>
22 #include <svx/sdr/table/tablecontroller.hxx>
23 #include <tablemodel.hxx>
25 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
26 #include <com/sun/star/container/XIndexAccess.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/table/XMergeableCellRange.hpp>
30 #include <com/sun/star/table/XMergeableCell.hpp>
32 #include <sal/config.h>
34 #include <vcl/svapp.hxx>
35 #include <vcl/settings.hxx>
37 #include <svl/whiter.hxx>
39 #include <sfx2/request.hxx>
41 #include <editeng/scripttypeitem.hxx>
42 #include <svx/svdotable.hxx>
43 #include <svx/sdr/overlay/overlayobjectcell.hxx>
44 #include <svx/sdr/overlay/overlaymanager.hxx>
45 #include <svx/svxids.hrc>
46 #include <editeng/outlobj.hxx>
47 #include <svx/svdoutl.hxx>
48 #include <svx/svdpagv.hxx>
49 #include <svx/svdetc.hxx>
50 #include <editeng/editobj.hxx>
51 #include "editeng/editstat.hxx"
52 #include "editeng/unolingu.hxx"
53 #include "svx/sdrpagewindow.hxx"
54 #include <svx/selectioncontroller.hxx>
55 #include <svx/svdmodel.hxx>
56 #include "svx/sdrpaintwindow.hxx"
57 #include <svx/svxdlg.hxx>
58 #include <editeng/boxitem.hxx>
59 #include "cell.hxx"
60 #include <editeng/borderline.hxx>
61 #include <editeng/colritem.hxx>
62 #include "editeng/lineitem.hxx"
63 #include "svx/svdstr.hrc"
64 #include "svdglob.hxx"
65 #include "svx/svdpage.hxx"
66 #include "tableundo.hxx"
67 #include "tablelayouter.hxx"
68 #include <vcl/msgbox.hxx>
69 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
70 #include <memory>
71 #include <o3tl/enumarray.hxx>
72 #include <o3tl/enumrange.hxx>
74 using ::editeng::SvxBorderLine;
75 using namespace sdr::table;
76 using namespace ::com::sun::star;
77 using namespace ::com::sun::star::uno;
78 using namespace ::com::sun::star::table;
79 using namespace ::com::sun::star::beans;
80 using namespace ::com::sun::star::container;
81 using namespace ::com::sun::star::text;
82 using namespace ::com::sun::star::style;
84 namespace sdr { namespace table {
86 class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XModifyListener >
88 public:
89 SvxTableControllerModifyListener( SvxTableController* pController )
90 : mpController( pController ) {}
92 // XModifyListener
93 virtual void SAL_CALL modified( const ::com::sun::star::lang::EventObject& aEvent ) throw (::com::sun::star::uno::RuntimeException, std::exception) SAL_OVERRIDE;
95 // XEventListener
96 virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (::com::sun::star::uno::RuntimeException, std::exception) SAL_OVERRIDE;
98 SvxTableController* mpController;
102 // XModifyListener
105 void SAL_CALL SvxTableControllerModifyListener::modified( const ::com::sun::star::lang::EventObject& ) throw (::com::sun::star::uno::RuntimeException, std::exception)
107 if( mpController )
108 mpController->onTableModified();
112 // XEventListener
115 void SAL_CALL SvxTableControllerModifyListener::disposing( const ::com::sun::star::lang::EventObject& ) throw (::com::sun::star::uno::RuntimeException, std::exception)
117 mpController = 0;
121 // class SvxTableController
124 rtl::Reference< sdr::SelectionController > CreateTableController( SdrObjEditView* pView, const SdrObject* pObj, const rtl::Reference< sdr::SelectionController >& xRefController )
126 return SvxTableController::create( pView, pObj, xRefController );
131 rtl::Reference< sdr::SelectionController > SvxTableController::create( SdrObjEditView* pView, const SdrObject* pObj, const rtl::Reference< sdr::SelectionController >& xRefController )
133 if( xRefController.is() )
135 SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
136 if( pController && (pController->mxTableObj.get() == pObj) && (pController->mpView == pView) )
137 return xRefController;
139 return new SvxTableController( pView, pObj );
144 SvxTableController::SvxTableController( SdrObjEditView* pView, const SdrObject* pObj )
145 : mbCellSelectionMode(false)
146 , mbLeftButtonDown(false)
147 , mpSelectionOverlay(0)
148 , mpView( dynamic_cast< SdrView* >( pView ) )
149 , mxTableObj( dynamic_cast< SdrTableObj* >( const_cast< SdrObject* >( pObj ) ) )
150 , mpModel( 0 )
151 , mnUpdateEvent( 0 )
153 if( pObj )
155 mpModel = pObj->GetModel();
157 if( mxTableObj.is() )
159 static_cast< const SdrTableObj* >( pObj )->getActiveCellPos( maCursorFirstPos );
160 maCursorLastPos = maCursorFirstPos;
162 Reference< XTable > xTable( static_cast< const SdrTableObj* >( pObj )->getTable() );
163 if( xTable.is() )
165 mxModifyListener = new SvxTableControllerModifyListener( this );
166 xTable->addModifyListener( mxModifyListener );
168 mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
174 SvxTableController::~SvxTableController()
176 if( mnUpdateEvent )
178 Application::RemoveUserEvent( mnUpdateEvent );
181 if( mxModifyListener.is() && mxTableObj.get() )
183 Reference< XTable > xTable( static_cast< SdrTableObj* >( mxTableObj.get() )->getTable() );
184 if( xTable.is() )
186 xTable->removeModifyListener( mxModifyListener );
187 mxModifyListener.clear();
194 const sal_uInt16 ACTION_NONE = 0;
195 const sal_uInt16 ACTION_GOTO_FIRST_CELL = 1;
196 const sal_uInt16 ACTION_GOTO_FIRST_COLUMN = 2;
197 const sal_uInt16 ACTION_GOTO_FIRST_ROW = 3;
198 const sal_uInt16 ACTION_GOTO_LEFT_CELL = 4;
199 const sal_uInt16 ACTION_GOTO_UP_CELL = 5;
200 const sal_uInt16 ACTION_GOTO_RIGHT_CELL = 6;
201 const sal_uInt16 ACTION_GOTO_DOWN_CELL = 7;
202 const sal_uInt16 ACTION_GOTO_LAST_CELL = 8;
203 const sal_uInt16 ACTION_GOTO_LAST_COLUMN = 9;
204 const sal_uInt16 ACTION_GOTO_LAST_ROW = 10;
205 const sal_uInt16 ACTION_EDIT_CELL = 11;
206 const sal_uInt16 ACTION_STOP_TEXT_EDIT = 12;
207 const sal_uInt16 ACTION_REMOVE_SELECTION = 13;
208 const sal_uInt16 ACTION_START_SELECTION = 14;
209 const sal_uInt16 ACTION_HANDLED_BY_VIEW = 15;
210 const sal_uInt16 ACTION_TAB = 18;
212 bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
214 if( !checkTableObject() )
215 return false;
217 // check if we are read only
218 if( mpModel && mpModel->IsReadOnly())
220 switch( rKEvt.GetKeyCode().GetCode() )
222 case awt::Key::DOWN:
223 case awt::Key::UP:
224 case awt::Key::LEFT:
225 case awt::Key::RIGHT:
226 case awt::Key::TAB:
227 case awt::Key::HOME:
228 case awt::Key::END:
229 case awt::Key::NUM2:
230 case awt::Key::NUM4:
231 case awt::Key::NUM6:
232 case awt::Key::NUM8:
233 case awt::Key::ESCAPE:
234 case awt::Key::F2:
235 break;
236 default:
237 // tell the view we eat the event, no further processing needed
238 return true;
242 sal_uInt16 nAction = getKeyboardAction( rKEvt, pWindow );
244 return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
248 // ::com::sun::star::awt::XMouseClickHandler:
251 bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
253 if (mxTableObj->GetModel()->isTiledRendering() && !pWindow)
255 // Tiled rendering: get the window that has the disabled map mode.
256 if (OutputDevice* pOutputDevice = mpView->GetFirstOutputDevice())
258 if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
259 pWindow = static_cast<vcl::Window*>(pOutputDevice);
263 if( !pWindow || !checkTableObject() )
264 return false;
266 SdrViewEvent aVEvt;
267 if( !rMEvt.IsRight() && mpView->PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SDRHIT_HANDLE )
268 return false;
270 TableHitKind eHit = static_cast< SdrTableObj* >(mxTableObj.get())->CheckTableHit( pWindow->PixelToLogic(rMEvt.GetPosPixel()), maMouseDownPos.mnCol, maMouseDownPos.mnRow, 0 );
272 mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
274 if( eHit == SDRTABLEHIT_CELL )
276 StartSelection( maMouseDownPos );
277 return true;
280 if( rMEvt.IsRight() && eHit != SDRTABLEHIT_NONE )
281 return true; // right click will become context menu
283 // for cell selection with the mouse remember our first hit
284 if( mbLeftButtonDown )
286 RemoveSelection();
288 Point aPnt(rMEvt.GetPosPixel());
289 if (pWindow!=NULL)
290 aPnt=pWindow->PixelToLogic(aPnt);
292 SdrHdl* pHdl = mpView->PickHandle(aPnt);
294 if( pHdl )
296 mbLeftButtonDown = false;
298 else
300 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
302 if( !pWindow || !pTableObj || eHit == SDRTABLEHIT_NONE)
304 mbLeftButtonDown = false;
309 if (mxTableObj->GetModel()->isTiledRendering() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == SDRTABLEHIT_CELLTEXTAREA)
311 bool bEmptyOutliner = false;
312 if (Outliner* pOutliner = mpView->GetTextEditOutliner())
314 if (pOutliner->GetParagraphCount() == 1)
316 if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
317 bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
320 if (bEmptyOutliner)
322 // Tiled rendering: a left double-click in an empty cell: select it.
323 StartSelection(maMouseDownPos);
324 setSelectedCells(maMouseDownPos, maMouseDownPos);
325 // Update graphic selection, should be hidden now.
326 mpView->AdjustMarkHdl();
327 return true;
331 return false;
336 bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
338 if( !checkTableObject() )
339 return false;
341 mbLeftButtonDown = false;
343 if( rMEvt.GetClicks() == 2 )
344 return true;
346 return false;
351 bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
353 if( !checkTableObject() )
354 return false;
356 SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >( mxTableObj.get() );
357 CellPos aPos;
358 if( mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit( pWindow->PixelToLogic(rMEvt.GetPosPixel()), aPos.mnCol, aPos.mnRow, 0 ) != SDRTABLEHIT_NONE )
360 if(aPos != maMouseDownPos)
362 if( mbCellSelectionMode )
364 setSelectedCells( maMouseDownPos, aPos );
365 return true;
367 else
369 StartSelection( maMouseDownPos );
372 else if( mbCellSelectionMode )
374 UpdateSelection( aPos );
375 return true;
378 return false;
383 void SvxTableController::onSelectionHasChanged()
385 bool bSelected = false;
387 SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >( mxTableObj.get() );
388 if( pTableObj && pTableObj->IsTextEditActive() )
390 pTableObj->getActiveCellPos( maCursorFirstPos );
391 maCursorLastPos = maCursorFirstPos;
392 mbCellSelectionMode = false;
394 else
396 const SdrMarkList& rMarkList= mpView->GetMarkedObjectList();
397 if( rMarkList.GetMarkCount() == 1 )
398 bSelected = mxTableObj.get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
399 /* fdo#46186 Selecting the table means selecting the entire cells */
400 if (!hasSelectedCells() && pTableObj)
402 maCursorFirstPos = SdrTableObj::getFirstCell();
403 maCursorLastPos = pTableObj->getLastCell();
404 mbCellSelectionMode=true;
408 if( bSelected )
410 updateSelectionOverlay();
412 else
414 destroySelectionOverlay();
420 void SvxTableController::GetState( SfxItemSet& rSet )
422 if( !mxTable.is() || !mxTableObj.is() || !mxTableObj->GetModel() )
423 return;
425 std::unique_ptr<SfxItemSet> xSet;
427 bool bVertDone = false;
429 // Iterate over all requested items in the set.
430 SfxWhichIter aIter( rSet );
431 sal_uInt16 nWhich = aIter.FirstWhich();
432 while (nWhich)
434 switch (nWhich)
436 case SID_TABLE_VERT_BOTTOM:
437 case SID_TABLE_VERT_CENTER:
438 case SID_TABLE_VERT_NONE:
440 if( !mxTable.is() || !mxTableObj->GetModel() )
442 rSet.DisableItem(nWhich);
444 else if(!bVertDone)
446 if (!xSet)
448 xSet.reset(new SfxItemSet( mxTableObj->GetModel()->GetItemPool() ));
449 MergeAttrFromSelectedCells(*xSet, false);
452 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
454 if (xSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
455 eAdj = static_cast<const SdrTextVertAdjustItem&>(xSet->Get(SDRATTR_TEXT_VERTADJUST)).GetValue();
457 rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
458 rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
459 rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
460 bVertDone = true;
462 break;
464 case SID_TABLE_DELETE_ROW:
465 if( !mxTable.is() || !hasSelectedCells() || (mxTable->getRowCount() <= 1) )
466 rSet.DisableItem(SID_TABLE_DELETE_ROW);
467 break;
468 case SID_TABLE_DELETE_COL:
469 if( !mxTable.is() || !hasSelectedCells() || (mxTable->getColumnCount() <= 1) )
470 rSet.DisableItem(SID_TABLE_DELETE_COL);
471 break;
472 case SID_TABLE_MERGE_CELLS:
473 if( !mxTable.is() || !hasSelectedCells() )
474 rSet.DisableItem(SID_TABLE_MERGE_CELLS);
475 break;
476 case SID_TABLE_SPLIT_CELLS:
477 if( !hasSelectedCells() || !mxTable.is() )
478 rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
479 break;
481 case SID_OPTIMIZE_TABLE:
482 case SID_TABLE_DISTRIBUTE_COLUMNS:
483 case SID_TABLE_DISTRIBUTE_ROWS:
485 bool bDistributeColumns = false;
486 bool bDistributeRows = false;
487 if( mxTable.is() )
489 CellPos aStart, aEnd;
490 getSelectedCells( aStart, aEnd );
492 bDistributeColumns = aStart.mnCol != aEnd.mnCol;
493 bDistributeRows = aStart.mnRow != aEnd.mnRow;
495 if( !bDistributeColumns && !bDistributeRows )
496 rSet.DisableItem(SID_OPTIMIZE_TABLE);
497 if( !bDistributeColumns )
498 rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
499 if( !bDistributeRows )
500 rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
501 break;
504 case SID_AUTOFORMAT:
505 case SID_TABLE_SORT_DIALOG:
506 case SID_TABLE_AUTOSUM:
507 // if( !mxTable.is() )
508 // rSet.DisableItem( nWhich );
509 break;
510 default:
511 break;
513 nWhich = aIter.NextWhich();
519 void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
521 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
522 if( !pTableObj )
523 return;
525 if( mxTable.is() ) try
528 bool bInsertAfter = true;
529 sal_uInt16 nCount = 0;
530 if( pArgs )
532 const SfxPoolItem* pItem = 0;
533 pArgs->GetItemState(nSId, false, &pItem);
534 if (pItem)
536 nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
537 if(SfxItemState::SET == pArgs->GetItemState(SID_TABLE_PARAM_INSERT_AFTER, true, &pItem))
538 bInsertAfter = static_cast<const SfxBoolItem*>(pItem)->GetValue();
542 CellPos aStart, aEnd;
543 if( hasSelectedCells() )
545 getSelectedCells( aStart, aEnd );
547 else
549 if( bInsertAfter )
551 aStart.mnCol = mxTable->getColumnCount() - 1;
552 aStart.mnRow = mxTable->getRowCount() - 1;
553 aEnd = aStart;
557 if( pTableObj->IsTextEditActive() )
558 mpView->SdrEndTextEdit(true);
560 RemoveSelection();
562 const OUString sSize( "Size" );
564 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
566 switch( nSId )
568 case SID_TABLE_INSERT_COL:
570 TableModelNotifyGuard aGuard( mxTable.get() );
572 if( bUndo )
574 mpModel->BegUndo( ImpGetResStr(STR_TABLE_INSCOL) );
575 mpModel->AddUndo( mpModel->GetSdrUndoFactory().CreateUndoGeoObject(*pTableObj) );
578 Reference< XTableColumns > xCols( mxTable->getColumns() );
579 const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
580 const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
581 xCols->insertByIndex( nNewStartColumn, nNewColumns );
583 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
585 // Resolves fdo#61540
586 // On Insert before, the reference column whose size is going to be
587 // used for newly created column(s) is wrong. As the new columns are
588 // inserted before the reference column, the reference column moved
589 // to the new position by no., of new columns i.e (earlier+newcolumns).
590 Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
591 setPropertyValue( sSize,
592 Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
593 getPropertyValue( sSize ) );
596 // Copy cell properties
597 sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
598 sal_Int32 nRowSpan = 0;
599 bool bNewSpan = false;
601 for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
603 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
605 // When we insert new COLUMNs, we want to copy ROW spans.
606 if (xSourceCell.is() && nRowSpan == 0)
608 // we are not in a span yet. Let's find out if the current cell is in a span.
609 sal_Int32 nColSpan = sal_Int32();
610 sal_Int32 nSpanInfoCol = sal_Int32();
612 if( xSourceCell->getRowSpan() > 1 )
614 // The current cell is the top-left cell in a span.
615 // Get the span info and propagate it to the target.
616 nRowSpan = xSourceCell->getRowSpan();
617 nColSpan = xSourceCell->getColumnSpan();
618 nSpanInfoCol = nPropSrcCol;
620 else if( xSourceCell->isMerged() )
622 // The current cell is a middle cell in a 2D span.
623 // Look for the top-left cell in the span.
624 for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
626 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
627 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
629 nRowSpan = xMergeInfoCell->getRowSpan();
630 nColSpan = xMergeInfoCell->getColumnSpan();
631 break;
634 if( nRowSpan == 1 )
635 nRowSpan = 0;
638 // The target colomns are outside the span; Start a new span.
639 if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
640 bNewSpan = true;
643 // Now copy the properties from the source to the targets
644 for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
646 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
647 if( xTargetCell.is() )
649 if( nRowSpan > 0 )
651 if( bNewSpan )
652 xTargetCell->merge( 1, nRowSpan );
653 else
654 xTargetCell->setMerged();
656 xTargetCell->copyFormatFrom( xSourceCell );
660 if( nRowSpan > 0 )
662 --nRowSpan;
663 bNewSpan = false;
667 if( bUndo )
668 mpModel->EndUndo();
670 aStart.mnCol = nNewStartColumn;
671 aStart.mnRow = 0;
672 aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
673 aEnd.mnRow = mxTable->getRowCount() - 1;
674 break;
677 case SID_TABLE_INSERT_ROW:
679 TableModelNotifyGuard aGuard( mxTable.get() );
681 if( bUndo )
683 mpModel->BegUndo( ImpGetResStr(STR_TABLE_INSROW ) );
684 mpModel->AddUndo( mpModel->GetSdrUndoFactory().CreateUndoGeoObject(*pTableObj) );
687 Reference< XTableRows > xRows( mxTable->getRows() );
688 const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
689 const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
690 xRows->insertByIndex( nNewRowStart, nNewRows );
692 for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
694 Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
695 setPropertyValue( sSize,
696 Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
697 getPropertyValue( sSize ) );
700 // Copy the cell properties
701 sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
702 sal_Int32 nColSpan = 0;
703 bool bNewSpan = false;
705 for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
707 CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
709 if (!xSourceCell.is())
710 continue;
712 // When we insert new ROWs, we want to copy COLUMN spans.
713 if( nColSpan == 0 )
715 // we are not in a span yet. Let's find out if the current cell is in a span.
716 sal_Int32 nRowSpan = sal_Int32();
717 sal_Int32 nSpanInfoRow = sal_Int32();
719 if( xSourceCell->getColumnSpan() > 1 )
721 // The current cell is the top-left cell in a span.
722 // Get the span info and propagate it to the target.
723 nColSpan = xSourceCell->getColumnSpan();
724 nRowSpan = xSourceCell->getRowSpan();
725 nSpanInfoRow = nPropSrcRow;
727 else if( xSourceCell->isMerged() )
729 // The current cell is a middle cell in a 2D span.
730 // Look for the top-left cell in the span.
731 for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
733 CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
734 if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
736 nColSpan = xMergeInfoCell->getColumnSpan();
737 nRowSpan = xMergeInfoCell->getRowSpan();
738 break;
741 if( nColSpan == 1 )
742 nColSpan = 0;
745 // Inserted rows are outside the span; Start a new span.
746 if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
747 bNewSpan = true;
750 // Now copy the properties from the source to the targets
751 for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
753 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
754 if( xTargetCell.is() )
756 if( nColSpan > 0 )
758 if( bNewSpan )
759 xTargetCell->merge( nColSpan, 1 );
760 else
761 xTargetCell->setMerged();
763 xTargetCell->copyFormatFrom( xSourceCell );
767 if( nColSpan > 0 )
769 --nColSpan;
770 bNewSpan = false;
774 if( bUndo )
775 mpModel->EndUndo();
777 aStart.mnCol = 0;
778 aStart.mnRow = nNewRowStart;
779 aEnd.mnCol = mxTable->getColumnCount() - 1;
780 aEnd.mnRow = aStart.mnRow + nNewRows - 1;
781 break;
785 StartSelection( aStart );
786 UpdateSelection( aEnd );
788 catch( Exception& )
790 OSL_FAIL("svx::SvxTableController::onInsert(), exception caught!");
796 void SvxTableController::onDelete( sal_uInt16 nSId )
798 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
799 if( !pTableObj )
800 return;
802 if( mxTable.is() && hasSelectedCells() )
804 CellPos aStart, aEnd;
805 getSelectedCells( aStart, aEnd );
807 if( pTableObj->IsTextEditActive() )
808 mpView->SdrEndTextEdit(true);
810 RemoveSelection();
812 bool bDeleteTable = false;
813 switch( nSId )
815 case SID_TABLE_DELETE_COL:
817 const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
818 if( nRemovedColumns == mxTable->getColumnCount() )
820 bDeleteTable = true;
822 else
824 Reference< XTableColumns > xCols( mxTable->getColumns() );
825 xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
827 break;
830 case SID_TABLE_DELETE_ROW:
832 const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
833 if( nRemovedRows == mxTable->getRowCount() )
835 bDeleteTable = true;
837 else
839 Reference< XTableRows > xRows( mxTable->getRows() );
840 xRows->removeByIndex( aStart.mnRow, nRemovedRows );
842 break;
846 if( bDeleteTable )
847 mpView->DeleteMarkedObj();
848 else
849 UpdateTableShape();
855 void SvxTableController::onSelect( sal_uInt16 nSId )
857 if( mxTable.is() )
859 const sal_Int32 nRowCount = mxTable->getRowCount();
860 const sal_Int32 nColCount = mxTable->getColumnCount();
861 if( nRowCount && nColCount )
863 CellPos aStart, aEnd;
864 getSelectedCells( aStart, aEnd );
866 switch( nSId )
868 case SID_TABLE_SELECT_ALL:
869 aEnd.mnCol = 0; aEnd.mnRow = 0;
870 aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
871 break;
872 case SID_TABLE_SELECT_COL:
873 aEnd.mnRow = nRowCount - 1;
874 aStart.mnRow = 0;
875 break;
876 case SID_TABLE_SELECT_ROW:
877 aEnd.mnCol = nColCount - 1;
878 aStart.mnCol = 0;
879 break;
882 StartSelection( aEnd );
883 gotoCell( aStart, true, 0 );
888 namespace
890 SvxBoxItem mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet& rAttrSet)
892 // merge drawing layer text distance items into SvxBoxItem used by the dialog
893 SvxBoxItem aBoxItem( static_cast< const SvxBoxItem& >( rAttrSet.Get( SDRATTR_TABLE_BORDER ) ) );
894 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( static_cast<const SdrMetricItem&>(rAttrSet.Get(SDRATTR_TEXT_LEFTDIST)).GetValue()), SvxBoxItemLine::LEFT );
895 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( static_cast<const SdrMetricItem&>(rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST)).GetValue()), SvxBoxItemLine::RIGHT );
896 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( static_cast<const SdrMetricItem&>(rAttrSet.Get(SDRATTR_TEXT_UPPERDIST)).GetValue()), SvxBoxItemLine::TOP );
897 aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( static_cast<const SdrMetricItem&>(rAttrSet.Get(SDRATTR_TEXT_LOWERDIST)).GetValue()), SvxBoxItemLine::BOTTOM );
898 return aBoxItem;
902 void SvxTableController::onFormatTable( SfxRequest& rReq )
904 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
905 if( !pTableObj )
906 return;
908 const SfxItemSet* pArgs = rReq.GetArgs();
910 if( !pArgs && pTableObj->GetModel() )
912 SfxItemSet aNewAttr( pTableObj->GetModel()->GetItemPool() );
914 // merge drawing layer text distance items into SvxBoxItem used by the dialog
915 SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
917 SvxBoxInfoItem aBoxInfoItem( static_cast< const SvxBoxInfoItem& >( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) ) );
919 MergeAttrFromSelectedCells(aNewAttr, false);
920 FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
921 aNewAttr.Put( aBoxItem );
922 aNewAttr.Put( aBoxInfoItem );
924 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
925 std::unique_ptr< SfxAbstractTabDialog > xDlg( pFact ? pFact->CreateSvxFormatCellsDialog( NULL, &aNewAttr, pTableObj->GetModel(), pTableObj) : 0 );
926 // Even Cancel Button is returning positive(101) value,
927 if (xDlg.get() && xDlg->Execute() == RET_OK)
929 SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
931 //Only properties that were unchanged by the dialog appear in this
932 //itemset. We had constructed these two properties from other
933 //ones, so if they were not changed, then forcible set them back to
934 //their originals in the new result set so we can decompose that
935 //unchanged state back to their input properties
936 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
938 aNewSet.Put(aBoxItem);
940 if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
942 aNewSet.Put(aBoxInfoItem);
945 SvxBoxItem aNewBoxItem( static_cast< const SvxBoxItem& >( aNewSet.Get( SDRATTR_TABLE_BORDER ) ) );
947 if( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) != aBoxItem.GetDistance( SvxBoxItemLine::LEFT ) )
948 aNewSet.Put(makeSdrTextLeftDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) ) );
950 if( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) != aBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) )
951 aNewSet.Put(makeSdrTextRightDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) ) );
953 if( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) != aBoxItem.GetDistance( SvxBoxItemLine::TOP ) )
954 aNewSet.Put(makeSdrTextUpperDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) ) );
956 if( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) != aBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
957 aNewSet.Put(makeSdrTextLowerDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) ) );
959 SetAttrToSelectedCells(aNewSet, false);
964 void SvxTableController::Execute( SfxRequest& rReq )
966 const sal_uInt16 nSId = rReq.GetSlot();
967 switch( nSId )
969 case SID_TABLE_INSERT_ROW:
970 case SID_TABLE_INSERT_COL:
971 onInsert( nSId, rReq.GetArgs() );
972 break;
973 case SID_TABLE_DELETE_ROW:
974 case SID_TABLE_DELETE_COL:
975 onDelete( nSId );
976 break;
977 case SID_TABLE_SELECT_ALL:
978 case SID_TABLE_SELECT_COL:
979 case SID_TABLE_SELECT_ROW:
980 onSelect( nSId );
981 break;
982 case SID_FORMAT_TABLE_DLG:
983 onFormatTable( rReq );
984 break;
986 case SID_FRAME_LINESTYLE:
987 case SID_FRAME_LINECOLOR:
988 case SID_ATTR_BORDER:
990 const SfxItemSet* pArgs = rReq.GetArgs();
991 if( pArgs )
992 ApplyBorderAttr( *pArgs );
994 break;
996 case SID_ATTR_FILL_STYLE:
998 const SfxItemSet* pArgs = rReq.GetArgs();
999 if( pArgs )
1000 SetAttributes( *pArgs, false );
1002 break;
1004 case SID_TABLE_MERGE_CELLS:
1005 MergeMarkedCells();
1006 break;
1008 case SID_TABLE_SPLIT_CELLS:
1009 SplitMarkedCells();
1010 break;
1012 case SID_TABLE_DISTRIBUTE_COLUMNS:
1013 DistributeColumns();
1014 break;
1016 case SID_TABLE_DISTRIBUTE_ROWS:
1017 DistributeRows();
1018 break;
1020 case SID_TABLE_VERT_BOTTOM:
1021 case SID_TABLE_VERT_CENTER:
1022 case SID_TABLE_VERT_NONE:
1023 SetVertical( nSId );
1024 break;
1026 case SID_AUTOFORMAT:
1027 case SID_TABLE_SORT_DIALOG:
1028 case SID_TABLE_AUTOSUM:
1029 default:
1030 break;
1032 case SID_TABLE_STYLE:
1033 SetTableStyle( rReq.GetArgs() );
1034 break;
1036 case SID_TABLE_STYLE_SETTINGS:
1037 SetTableStyleSettings( rReq.GetArgs() );
1038 break;
1042 void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
1044 SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1045 SdrModel* pModel = pTableObj ? pTableObj->GetModel() : 0;
1047 if( !pTableObj || !pModel || !pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)) )
1048 return;
1050 const SfxStringItem* pArg = dynamic_cast< const SfxStringItem* >( &pArgs->Get( SID_TABLE_STYLE ) );
1051 if( pArg && mxTable.is() ) try
1053 Reference< XStyleFamiliesSupplier > xSFS( pModel->getUnoModel(), UNO_QUERY_THROW );
1054 Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_QUERY_THROW );
1055 const OUString sFamilyName( "table" );
1056 Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( sFamilyName ), UNO_QUERY_THROW );
1058 if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
1060 // found table style with the same name
1061 Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
1063 const bool bUndo = pModel->IsUndoEnabled();
1065 if( bUndo )
1067 pModel->BegUndo( ImpGetResStr(STR_TABLE_STYLE) );
1068 pModel->AddUndo( new TableStyleUndo( *pTableObj ) );
1071 pTableObj->setTableStyle( xNewTableStyle );
1073 const sal_Int32 nRowCount = mxTable->getRowCount();
1074 const sal_Int32 nColCount = mxTable->getColumnCount();
1075 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1077 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
1079 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1080 if( xCell.is() )
1082 SfxItemSet aSet( xCell->GetItemSet() );
1083 bool bChanges = false;
1084 SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
1085 SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
1086 if (pStyleSheet)
1088 const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
1090 for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
1092 if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
1094 aSet.ClearItem( nWhich );
1095 bChanges = true;
1100 if( bChanges )
1102 if( bUndo )
1103 xCell->AddUndo();
1105 xCell->SetMergedItemSetAndBroadcast( aSet, true );
1109 catch( Exception& )
1111 OSL_FAIL( "svx::SvxTableController::SetTableStyle(), exception caught!" );
1115 if( bUndo )
1116 pModel->EndUndo();
1119 catch( Exception& )
1121 OSL_FAIL( "svx::SvxTableController::SetTableStyle(), exception caught!" );
1125 void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
1127 SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1128 SdrModel* pModel = pTableObj ? pTableObj->GetModel() : 0;
1130 if( !pTableObj || !pModel )
1131 return;
1133 TableStyleSettings aSettings( pTableObj->getTableStyleSettings() );
1135 const SfxPoolItem *pPoolItem=NULL;
1137 if( (SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTROWSTYLE, false,&pPoolItem)) )
1138 aSettings.mbUseFirstRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1140 if( (SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTROWSTYLE, false,&pPoolItem)) )
1141 aSettings.mbUseLastRow = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1143 if( (SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGROWSTYLE, false,&pPoolItem)) )
1144 aSettings.mbUseRowBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1146 if( (SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEFIRSTCOLUMNSTYLE, false,&pPoolItem)) )
1147 aSettings.mbUseFirstColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1149 if( (SfxItemState::SET == pArgs->GetItemState(ID_VAL_USELASTCOLUMNSTYLE, false,&pPoolItem)) )
1150 aSettings.mbUseLastColumn = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1152 if( (SfxItemState::SET == pArgs->GetItemState(ID_VAL_USEBANDINGCOLUMNSTYLE, false,&pPoolItem)) )
1153 aSettings.mbUseColumnBanding = static_cast< const SfxBoolItem* >(pPoolItem)->GetValue();
1155 if( aSettings == pTableObj->getTableStyleSettings() )
1156 return;
1158 const bool bUndo = pModel->IsUndoEnabled();
1160 if( bUndo )
1162 pModel->BegUndo( ImpGetResStr(STR_TABLE_STYLE_SETTINGS) );
1163 pModel->AddUndo( new TableStyleUndo( *pTableObj ) );
1166 pTableObj->setTableStyleSettings( aSettings );
1168 if( bUndo )
1169 pModel->EndUndo();
1172 void SvxTableController::SetVertical( sal_uInt16 nSId )
1174 SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1175 if( mxTable.is() && pTableObj )
1177 TableModelNotifyGuard aGuard( mxTable.get() );
1179 CellPos aStart, aEnd;
1180 getSelectedCells( aStart, aEnd );
1182 SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
1184 switch( nSId )
1186 case SID_TABLE_VERT_BOTTOM:
1187 eAdj = SDRTEXTVERTADJUST_BOTTOM;
1188 break;
1189 case SID_TABLE_VERT_CENTER:
1190 eAdj = SDRTEXTVERTADJUST_CENTER;
1191 break;
1192 //case SID_TABLE_VERT_NONE:
1193 default:
1194 break;
1197 SdrTextVertAdjustItem aItem( eAdj );
1199 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1201 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1203 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1204 if( xCell.is() )
1205 xCell->SetMergedItem(aItem);
1209 UpdateTableShape();
1213 void SvxTableController::MergeMarkedCells()
1215 CellPos aStart, aEnd;
1216 getSelectedCells( aStart, aEnd );
1217 SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1218 if( pTableObj )
1220 if( pTableObj->IsTextEditActive() )
1221 mpView->SdrEndTextEdit(true);
1223 TableModelNotifyGuard aGuard( mxTable.get() );
1224 MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
1228 void SvxTableController::SplitMarkedCells()
1230 if( mxTable.is() )
1232 CellPos aStart, aEnd;
1233 getSelectedCells( aStart, aEnd );
1235 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1236 std::unique_ptr< SvxAbstractSplittTableDialog > xDlg( pFact ? pFact->CreateSvxSplittTableDialog( NULL, false, 99, 99 ) : 0 );
1237 if( xDlg.get() && xDlg->Execute() )
1239 const sal_Int32 nCount = xDlg->GetCount() - 1;
1240 if( nCount < 1 )
1241 return;
1243 getSelectedCells( aStart, aEnd );
1245 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
1247 const sal_Int32 nRowCount = mxTable->getRowCount();
1248 const sal_Int32 nColCount = mxTable->getColumnCount();
1251 SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >( mxTableObj.get() );
1252 if( pTableObj )
1254 if( pTableObj->IsTextEditActive() )
1255 mpView->SdrEndTextEdit(true);
1257 TableModelNotifyGuard aGuard( mxTable.get() );
1259 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
1260 if( bUndo )
1262 mpModel->BegUndo( ImpGetResStr(STR_TABLE_SPLIT) );
1263 mpModel->AddUndo( mpModel->GetSdrUndoFactory().CreateUndoGeoObject(*pTableObj) );
1266 if( xDlg->IsHorizontal() )
1268 xRange->split( 0, nCount );
1270 else
1272 xRange->split( nCount, 0 );
1275 if( bUndo )
1276 mpModel->EndUndo();
1278 aEnd.mnRow += mxTable->getRowCount() - nRowCount;
1279 aEnd.mnCol += mxTable->getColumnCount() - nColCount;
1281 setSelectedCells( aStart, aEnd );
1286 void SvxTableController::DistributeColumns()
1288 SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >( mxTableObj.get() );
1289 if( pTableObj )
1291 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
1292 if( bUndo )
1294 mpModel->BegUndo( ImpGetResStr(STR_TABLE_DISTRIBUTE_COLUMNS) );
1295 mpModel->AddUndo( mpModel->GetSdrUndoFactory().CreateUndoGeoObject(*pTableObj) );
1298 CellPos aStart, aEnd;
1299 getSelectedCells( aStart, aEnd );
1300 pTableObj->DistributeColumns( aStart.mnCol, aEnd.mnCol );
1302 if( bUndo )
1303 mpModel->EndUndo();
1307 void SvxTableController::DistributeRows()
1309 SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >( mxTableObj.get() );
1310 if( pTableObj )
1312 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
1313 if( bUndo )
1315 mpModel->BegUndo( ImpGetResStr(STR_TABLE_DISTRIBUTE_ROWS) );
1316 mpModel->AddUndo( mpModel->GetSdrUndoFactory().CreateUndoGeoObject(*pTableObj) );
1319 CellPos aStart, aEnd;
1320 getSelectedCells( aStart, aEnd );
1321 pTableObj->DistributeRows( aStart.mnRow, aEnd.mnRow );
1323 if( bUndo )
1324 mpModel->EndUndo();
1328 bool SvxTableController::DeleteMarked()
1330 if( mbCellSelectionMode )
1332 if( mxTable.is() )
1334 CellPos aStart, aEnd;
1335 getSelectedCells( aStart, aEnd );
1336 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1338 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1340 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1341 if( xCell.is() )
1342 xCell->SetOutlinerParaObject( 0 );
1346 UpdateTableShape();
1347 return true;
1351 return false;
1354 bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
1356 if( hasSelectedCells() )
1358 rpStyleSheet = 0;
1360 if( mxTable.is() )
1362 SfxStyleSheet* pRet=0;
1363 bool b1st=true;
1365 CellPos aStart, aEnd;
1366 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
1368 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1370 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1372 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1373 if( xCell.is() )
1375 SfxStyleSheet* pSS=xCell->GetStyleSheet();
1376 if(b1st)
1378 pRet=pSS;
1380 else if(pRet != pSS)
1382 return true;
1384 b1st=false;
1388 rpStyleSheet = pRet;
1389 return true;
1392 return false;
1395 bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
1397 if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SFX_STYLE_FAMILY_FRAME) )
1399 if( mxTable.is() )
1401 CellPos aStart, aEnd;
1402 getSelectedCells( aStart, aEnd );
1404 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
1406 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
1408 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
1409 if( xCell.is() )
1410 xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
1414 UpdateTableShape();
1415 return true;
1418 return false;
1422 // internals
1425 bool SvxTableController::checkTableObject()
1427 return mxTableObj.is();
1432 sal_uInt16 SvxTableController::getKeyboardAction( const KeyEvent& rKEvt, vcl::Window* /*pWindow*/ )
1434 const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
1435 const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
1437 const bool bTextEdit = mpView->IsTextEdit();
1439 sal_uInt16 nAction = ACTION_HANDLED_BY_VIEW;
1441 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1442 if( !pTableObj )
1443 return nAction;
1445 // handle special keys
1446 const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
1447 switch( nCode )
1449 case awt::Key::ESCAPE: // handle escape
1451 if( bTextEdit )
1453 // escape during text edit ends text edit
1454 nAction = ACTION_STOP_TEXT_EDIT;
1456 if( mbCellSelectionMode )
1458 // escape with selected cells removes selection
1459 nAction = ACTION_REMOVE_SELECTION;
1461 break;
1463 case awt::Key::RETURN: // handle return
1465 if( !bMod1 && !bMod2 && !bTextEdit )
1467 // when not already editing, return starts text edit
1468 setSelectionStart( SdrTableObj::getFirstCell() );
1469 nAction = ACTION_EDIT_CELL;
1471 break;
1473 case awt::Key::F2: // f2 toggles text edit
1475 if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
1478 else if( bTextEdit )
1480 // f2 during text edit stops text edit
1481 nAction = ACTION_STOP_TEXT_EDIT;
1483 else if( mbCellSelectionMode )
1485 // f2 with selected cells removes selection
1486 nAction = ACTION_REMOVE_SELECTION;
1488 else
1490 // f2 with no selection and no text edit starts text edit
1491 setSelectionStart( SdrTableObj::getFirstCell() );
1492 nAction = ACTION_EDIT_CELL;
1494 break;
1496 case awt::Key::HOME:
1497 case awt::Key::NUM7:
1499 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1501 if( bMod1 && !bMod2 )
1503 // ctrl + home jumps to first cell
1504 nAction = ACTION_GOTO_FIRST_CELL;
1506 else if( !bMod1 && bMod2 )
1508 // alt + home jumps to first column
1509 nAction = ACTION_GOTO_FIRST_COLUMN;
1512 break;
1514 case awt::Key::END:
1515 case awt::Key::NUM1:
1517 if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
1519 if( bMod1 && !bMod2 )
1521 // ctrl + end jumps to last cell
1522 nAction = ACTION_GOTO_LAST_CELL;
1524 else if( !bMod1 && bMod2 )
1526 // alt + home jumps to last column
1527 nAction = ACTION_GOTO_LAST_COLUMN;
1530 break;
1533 case awt::Key::TAB:
1535 if( bTextEdit || mbCellSelectionMode )
1536 nAction = ACTION_TAB;
1537 break;
1540 case awt::Key::UP:
1541 case awt::Key::NUM8:
1542 case awt::Key::DOWN:
1543 case awt::Key::NUM2:
1544 case awt::Key::LEFT:
1545 case awt::Key::NUM4:
1546 case awt::Key::RIGHT:
1547 case awt::Key::NUM6:
1549 bool bTextMove = false;
1551 if( !bMod1 && bMod2 )
1553 if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
1555 nAction = ACTION_GOTO_LEFT_CELL;
1557 else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
1559 nAction = ACTION_GOTO_RIGHT_CELL;
1561 break;
1564 if( !bTextMove )
1566 OutlinerView* pOLV = mpView->GetTextEditOutlinerView();
1567 if( pOLV )
1569 RemoveSelection();
1570 // during text edit, check if we navigate out of the cell
1571 ESelection aOldSelection = pOLV->GetSelection();
1572 pOLV->PostKeyEvent(rKEvt);
1573 bTextMove = pOLV && ( aOldSelection.IsEqual(pOLV->GetSelection()) );
1574 if( !bTextMove )
1576 nAction = ACTION_NONE;
1581 if( mbCellSelectionMode || bTextMove )
1583 // no text edit, navigate in cells if selection active
1584 switch( nCode )
1586 case awt::Key::LEFT:
1587 case awt::Key::NUM4:
1588 nAction = ACTION_GOTO_LEFT_CELL;
1589 break;
1590 case awt::Key::RIGHT:
1591 case awt::Key::NUM6:
1592 nAction = ACTION_GOTO_RIGHT_CELL;
1593 break;
1594 case awt::Key::DOWN:
1595 case awt::Key::NUM2:
1596 nAction = ACTION_GOTO_DOWN_CELL;
1597 break;
1598 case awt::Key::UP:
1599 case awt::Key::NUM8:
1600 nAction = ACTION_GOTO_UP_CELL;
1601 break;
1604 break;
1606 case awt::Key::PAGEUP:
1607 if( bMod2 )
1608 nAction = ACTION_GOTO_FIRST_ROW;
1609 break;
1611 case awt::Key::PAGEDOWN:
1612 if( bMod2 )
1613 nAction = ACTION_GOTO_LAST_ROW;
1614 break;
1616 return nAction;
1619 bool SvxTableController::executeAction( sal_uInt16 nAction, bool bSelect, vcl::Window* pWindow )
1621 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1622 if( !pTableObj )
1623 return false;
1625 switch( nAction )
1627 case ACTION_GOTO_FIRST_CELL:
1629 gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
1630 break;
1633 case ACTION_GOTO_LEFT_CELL:
1635 gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
1636 break;
1639 case ACTION_GOTO_RIGHT_CELL:
1641 gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
1642 break;
1645 case ACTION_GOTO_LAST_CELL:
1647 gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
1648 break;
1651 case ACTION_GOTO_FIRST_COLUMN:
1653 CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
1654 gotoCell( aPos, bSelect, pWindow, nAction );
1655 break;
1658 case ACTION_GOTO_LAST_COLUMN:
1660 CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
1661 gotoCell( aPos, bSelect, pWindow, nAction );
1662 break;
1665 case ACTION_GOTO_FIRST_ROW:
1667 CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
1668 gotoCell( aPos, bSelect, pWindow, nAction );
1669 break;
1672 case ACTION_GOTO_UP_CELL:
1674 gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1675 break;
1678 case ACTION_GOTO_DOWN_CELL:
1680 gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
1681 break;
1684 case ACTION_GOTO_LAST_ROW:
1686 CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
1687 gotoCell( aPos, bSelect, pWindow, nAction );
1688 break;
1691 case ACTION_EDIT_CELL:
1692 EditCell( getSelectionStart(), pWindow, 0, nAction );
1693 break;
1695 case ACTION_STOP_TEXT_EDIT:
1696 StopTextEdit();
1697 break;
1699 case ACTION_REMOVE_SELECTION:
1700 RemoveSelection();
1701 break;
1703 case ACTION_START_SELECTION:
1704 StartSelection( getSelectionStart() );
1705 break;
1707 case ACTION_TAB:
1709 if( bSelect )
1710 gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
1711 else
1713 CellPos aSelectionEnd( getSelectionEnd() );
1714 CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
1715 if( aSelectionEnd == aNextCell )
1717 onInsert( SID_TABLE_INSERT_ROW, 0 );
1718 aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
1720 gotoCell( aNextCell, false, pWindow, nAction );
1722 break;
1726 return nAction != ACTION_HANDLED_BY_VIEW;
1731 void SvxTableController::gotoCell( const CellPos& rPos, bool bSelect, vcl::Window* pWindow, sal_uInt16 nAction )
1733 if( mxTableObj.is() && static_cast<SdrTableObj*>(mxTableObj.get())->IsTextEditActive() )
1734 mpView->SdrEndTextEdit(true);
1736 if( bSelect )
1738 maCursorLastPos = rPos;
1739 if( mxTableObj.is() )
1740 static_cast< SdrTableObj* >( mxTableObj.get() )->setActiveCell( rPos );
1742 if( !mbCellSelectionMode )
1744 setSelectedCells( maCursorFirstPos, rPos );
1746 else
1748 UpdateSelection( rPos );
1751 else
1753 RemoveSelection();
1754 EditCell( rPos, pWindow, 0, nAction );
1760 const CellPos& SvxTableController::getSelectionStart()
1762 checkCell( maCursorFirstPos );
1763 return maCursorFirstPos;
1768 void SvxTableController::setSelectionStart( const CellPos& rPos )
1770 maCursorFirstPos = rPos;
1775 const CellPos& SvxTableController::getSelectionEnd()
1777 checkCell( maCursorLastPos );
1778 return maCursorLastPos;
1783 void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
1785 if( mxTable.is() ) try
1787 Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
1788 if( xRange->isMergeable() )
1790 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
1791 if( bUndo )
1793 mpModel->BegUndo( ImpGetResStr(STR_TABLE_MERGE) );
1794 mpModel->AddUndo( mpModel->GetSdrUndoFactory().CreateUndoGeoObject(*mxTableObj.get()) );
1797 xRange->merge();
1799 if( bUndo )
1800 mpModel->EndUndo();
1803 catch( Exception& )
1805 DBG_ASSERT( false, "sdr::table::SvxTableController::MergeRange(), exception caught!" );
1813 void SvxTableController::checkCell( CellPos& rPos )
1815 if( mxTable.is() ) try
1817 if( rPos.mnCol >= mxTable->getColumnCount() )
1818 rPos.mnCol = mxTable->getColumnCount()-1;
1820 if( rPos.mnRow >= mxTable->getRowCount() )
1821 rPos.mnRow = mxTable->getRowCount()-1;
1823 catch( Exception& )
1825 OSL_FAIL("sdr::table::SvxTableController::checkCell(), exception caught!" );
1831 void SvxTableController::findMergeOrigin( CellPos& rPos )
1833 if( mxTable.is() ) try
1835 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
1836 if( xCell.is() && xCell->isMerged() )
1838 ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
1841 catch( Exception& )
1843 OSL_FAIL("sdr::table::SvxTableController::findMergeOrigin(), exception caught!" );
1849 void SvxTableController::EditCell( const CellPos& rPos, vcl::Window* pWindow, const awt::MouseEvent* pMouseEvent /*= 0*/, sal_uInt16 nAction /*= ACTION_NONE */ )
1851 SdrPageView* pPV = mpView->GetSdrPageView();
1853 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
1854 if( pTableObj && pTableObj->GetPage() == pPV->GetPage() )
1856 bool bEmptyOutliner = false;
1858 if(!pTableObj->GetOutlinerParaObject() && mpView->GetTextEditOutliner())
1860 ::Outliner* pOutl = mpView->GetTextEditOutliner();
1861 sal_Int32 nParaAnz = pOutl->GetParagraphCount();
1862 Paragraph* p1stPara = pOutl->GetParagraph( 0 );
1864 if(nParaAnz==1 && p1stPara)
1866 // Bei nur einem Pararaph
1867 if (pOutl->GetText(p1stPara).isEmpty())
1869 bEmptyOutliner = true;
1874 CellPos aPos( rPos );
1875 findMergeOrigin( aPos );
1877 if( pTableObj != mpView->GetTextEditObject() || bEmptyOutliner || !pTableObj->IsTextEditActive( aPos ) )
1879 if( pTableObj->IsTextEditActive() )
1880 mpView->SdrEndTextEdit(true);
1882 pTableObj->setActiveCell( aPos );
1884 // create new outliner, owner will be the SdrObjEditView
1885 SdrOutliner* pOutl = mpModel ? SdrMakeOutliner(OUTLINERMODE_OUTLINEOBJECT, *mpModel) : NULL;
1886 if (pOutl && pTableObj->IsVerticalWriting())
1887 pOutl->SetVertical( true );
1889 if (mpView->SdrBeginTextEdit(pTableObj, pPV, pWindow, true, pOutl))
1891 maCursorLastPos = maCursorFirstPos = rPos;
1893 OutlinerView* pOLV = mpView->GetTextEditOutlinerView();
1895 bool bNoSel = true;
1897 if( pMouseEvent )
1899 ::MouseEvent aMEvt( *pMouseEvent );
1901 SdrViewEvent aVEvt;
1902 SdrHitKind eHit = mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt);
1904 if (eHit == SDRHIT_TEXTEDIT)
1906 // Text getroffen
1907 pOLV->MouseButtonDown(aMEvt);
1908 pOLV->MouseMove(aMEvt);
1909 pOLV->MouseButtonUp(aMEvt);
1910 // pOLV->MouseButtonDown(aMEvt);
1911 bNoSel = false;
1913 else
1915 nAction = ACTION_GOTO_LEFT_CELL;
1919 if( bNoSel )
1921 // Move cursor to end of text
1922 ESelection aNewSelection;
1924 const WritingMode eMode = pTableObj->GetWritingMode();
1925 if( ((nAction == ACTION_GOTO_LEFT_CELL) || (nAction == ACTION_GOTO_RIGHT_CELL)) && (eMode != WritingMode_TB_RL) )
1927 const bool bLast = ((nAction == ACTION_GOTO_LEFT_CELL) && (eMode == WritingMode_LR_TB)) ||
1928 ((nAction == ACTION_GOTO_RIGHT_CELL) && (eMode == WritingMode_RL_TB));
1930 if( bLast )
1931 aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
1933 pOLV->SetSelection(aNewSelection);
1942 bool SvxTableController::StopTextEdit()
1944 if(mpView->IsTextEdit())
1946 mpView->SdrEndTextEdit();
1947 mpView->SetCurrentObj(OBJ_TABLE);
1948 mpView->SetEditMode(SDREDITMODE_EDIT);
1949 return true;
1951 else
1953 return false;
1959 void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
1961 if( mbCellSelectionMode )
1963 checkCell( maCursorFirstPos );
1964 checkCell( maCursorLastPos );
1966 rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
1967 rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
1968 rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
1969 rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
1971 bool bExt = false;
1972 if( mxTable.is() ) do
1974 bExt = false;
1975 for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
1977 for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
1979 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
1980 if( !xCell.is() )
1981 continue;
1983 if( xCell->isMerged() )
1985 CellPos aPos( nCol, nRow );
1986 findMergeOrigin( aPos );
1987 if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
1989 rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
1990 rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
1991 bExt = true;
1994 else
1996 if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
1998 rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
1999 rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
2000 bExt = true;
2006 while(bExt);
2008 else if( mpView && mpView->IsTextEdit() )
2010 rFirst = getSelectionStart();
2011 findMergeOrigin( rFirst );
2012 rLast = rFirst;
2014 if( mxTable.is() )
2016 Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
2017 if( xCell.is() )
2019 rLast.mnCol += xCell->getColumnSpan() - 1;
2020 rLast.mnRow += xCell->getRowSpan() - 1;
2024 else
2026 rFirst.mnCol = 0;
2027 rFirst.mnRow = 0;
2028 if( mxTable.is() )
2030 rLast.mnRow = mxTable->getRowCount()-1;
2031 rLast.mnCol = mxTable->getColumnCount()-1;
2033 else
2035 rLast.mnRow = 0;
2036 rLast.mnCol = 0;
2043 void SvxTableController::StartSelection( const CellPos& rPos )
2045 StopTextEdit();
2046 mbCellSelectionMode = true;
2047 maCursorLastPos = maCursorFirstPos = rPos;
2048 mpView->MarkListHasChanged();
2053 void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
2055 StopTextEdit();
2056 mbCellSelectionMode = true;
2057 maCursorFirstPos = rStart;
2058 UpdateSelection( rEnd );
2063 void SvxTableController::UpdateSelection( const CellPos& rPos )
2065 maCursorLastPos = rPos;
2066 mpView->MarkListHasChanged();
2071 void SvxTableController::clearSelection()
2073 RemoveSelection();
2078 void SvxTableController::selectAll()
2080 if( mxTable.is() )
2082 CellPos aPos1, aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
2083 if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
2085 setSelectedCells( aPos1, aPos2 );
2092 void SvxTableController::RemoveSelection()
2094 if( mbCellSelectionMode )
2096 mbCellSelectionMode = false;
2097 mpView->MarkListHasChanged();
2103 void SvxTableController::onTableModified()
2105 if( mnUpdateEvent == 0 )
2106 mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
2110 void SvxTableController::updateSelectionOverlay()
2112 destroySelectionOverlay();
2113 if( mbCellSelectionMode )
2115 sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
2116 if( pTableObj )
2118 sdr::overlay::OverlayObjectCell::RangeVector aRanges;
2120 Rectangle aStartRect, aEndRect;
2121 CellPos aStart,aEnd;
2122 getSelectedCells( aStart, aEnd );
2123 pTableObj->getCellBounds( aStart, aStartRect );
2125 basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
2126 a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
2128 findMergeOrigin( aEnd );
2129 pTableObj->getCellBounds( aEnd, aEndRect );
2130 a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
2131 a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
2132 aRanges.push_back( a2DRange );
2134 ::Color aHighlight( COL_BLUE );
2135 OutputDevice* pOutDev = mpView->GetFirstOutputDevice();
2136 if( pOutDev )
2137 aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
2139 const sal_uInt32 nCount = mpView->PaintWindowCount();
2140 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
2142 SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(nIndex);
2143 if( pPaintWindow )
2145 rtl::Reference < sdr::overlay::OverlayManager > xOverlayManager = pPaintWindow->GetOverlayManager();
2146 if( xOverlayManager.is() )
2148 // sdr::overlay::CellOverlayType eType = sdr::overlay::CELL_OVERLAY_INVERT;
2149 sdr::overlay::CellOverlayType eType = sdr::overlay::CELL_OVERLAY_TRANSPARENT;
2151 sdr::overlay::OverlayObjectCell* pOverlay = new sdr::overlay::OverlayObjectCell( eType, aHighlight, aRanges );
2153 xOverlayManager->add(*pOverlay);
2154 mpSelectionOverlay = new sdr::overlay::OverlayObjectList;
2155 mpSelectionOverlay->append(*pOverlay);
2160 // If tiled rendering, emit callbacks for sdr table selection.
2161 if (pOutDev && pTableObj->GetModel()->isTiledRendering())
2163 // Left edge of aStartRect.
2164 Rectangle aSelectionStart(aStartRect.Left(), aStartRect.Top(), aStartRect.Left(), aStartRect.Bottom());
2165 // Right edge of aEndRect.
2166 Rectangle aSelectionEnd(aEndRect.Right(), aEndRect.Top(), aEndRect.Right(), aEndRect.Bottom());
2167 Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
2169 if (pOutDev->GetMapMode().GetMapUnit() == MAP_100TH_MM)
2171 aSelectionStart = OutputDevice::LogicToLogic(aSelectionStart, MAP_100TH_MM, MAP_TWIP);
2172 aSelectionEnd = OutputDevice::LogicToLogic(aSelectionEnd, MAP_100TH_MM, MAP_TWIP);
2173 aSelection = OutputDevice::LogicToLogic(aSelection, MAP_100TH_MM, MAP_TWIP);
2176 pTableObj->GetModel()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_START, aSelectionStart.toString().getStr());
2177 pTableObj->GetModel()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_END, aSelectionEnd.toString().getStr());
2178 pTableObj->GetModel()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString().getStr());
2186 void SvxTableController::destroySelectionOverlay()
2188 if( mpSelectionOverlay )
2190 delete mpSelectionOverlay;
2191 mpSelectionOverlay = 0;
2193 if (mxTableObj->GetModel()->isTiledRendering())
2195 // Clear the LOK text selection so far provided by this table.
2196 mxTableObj->GetModel()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
2197 mxTableObj->GetModel()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
2198 mxTableObj->GetModel()->libreOfficeKitCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
2205 void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
2207 if( mxTable.is() )
2209 CellPos aStart, aEnd;
2210 const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
2212 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2214 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2216 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2217 if( xCell.is() && !xCell->isMerged() )
2219 const SfxItemSet& rSet = xCell->GetItemSet();
2220 SfxWhichIter aIter(rSet);
2221 sal_uInt16 nWhich(aIter.FirstWhich());
2222 while(nWhich)
2224 if(!bOnlyHardAttr)
2226 if(SfxItemState::DONTCARE == rSet.GetItemState(nWhich, false))
2227 rAttr.InvalidateItem(nWhich);
2228 else
2229 rAttr.MergeValue(rSet.Get(nWhich), true);
2231 else if(SfxItemState::SET == rSet.GetItemState(nWhich, false))
2233 const SfxPoolItem& rItem = rSet.Get(nWhich);
2234 rAttr.MergeValue(rItem, true);
2237 nWhich = aIter.NextWhich();
2247 const sal_uInt16 CELL_BEFORE = 0x0001;
2248 const sal_uInt16 CELL_LEFT = 0x0002;
2249 const sal_uInt16 CELL_RIGHT = 0x0004;
2250 const sal_uInt16 CELL_AFTER = 0x0008;
2252 const sal_uInt16 CELL_UPPER = 0x0010;
2253 const sal_uInt16 CELL_TOP = 0x0020;
2254 const sal_uInt16 CELL_BOTTOM = 0x0040;
2255 const sal_uInt16 CELL_LOWER = 0x0080;
2259 static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
2261 if( pNew )
2263 const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
2264 if( pOld )
2266 SvxBorderLine aNewLine( *pNew );
2267 aNewLine.SetColor( pOld->GetColor() );
2268 rNewFrame.SetLine( &aNewLine, nLine );
2269 return;
2272 rNewFrame.SetLine( pNew, nLine );
2277 static void ImplApplyBoxItem( sal_uInt16 nCellFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
2279 if( (nCellFlags & (CELL_BEFORE|CELL_AFTER|CELL_UPPER|CELL_LOWER)) != 0 )
2281 // current cell is outside the selection
2283 if( (nCellFlags & ( CELL_BEFORE|CELL_AFTER)) == 0 ) // check if its not nw or ne corner
2285 if( nCellFlags & CELL_UPPER )
2287 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
2288 rNewFrame.SetLine(0, SvxBoxItemLine::BOTTOM );
2290 else if( nCellFlags & CELL_LOWER )
2292 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
2293 rNewFrame.SetLine( 0, SvxBoxItemLine::TOP );
2296 else if( (nCellFlags & ( CELL_UPPER|CELL_LOWER)) == 0 ) // check if its not sw or se corner
2298 if( nCellFlags & CELL_BEFORE )
2300 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
2301 rNewFrame.SetLine( 0, SvxBoxItemLine::RIGHT );
2303 else if( nCellFlags & CELL_AFTER )
2305 if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
2306 rNewFrame.SetLine( 0, SvxBoxItemLine::LEFT );
2310 else
2312 // current cell is inside the selection
2314 if( (nCellFlags & CELL_LEFT) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2315 rNewFrame.SetLine( (nCellFlags & CELL_LEFT) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
2317 if( (nCellFlags & CELL_RIGHT) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
2318 rNewFrame.SetLine( (nCellFlags & CELL_RIGHT) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
2320 if( (nCellFlags & CELL_TOP) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2321 rNewFrame.SetLine( (nCellFlags & CELL_TOP) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
2323 if( (nCellFlags & CELL_BOTTOM) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
2324 rNewFrame.SetLine( (nCellFlags & CELL_BOTTOM) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
2326 // apply distance to borders
2327 if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
2328 for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
2329 rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
2335 static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
2337 const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
2338 if( pSourceLine )
2340 SvxBorderLine aLine( *pSourceLine );
2341 aLine.SetColor( rColor );
2342 rNewFrame.SetLine( &aLine, nLine );
2348 static void ImplApplyLineColorItem( sal_uInt16 nCellFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
2350 const Color aColor( pLineColorItem->GetValue() );
2352 if( (nCellFlags & (CELL_LOWER|CELL_BEFORE|CELL_AFTER)) == 0 )
2353 ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
2355 if( (nCellFlags & (CELL_UPPER|CELL_BEFORE|CELL_AFTER)) == 0 )
2356 ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
2358 if( (nCellFlags & (CELL_UPPER|CELL_LOWER|CELL_AFTER)) == 0 )
2359 ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
2361 if( (nCellFlags & (CELL_UPPER|CELL_LOWER|CELL_BEFORE)) == 0 )
2362 ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
2367 static void ImplApplyBorderLineItem( sal_uInt16 nCellFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
2369 if( (nCellFlags & ( CELL_BEFORE|CELL_AFTER|CELL_UPPER|CELL_LOWER)) != 0 )
2371 if( (nCellFlags & ( CELL_BEFORE|CELL_AFTER)) == 0 ) // check if its not nw or ne corner
2373 if( nCellFlags & CELL_UPPER )
2375 if( rNewFrame.GetBottom() )
2376 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2378 else if( nCellFlags & CELL_LOWER )
2380 if( rNewFrame.GetTop() )
2381 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2384 else if( (nCellFlags & ( CELL_UPPER|CELL_LOWER)) == 0 ) // check if its not sw or se corner
2386 if( nCellFlags & CELL_BEFORE )
2388 if( rNewFrame.GetRight() )
2389 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2391 else if( nCellFlags & CELL_AFTER )
2393 if( rNewFrame.GetLeft() )
2394 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2398 else
2400 if( rNewFrame.GetBottom() )
2401 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
2402 if( rNewFrame.GetTop() )
2403 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
2404 if( rNewFrame.GetRight() )
2405 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
2406 if( rNewFrame.GetLeft() )
2407 ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
2413 void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
2415 if( mxTable.is() )
2417 const sal_Int32 nRowCount = mxTable->getRowCount();
2418 const sal_Int32 nColCount = mxTable->getColumnCount();
2419 if( nRowCount && nColCount )
2421 const SvxBoxItem* pBoxItem = 0;
2422 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
2423 pBoxItem = dynamic_cast< const SvxBoxItem* >( &rAttr.Get( SDRATTR_TABLE_BORDER ) );
2425 const SvxBoxInfoItem* pBoxInfoItem = 0;
2426 if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
2427 pBoxInfoItem = dynamic_cast< const SvxBoxInfoItem* >( &rAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
2429 const SvxColorItem* pLineColorItem = 0;
2430 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
2431 pLineColorItem = dynamic_cast< const SvxColorItem* >( &rAttr.Get( SID_FRAME_LINECOLOR ) );
2433 const SvxBorderLine* pBorderLineItem = 0;
2434 if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
2435 pBorderLineItem = static_cast<const SvxLineItem&>(rAttr.Get( SID_FRAME_LINESTYLE )).GetLine();
2437 if( pBoxInfoItem && !pBoxItem )
2439 const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
2440 pBoxItem = &gaEmptyBoxItem;
2442 else if( pBoxItem && !pBoxInfoItem )
2444 const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
2445 pBoxInfoItem = &gaEmptyBoxInfoItem;
2448 CellPos aStart, aEnd;
2449 getSelectedCells( aStart, aEnd );
2451 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
2452 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
2454 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, (sal_Int32)0 ); nRow < nLastRow; nRow++ )
2456 sal_uInt16 nRowFlags = 0;
2457 nRowFlags |= (nRow == aStart.mnRow) ? CELL_TOP : 0;
2458 nRowFlags |= (nRow == aEnd.mnRow) ? CELL_BOTTOM : 0;
2459 nRowFlags |= (nRow < aStart.mnRow) ? CELL_UPPER : 0;
2460 nRowFlags |= (nRow > aEnd.mnRow) ? CELL_LOWER : 0;
2462 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, (sal_Int32)0 ); nCol < nLastCol; nCol++ )
2464 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2465 if( !xCell.is() )
2466 continue;
2468 const SfxItemSet& rSet = xCell->GetItemSet();
2469 const SvxBoxItem* pOldOuter = static_cast<const SvxBoxItem*>(&rSet.Get( SDRATTR_TABLE_BORDER ));
2471 SvxBoxItem aNewFrame( *pOldOuter );
2473 sal_uInt16 nCellFlags = nRowFlags;
2474 nCellFlags |= (nCol == aStart.mnCol) ? CELL_LEFT : 0;
2475 nCellFlags |= (nCol == aEnd.mnCol) ? CELL_RIGHT : 0;
2476 nCellFlags |= (nCol < aStart.mnCol) ? CELL_BEFORE : 0;
2477 nCellFlags |= (nCol > aEnd.mnCol) ? CELL_AFTER : 0;
2479 if( pBoxItem && pBoxInfoItem )
2480 ImplApplyBoxItem( nCellFlags, pBoxItem, pBoxInfoItem, aNewFrame );
2482 if( pLineColorItem )
2483 ImplApplyLineColorItem( nCellFlags, pLineColorItem, aNewFrame );
2485 if( pBorderLineItem )
2486 ImplApplyBorderLineItem( nCellFlags, pBorderLineItem, aNewFrame );
2488 if (aNewFrame != *pOldOuter)
2490 SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
2491 aAttr.Put(aNewFrame);
2492 xCell->SetMergedItemSetAndBroadcast( aAttr, false );
2502 void SvxTableController::UpdateTableShape()
2504 SdrObject* pTableObj = mxTableObj.get();
2505 if( pTableObj )
2507 pTableObj->ActionChanged();
2508 pTableObj->BroadcastObjectChange();
2510 updateSelectionOverlay();
2516 void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
2518 if( mxTable.is() )
2520 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
2522 if( bUndo )
2523 mpModel->BegUndo( ImpGetResStr(STR_TABLE_NUMFORMAT) );
2525 CellPos aStart, aEnd;
2526 getSelectedCells( aStart, aEnd );
2528 SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
2529 aAttr.Put(rAttr, true);
2531 const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2533 if( bFrame )
2535 aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2536 aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2539 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2541 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2543 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2544 if( xCell.is() )
2546 if( bUndo )
2547 xCell->AddUndo();
2548 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2553 if( bFrame )
2555 ApplyBorderAttr( rAttr );
2558 UpdateTableShape();
2560 if( bUndo )
2561 mpModel->EndUndo();
2568 bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
2570 if( mxTableObj.is() && hasSelectedCells() )
2572 MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
2574 if( mpView->IsTextEdit() )
2576 if( mxTableObj->GetOutlinerParaObject() )
2577 rTargetSet.Put( SvxScriptTypeItem( mxTableObj->GetOutlinerParaObject()->GetTextObject().GetScriptType() ) );
2579 OutlinerView* pTextEditOutlinerView = mpView->GetTextEditOutlinerView();
2580 if(pTextEditOutlinerView)
2582 // FALSE= InvalidItems nicht al Default, sondern als "Loecher" betrachten
2583 rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
2584 rTargetSet.Put( SvxScriptTypeItem( pTextEditOutlinerView->GetSelectedScriptType() ) );
2588 return true;
2590 else
2592 return false;
2598 bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
2600 if( mbCellSelectionMode || mpView->IsTextEdit() )
2602 SetAttrToSelectedCells( rSet, bReplaceAll );
2603 return true;
2605 return false;
2610 bool SvxTableController::GetMarkedObjModel( SdrPage* pNewPage )
2612 if( mxTableObj.is() && mbCellSelectionMode && pNewPage ) try
2614 sdr::table::SdrTableObj& rTableObj = *static_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
2616 CellPos aStart, aEnd;
2617 getSelectedCells( aStart, aEnd );
2619 SdrTableObj* pNewTableObj = rTableObj.CloneRange( aStart, aEnd );
2621 pNewTableObj->SetPage( pNewPage );
2622 pNewTableObj->SetModel( pNewPage->GetModel() );
2624 SdrInsertReason aReason(SDRREASON_VIEWCALL);
2625 pNewPage->InsertObject(pNewTableObj, SAL_MAX_SIZE, &aReason);
2627 return true;
2629 catch( Exception& )
2631 OSL_FAIL( "svx::SvxTableController::GetMarkedObjModel(), exception caught!" );
2633 return false;
2638 bool SvxTableController::PasteObjModel( const SdrModel& rModel )
2640 if( mxTableObj.is() && mpView && (rModel.GetPageCount() >= 1) )
2642 const SdrPage* pPastePage = rModel.GetPage(0);
2643 if( pPastePage && pPastePage->GetObjCount() == 1 )
2645 SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
2646 if( pPasteTableObj )
2648 return PasteObject( pPasteTableObj );
2653 return false;
2658 bool SvxTableController::PasteObject( SdrTableObj* pPasteTableObj )
2660 if( !pPasteTableObj )
2661 return false;
2663 Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
2664 if( !xPasteTable.is() )
2665 return false;
2667 if( !mxTable.is() )
2668 return false;
2670 sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
2671 sal_Int32 nPasteRows = xPasteTable->getRowCount();
2673 CellPos aStart, aEnd;
2674 getSelectedCells( aStart, aEnd );
2676 if( mpView->IsTextEdit() )
2677 mpView->SdrEndTextEdit(true);
2679 sal_Int32 nColumns = mxTable->getColumnCount();
2680 sal_Int32 nRows = mxTable->getRowCount();
2682 const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
2683 if( nMissing > 0 )
2685 Reference< XTableRows > xRows( mxTable->getRows() );
2686 xRows->insertByIndex( nRows, nMissing );
2687 nRows = mxTable->getRowCount();
2690 nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
2691 nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
2693 // copy cell contents
2694 for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
2696 for( sal_Int32 nCol = 0; nCol < nPasteColumns; ++nCol )
2698 CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( aStart.mnCol + nCol, aStart.mnRow + nRow ).get() ) );
2699 if( xTargetCell.is() && !xTargetCell->isMerged() )
2701 xTargetCell->AddUndo();
2702 xTargetCell->cloneFrom( dynamic_cast< Cell* >( xPasteTable->getCellByPosition( nCol, nRow ).get() ) );
2703 nCol += xTargetCell->getColumnSpan() - 1;
2708 UpdateTableShape();
2710 return true;
2713 bool SvxTableController::TakeFormatPaintBrush( std::shared_ptr< SfxItemSet >& /*rFormatSet*/ )
2715 // SdrView::TakeFormatPaintBrush() is enough
2716 return false;
2719 bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
2721 if( mbCellSelectionMode )
2723 SdrTextObj* pTableObj = dynamic_cast<SdrTextObj*>( mxTableObj.get() );
2724 if( !pTableObj )
2725 return false;
2727 const bool bUndo = mpModel && mpModel->IsUndoEnabled();
2729 if( bUndo )
2730 mpModel->BegUndo( ImpGetResStr(STR_TABLE_NUMFORMAT) );
2732 CellPos aStart, aEnd;
2733 getSelectedCells( aStart, aEnd );
2735 SfxItemSet aAttr(*rFormatSet.GetPool(), rFormatSet.GetRanges());
2736 aAttr.Put(rFormatSet, true);
2738 const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
2740 if( bFrame )
2742 aAttr.ClearItem( SDRATTR_TABLE_BORDER );
2743 aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
2746 const sal_uInt16* pRanges = rFormatSet.GetRanges();
2747 bool bTextOnly = true;
2749 while( *pRanges )
2751 if( (*pRanges != EE_PARA_START) && (*pRanges != EE_CHAR_START) )
2753 bTextOnly = false;
2754 break;
2756 pRanges += 2;
2759 const bool bReplaceAll = false;
2760 for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
2762 for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
2764 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
2765 if( xCell.is() )
2767 if( bUndo )
2768 xCell->AddUndo();
2769 if( !bTextOnly )
2770 xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
2772 SdrText* pText = static_cast< SdrText* >( xCell.get() );
2773 SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, *pTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
2778 if( bFrame )
2780 ApplyBorderAttr( rFormatSet );
2783 UpdateTableShape();
2785 if( bUndo )
2786 mpModel->EndUndo();
2788 return true;
2791 return false;
2797 IMPL_LINK_NOARG(SvxTableController, UpdateHdl)
2799 mnUpdateEvent = 0;
2801 if( mbCellSelectionMode )
2803 CellPos aStart( maCursorFirstPos );
2804 CellPos aEnd( maCursorLastPos );
2805 checkCell(aStart);
2806 checkCell(aEnd);
2807 if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
2809 setSelectedCells( aStart, aEnd );
2812 updateSelectionOverlay();
2814 return 0;
2817 namespace
2820 struct LinesState
2822 LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
2823 : rBoxItem(rBoxItem_)
2824 , rBoxInfoItem(rBoxInfoItem_)
2825 , bDistanceIndeterminate(false)
2827 aBorderSet.fill(false);
2828 aInnerLineSet.fill(false);
2829 aBorderIndeterminate.fill(false);
2830 aInnerLineIndeterminate.fill(false);
2831 aDistanceSet.fill(false);
2832 aDistance.fill(0);
2835 SvxBoxItem& rBoxItem;
2836 SvxBoxInfoItem& rBoxInfoItem;
2837 o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
2838 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
2839 o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
2840 o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
2841 o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
2842 o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
2843 bool bDistanceIndeterminate;
2846 class BoxItemWrapper
2848 public:
2849 BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
2851 const SvxBorderLine* getLine() const;
2852 void setLine(const SvxBorderLine* pLine);
2854 private:
2855 SvxBoxItem& m_rBoxItem;
2856 SvxBoxInfoItem& m_rBoxInfoItem;
2857 const SvxBoxItemLine m_nBorderLine;
2858 const SvxBoxInfoItemLine m_nInnerLine;
2859 const bool m_bBorder;
2862 BoxItemWrapper::BoxItemWrapper(
2863 SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
2864 const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
2865 : m_rBoxItem(rBoxItem)
2866 , m_rBoxInfoItem(rBoxInfoItem)
2867 , m_nBorderLine(nBorderLine)
2868 , m_nInnerLine(nInnerLine)
2869 , m_bBorder(bBorder)
2873 const SvxBorderLine* BoxItemWrapper::getLine() const
2875 if (m_bBorder)
2876 return m_rBoxItem.GetLine(m_nBorderLine);
2877 else
2878 return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
2881 void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
2883 if (m_bBorder)
2884 m_rBoxItem.SetLine(pLine, m_nBorderLine);
2885 else
2886 m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
2889 void lcl_MergeBorderLine(
2890 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
2891 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
2893 const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
2894 BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
2895 bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
2897 if (rbSet)
2899 bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
2900 if (!rbIndeterminate)
2902 const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
2903 if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
2905 aBoxItem.setLine(0);
2906 rbIndeterminate = true;
2910 else
2912 aBoxItem.setLine(pLine);
2913 rbSet = true;
2917 void lcl_MergeBorderOrInnerLine(
2918 LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
2919 SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
2921 if (bBorder)
2922 lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
2923 else
2925 const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
2926 lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
2930 void lcl_MergeDistance(
2931 LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
2933 if (rLinesState.aDistanceSet[nIndex])
2935 if (!rLinesState.bDistanceIndeterminate)
2936 rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
2938 else
2940 rLinesState.aDistance[nIndex] = nDistance;
2941 rLinesState.aDistanceSet[nIndex] = true;
2945 void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const sal_Int32 nCellFlags)
2947 if( (nCellFlags & (CELL_BEFORE|CELL_AFTER|CELL_UPPER|CELL_LOWER)) != 0 )
2949 // current cell is outside the selection
2951 if( (nCellFlags & ( CELL_BEFORE|CELL_AFTER)) == 0 ) // check if its not nw or ne corner
2953 if( nCellFlags & CELL_UPPER )
2954 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
2955 else if( nCellFlags & CELL_LOWER )
2956 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
2958 else if( (nCellFlags & ( CELL_UPPER|CELL_LOWER)) == 0 ) // check if its not sw or se corner
2960 if( nCellFlags & CELL_BEFORE )
2961 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
2962 else if( nCellFlags & CELL_AFTER )
2963 lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
2966 // NOTE: inner distances for cells outside the selected range
2967 // are not relevant -> we ignore them.
2969 else
2971 // current cell is inside the selection
2973 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, (nCellFlags & CELL_TOP) != 0);
2974 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, (nCellFlags & CELL_BOTTOM) != 0);
2975 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, (nCellFlags & CELL_LEFT) != 0);
2976 lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, (nCellFlags & CELL_RIGHT) != 0);
2978 lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
2979 lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
2980 lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
2981 lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
2987 void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
2989 if( mxTable.is() )
2991 const sal_Int32 nRowCount = mxTable->getRowCount();
2992 const sal_Int32 nColCount = mxTable->getColumnCount();
2993 if( nRowCount && nColCount )
2995 CellPos aStart, aEnd;
2996 const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
2998 // We are adding one more row/column around the block of selected cells.
2999 // We will be checking the adjoining border of these too.
3000 const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
3001 const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
3003 rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
3004 LinesState aLinesState( rBoxItem, rBoxInfoItem );
3006 /* Here we go through all the selected cells (enhanced by
3007 * the adjoining row/column on each side) and determine the
3008 * lines for presentation. The algorithm is simple:
3009 * 1. if a border or inner line is set (or unset) in all
3010 * cells to the same value, it will be used.
3011 * 2. if a border or inner line is set only in some cells,
3012 * it will be set to indeterminate state (SetValid() on
3013 * rBoxInfoItem).
3015 for( sal_Int32 nRow = std::max( aStart.mnRow - 1, (sal_Int32)0 ); nRow < nLastRow; nRow++ )
3017 sal_uInt16 nRowFlags = 0;
3018 nRowFlags |= (nRow == aStart.mnRow) ? CELL_TOP : 0;
3019 nRowFlags |= (nRow == aEnd.mnRow) ? CELL_BOTTOM : 0;
3020 nRowFlags |= (nRow < aStart.mnRow) ? CELL_UPPER : 0;
3021 nRowFlags |= (nRow > aEnd.mnRow) ? CELL_LOWER : 0;
3023 for( sal_Int32 nCol = std::max( aStart.mnCol - 1, (sal_Int32)0 ); nCol < nLastCol; nCol++ )
3025 CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
3026 if( !xCell.is() )
3027 continue;
3029 sal_uInt16 nCellFlags = nRowFlags;
3030 nCellFlags |= (nCol == aStart.mnCol) ? CELL_LEFT : 0;
3031 nCellFlags |= (nCol == aEnd.mnCol) ? CELL_RIGHT : 0;
3032 nCellFlags |= (nCol < aStart.mnCol) ? CELL_BEFORE : 0;
3033 nCellFlags |= (nCol > aEnd.mnCol) ? CELL_AFTER : 0;
3035 const SfxItemSet& rSet = xCell->GetItemSet();
3036 SvxBoxItem aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
3037 lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellFlags );
3041 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
3042 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
3043 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
3044 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
3045 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
3046 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
3047 if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
3048 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
3049 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
3050 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
3051 if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
3052 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
3054 if (!aLinesState.bDistanceIndeterminate)
3056 if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
3057 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
3058 if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
3059 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
3060 if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
3061 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
3062 if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
3063 aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
3064 aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
3070 bool SvxTableController::selectRow( sal_Int32 row )
3072 if( !mxTable.is() )
3073 return false;
3074 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3075 StartSelection( aEnd );
3076 gotoCell( aStart, true, 0 );
3077 return true;
3080 bool SvxTableController::selectColumn( sal_Int32 column )
3082 if( !mxTable.is() )
3083 return false;
3084 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3085 StartSelection( aEnd );
3086 gotoCell( aStart, true, 0 );
3087 return true;
3090 bool SvxTableController::deselectRow( sal_Int32 row )
3092 if( !mxTable.is() )
3093 return false;
3094 CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
3095 StartSelection( aEnd );
3096 gotoCell( aStart, false, 0 );
3097 return true;
3100 bool SvxTableController::deselectColumn( sal_Int32 column )
3102 if( !mxTable.is() )
3103 return false;
3104 CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
3105 StartSelection( aEnd );
3106 gotoCell( aStart, false, 0 );
3107 return true;
3110 bool SvxTableController::isRowSelected( sal_Int32 nRow )
3112 if( hasSelectedCells() )
3114 CellPos aFirstPos, aLastPos;
3115 getSelectedCells( aFirstPos, aLastPos );
3116 if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
3117 return true;
3119 return false;
3122 bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
3124 if( hasSelectedCells() )
3126 CellPos aFirstPos, aLastPos;
3127 getSelectedCells( aFirstPos, aLastPos );
3128 if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
3129 return true;
3131 return false;
3134 bool SvxTableController::isRowHeader()
3136 SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
3137 SdrModel* pModel = pTableObj ? pTableObj->GetModel() : 0;
3139 if( !pTableObj || !pModel )
3140 return false;
3142 TableStyleSettings aSettings( pTableObj->getTableStyleSettings() );
3144 return aSettings.mbUseFirstRow;
3147 bool SvxTableController::isColumnHeader()
3149 SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mxTableObj.get() );
3150 SdrModel* pModel = pTableObj ? pTableObj->GetModel() : 0;
3152 if( !pTableObj || !pModel )
3153 return false;
3155 TableStyleSettings aSettings( pTableObj->getTableStyleSettings() );
3157 return aSettings.mbUseFirstColumn;
3160 bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
3162 if (mxTableObj->GetObjIdentifier() != OBJ_TABLE)
3163 return false;
3165 SdrTableObj* pTableObj = static_cast<SdrTableObj*>(mxTableObj.get());
3166 CellPos aCellPos;
3167 if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow, 0) != SDRTABLEHIT_NONE)
3169 // Position is a table cell.
3170 if (mbCellSelectionMode)
3172 // We have a table selection already: adjust the point or the mark.
3173 if (bPoint)
3174 setSelectedCells(maCursorFirstPos, aCellPos);
3175 else
3176 setSelectedCells(aCellPos, maCursorLastPos);
3177 return true;
3179 else if (aCellPos != maMouseDownPos)
3181 // No selection, but rPosition is at an other cell: start table selection.
3182 StartSelection(maMouseDownPos);
3183 // Update graphic selection, should be hidden now.
3184 mpView->AdjustMarkHdl();
3188 return false;
3193 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */