forked from amazingfate/loongoffice
I checked if this is used, but it can be replaced using Clear() -> all. This prevents that the whole array of Items in an ItemSet gets set to INVALID_POOL_ITEM. I also checked if INVALID_POOL_ITEM/IsInvalidItem is needed at all representing SfxItemState::DONTCARE but it is and still will need to be set for individual Items. At last checked if SfxItemState::UNKNOWN and ::DISABLED really need to be separate states, but indeed there are some rare cases that need that. To make things more consistent I also renamed SfxItemState::DONTCARE to SfxItemState::INVALID to better match Set/IsInvalid calls at ItemSet. The build showed a missing UT and led to a problem due to the hand-made ItemSet-like SearchAttrItemList. The state 'invalid' seems to be used as 'unused' marker. It should be changed to use SfxPoolItemHolder and not need that. For now, set by using an own loop to set to that state. Change-Id: Ifc51aad60570569a1e37d3084a5e307eed47d06c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165035 Tested-by: Jenkins Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
3369 lines
107 KiB
C++
3369 lines
107 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <algorithm>
|
|
|
|
#include <svx/sdr/table/tablecontroller.hxx>
|
|
#include <tablemodel.hxx>
|
|
|
|
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
|
|
#include <com/sun/star/container/XIndexAccess.hpp>
|
|
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/table/XMergeableCellRange.hpp>
|
|
#include <com/sun/star/table/XMergeableCell.hpp>
|
|
|
|
#include <sal/config.h>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <vcl/window.hxx>
|
|
|
|
#include <svl/whiter.hxx>
|
|
#include <svl/stritem.hxx>
|
|
|
|
#include <sfx2/request.hxx>
|
|
|
|
#include <svx/svdotable.hxx>
|
|
#include <sdr/overlay/overlayobjectcell.hxx>
|
|
#include <svx/sdr/overlay/overlaymanager.hxx>
|
|
#include <svx/svxids.hrc>
|
|
#include <svx/svdoutl.hxx>
|
|
#include <svx/svdpagv.hxx>
|
|
#include <svx/svdetc.hxx>
|
|
#include <svx/selectioncontroller.hxx>
|
|
#include <svx/svdmodel.hxx>
|
|
#include <svx/sdrpaintwindow.hxx>
|
|
#include <svx/svxdlg.hxx>
|
|
#include <editeng/boxitem.hxx>
|
|
#include <cell.hxx>
|
|
#include <editeng/borderline.hxx>
|
|
#include <editeng/colritem.hxx>
|
|
#include <editeng/lineitem.hxx>
|
|
#include <svx/strings.hrc>
|
|
#include <svx/dialmgr.hxx>
|
|
#include <svx/svdpage.hxx>
|
|
#include <svx/sdmetitm.hxx>
|
|
#include <svx/sdtditm.hxx>
|
|
#include "tableundo.hxx"
|
|
#include "tablelayouter.hxx"
|
|
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
|
|
#include <memory>
|
|
#include <o3tl/enumarray.hxx>
|
|
#include <o3tl/enumrange.hxx>
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <comphelper/lok.hxx>
|
|
#include <sfx2/viewsh.hxx>
|
|
#include <editeng/editview.hxx>
|
|
#include <tools/UnitConversion.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
|
|
using ::editeng::SvxBorderLine;
|
|
using namespace sdr::table;
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::table;
|
|
using namespace ::com::sun::star::beans;
|
|
using namespace ::com::sun::star::container;
|
|
using namespace ::com::sun::star::text;
|
|
using namespace ::com::sun::star::style;
|
|
|
|
namespace {
|
|
|
|
enum class CellPosFlag // signals the relative position of a cell to a selection
|
|
{
|
|
NONE = 0x0000, // not set or inside
|
|
// row
|
|
Before = 0x0001,
|
|
Left = 0x0002,
|
|
Right = 0x0004,
|
|
After = 0x0008,
|
|
// column
|
|
Upper = 0x0010,
|
|
Top = 0x0020,
|
|
Bottom = 0x0040,
|
|
Lower = 0x0080
|
|
};
|
|
|
|
}
|
|
|
|
namespace o3tl
|
|
{ template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
|
|
|
|
namespace sdr::table {
|
|
|
|
namespace {
|
|
|
|
class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
|
|
{
|
|
public:
|
|
explicit SvxTableControllerModifyListener( SvxTableController* pController )
|
|
: mpController( pController ) {}
|
|
|
|
// XModifyListener
|
|
virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
|
|
|
|
// XEventListener
|
|
virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
|
|
|
|
SvxTableController* mpController;
|
|
};
|
|
|
|
}
|
|
|
|
// XModifyListener
|
|
|
|
|
|
void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
|
|
{
|
|
if( mpController )
|
|
mpController->onTableModified();
|
|
}
|
|
|
|
|
|
// XEventListener
|
|
|
|
|
|
void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
|
|
{
|
|
mpController = nullptr;
|
|
}
|
|
|
|
|
|
|
|
|
|
rtl::Reference< sdr::SelectionController > CreateTableController(
|
|
SdrView& rView,
|
|
const SdrTableObj& rObj,
|
|
const rtl::Reference< sdr::SelectionController >& xRefController )
|
|
{
|
|
return SvxTableController::create(rView, rObj, xRefController);
|
|
}
|
|
|
|
|
|
rtl::Reference< sdr::SelectionController > SvxTableController::create(
|
|
SdrView& rView,
|
|
const SdrTableObj& rObj,
|
|
const rtl::Reference< sdr::SelectionController >& xRefController )
|
|
{
|
|
if( xRefController.is() )
|
|
{
|
|
SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
|
|
|
|
if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
|
|
{
|
|
return xRefController;
|
|
}
|
|
}
|
|
|
|
return new SvxTableController(rView, rObj);
|
|
}
|
|
|
|
|
|
SvxTableController::SvxTableController(
|
|
SdrView& rView,
|
|
const SdrTableObj& rObj)
|
|
: mbCellSelectionMode(false)
|
|
,mbHasJustMerged(false)
|
|
,mbLeftButtonDown(false)
|
|
,mrView(rView)
|
|
,mxTableObj(const_cast< SdrTableObj* >(&rObj))
|
|
,mnUpdateEvent( nullptr )
|
|
{
|
|
rObj.getActiveCellPos( maCursorFirstPos );
|
|
maCursorLastPos = maCursorFirstPos;
|
|
|
|
Reference< XTable > xTable( mxTableObj.get()->getTable() );
|
|
if( xTable.is() )
|
|
{
|
|
mxModifyListener = new SvxTableControllerModifyListener( this );
|
|
xTable->addModifyListener( mxModifyListener );
|
|
|
|
mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
|
|
}
|
|
}
|
|
|
|
SvxTableController::~SvxTableController()
|
|
{
|
|
if( mnUpdateEvent )
|
|
{
|
|
Application::RemoveUserEvent( mnUpdateEvent );
|
|
}
|
|
|
|
if( mxModifyListener.is() && mxTableObj.get() )
|
|
{
|
|
Reference< XTable > xTable( mxTableObj.get()->getTable() );
|
|
if( xTable.is() )
|
|
{
|
|
xTable->removeModifyListener( mxModifyListener );
|
|
mxModifyListener.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
|
|
{
|
|
if(!checkTableObject())
|
|
return false;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
|
|
// check if we are read only
|
|
if( rModel.IsReadOnly())
|
|
{
|
|
switch( rKEvt.GetKeyCode().GetCode() )
|
|
{
|
|
case awt::Key::DOWN:
|
|
case awt::Key::UP:
|
|
case awt::Key::LEFT:
|
|
case awt::Key::RIGHT:
|
|
case awt::Key::TAB:
|
|
case awt::Key::HOME:
|
|
case awt::Key::END:
|
|
case awt::Key::NUM2:
|
|
case awt::Key::NUM4:
|
|
case awt::Key::NUM6:
|
|
case awt::Key::NUM8:
|
|
case awt::Key::ESCAPE:
|
|
case awt::Key::F2:
|
|
break;
|
|
default:
|
|
// tell the view we eat the event, no further processing needed
|
|
return true;
|
|
}
|
|
}
|
|
|
|
TblAction nAction = getKeyboardAction(rKEvt);
|
|
|
|
return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
|
|
}
|
|
|
|
namespace {
|
|
|
|
Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
|
|
{
|
|
if (!pWindow)
|
|
return rPoint;
|
|
|
|
return pWindow->PixelToLogic(rPoint);
|
|
}
|
|
|
|
}
|
|
|
|
bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive() && !pWindow)
|
|
{
|
|
// Tiled rendering: get the window that has the disabled map mode.
|
|
if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
|
|
{
|
|
if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
|
|
pWindow = pOutputDevice->GetOwnerWindow();
|
|
}
|
|
}
|
|
|
|
if( !pWindow || !checkTableObject() )
|
|
return false;
|
|
|
|
SdrViewEvent aVEvt;
|
|
if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
|
|
return false;
|
|
|
|
TableHitKind eHit = mxTableObj.get()->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
|
|
|
|
mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
|
|
|
|
if( eHit == TableHitKind::Cell )
|
|
{
|
|
StartSelection( maMouseDownPos );
|
|
return true;
|
|
}
|
|
|
|
if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
|
|
return true; // right click will become context menu
|
|
|
|
// for cell selection with the mouse remember our first hit
|
|
if( mbLeftButtonDown )
|
|
{
|
|
RemoveSelection();
|
|
|
|
SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
|
|
|
|
if( pHdl )
|
|
{
|
|
mbLeftButtonDown = false;
|
|
}
|
|
else
|
|
{
|
|
rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
|
|
|
|
if (!pTableObj || eHit == TableHitKind::NONE)
|
|
{
|
|
mbLeftButtonDown = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
|
|
{
|
|
bool bEmptyOutliner = false;
|
|
if (Outliner* pOutliner = mrView.GetTextEditOutliner())
|
|
{
|
|
if (pOutliner->GetParagraphCount() == 1)
|
|
{
|
|
if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
|
|
bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
|
|
}
|
|
}
|
|
if (bEmptyOutliner)
|
|
{
|
|
// Tiled rendering: a left double-click in an empty cell: select it.
|
|
StartSelection(maMouseDownPos);
|
|
setSelectedCells(maMouseDownPos, maMouseDownPos);
|
|
// Update graphic selection, should be hidden now.
|
|
mrView.AdjustMarkHdl();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
|
|
{
|
|
if( !checkTableObject() )
|
|
return false;
|
|
|
|
mbLeftButtonDown = false;
|
|
|
|
return rMEvt.GetClicks() == 2;
|
|
}
|
|
|
|
|
|
bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
|
|
{
|
|
if( !checkTableObject() )
|
|
return false;
|
|
|
|
rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
|
|
CellPos aPos;
|
|
if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
|
|
{
|
|
if(aPos != maMouseDownPos)
|
|
{
|
|
if( mbCellSelectionMode )
|
|
{
|
|
setSelectedCells( maMouseDownPos, aPos );
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
StartSelection( maMouseDownPos );
|
|
}
|
|
}
|
|
else if( mbCellSelectionMode )
|
|
{
|
|
UpdateSelection( aPos );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void SvxTableController::onSelectionHasChanged()
|
|
{
|
|
bool bSelected = false;
|
|
|
|
rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
|
|
if( pTableObj && pTableObj->IsTextEditActive() )
|
|
{
|
|
pTableObj->getActiveCellPos( maCursorFirstPos );
|
|
maCursorLastPos = maCursorFirstPos;
|
|
mbCellSelectionMode = false;
|
|
}
|
|
else
|
|
{
|
|
const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
|
|
if( rMarkList.GetMarkCount() == 1 )
|
|
bSelected = mxTableObj.get().get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
|
|
}
|
|
|
|
if( bSelected )
|
|
{
|
|
updateSelectionOverlay();
|
|
}
|
|
else
|
|
{
|
|
destroySelectionOverlay();
|
|
}
|
|
}
|
|
void SvxTableController::onSelectAll()
|
|
{
|
|
rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
|
|
if ( pTableObj && !pTableObj->IsTextEditActive())
|
|
{
|
|
selectAll();
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::GetState( SfxItemSet& rSet )
|
|
{
|
|
if(!mxTable.is() || !mxTableObj.get().is())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
std::optional<SfxItemSet> oSet;
|
|
bool bVertDone(false);
|
|
|
|
// Iterate over all requested items in the set.
|
|
SfxWhichIter aIter( rSet );
|
|
sal_uInt16 nWhich = aIter.FirstWhich();
|
|
while (nWhich)
|
|
{
|
|
switch (nWhich)
|
|
{
|
|
case SID_TABLE_VERT_BOTTOM:
|
|
case SID_TABLE_VERT_CENTER:
|
|
case SID_TABLE_VERT_NONE:
|
|
{
|
|
if(!bVertDone)
|
|
{
|
|
if (!oSet)
|
|
{
|
|
oSet.emplace(rModel.GetItemPool());
|
|
MergeAttrFromSelectedCells(*oSet, false);
|
|
}
|
|
|
|
SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
|
|
|
|
if (oSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::INVALID)
|
|
eAdj = oSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
|
|
|
|
rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
|
|
rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
|
|
rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
|
|
bVertDone = true;
|
|
}
|
|
break;
|
|
}
|
|
case SID_TABLE_DELETE_ROW:
|
|
if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
|
|
rSet.DisableItem(SID_TABLE_DELETE_ROW);
|
|
break;
|
|
case SID_TABLE_DELETE_COL:
|
|
if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
|
|
rSet.DisableItem(SID_TABLE_DELETE_COL);
|
|
break;
|
|
case SID_TABLE_DELETE_TABLE:
|
|
if( !mxTable.is() )
|
|
rSet.DisableItem(SID_TABLE_DELETE_TABLE);
|
|
break;
|
|
case SID_TABLE_MERGE_CELLS:
|
|
if( !mxTable.is() || !hasSelectedCells() )
|
|
rSet.DisableItem(SID_TABLE_MERGE_CELLS);
|
|
break;
|
|
case SID_TABLE_SPLIT_CELLS:
|
|
if( !hasSelectedCells() || !mxTable.is() )
|
|
rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
|
|
break;
|
|
|
|
case SID_TABLE_OPTIMAL_ROW_HEIGHT:
|
|
case SID_TABLE_DISTRIBUTE_COLUMNS:
|
|
case SID_TABLE_DISTRIBUTE_ROWS:
|
|
{
|
|
bool bDistributeColumns = false;
|
|
bool bDistributeRows = false;
|
|
if( mxTable.is() )
|
|
{
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
bDistributeColumns = aStart.mnCol != aEnd.mnCol;
|
|
bDistributeRows = aStart.mnRow != aEnd.mnRow;
|
|
}
|
|
if( !bDistributeColumns )
|
|
rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
|
|
if( !bDistributeRows )
|
|
{
|
|
rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
|
|
rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
nWhich = aIter.NextWhich();
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
|
|
{
|
|
if(!checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
bool bInsertAfter = true;
|
|
sal_uInt16 nCount = 0;
|
|
|
|
if( pArgs )
|
|
{
|
|
const SfxPoolItem* pItem = nullptr;
|
|
pArgs->GetItemState(nSId, false, &pItem);
|
|
if (pItem)
|
|
{
|
|
nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
|
|
if(const SfxBoolItem* pItem2 = pArgs->GetItemIfSet(SID_TABLE_PARAM_INSERT_AFTER))
|
|
bInsertAfter = pItem2->GetValue();
|
|
}
|
|
}
|
|
|
|
CellPos aStart, aEnd;
|
|
if( hasSelectedCells() )
|
|
{
|
|
getSelectedCells( aStart, aEnd );
|
|
}
|
|
else
|
|
{
|
|
if( bInsertAfter )
|
|
{
|
|
aStart.mnCol = mxTable->getColumnCount() - 1;
|
|
aStart.mnRow = mxTable->getRowCount() - 1;
|
|
aEnd = aStart;
|
|
}
|
|
}
|
|
|
|
if( rTableObj.IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
RemoveSelection();
|
|
|
|
static constexpr OUString sSize( u"Size"_ustr );
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
switch( nSId )
|
|
{
|
|
case SID_TABLE_INSERT_COL:
|
|
{
|
|
TableModelNotifyGuard aGuard( mxTable.get() );
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
|
|
}
|
|
|
|
Reference< XTableColumns > xCols( mxTable->getColumns() );
|
|
const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
|
|
const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
|
|
xCols->insertByIndex( nNewStartColumn, nNewColumns );
|
|
|
|
for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
|
|
{
|
|
// Resolves fdo#61540
|
|
// On Insert before, the reference column whose size is going to be
|
|
// used for newly created column(s) is wrong. As the new columns are
|
|
// inserted before the reference column, the reference column moved
|
|
// to the new position by no., of new columns i.e (earlier+newcolumns).
|
|
Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
|
|
setPropertyValue( sSize,
|
|
Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
|
|
getPropertyValue( sSize ) );
|
|
}
|
|
|
|
// Copy cell properties
|
|
sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
|
|
sal_Int32 nRowSpan = 0;
|
|
bool bNewSpan = false;
|
|
|
|
for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
|
|
{
|
|
CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
|
|
|
|
// When we insert new COLUMNs, we want to copy ROW spans.
|
|
if (xSourceCell.is() && nRowSpan == 0)
|
|
{
|
|
// we are not in a span yet. Let's find out if the current cell is in a span.
|
|
sal_Int32 nColSpan = sal_Int32();
|
|
sal_Int32 nSpanInfoCol = sal_Int32();
|
|
|
|
if( xSourceCell->getRowSpan() > 1 )
|
|
{
|
|
// The current cell is the top-left cell in a span.
|
|
// Get the span info and propagate it to the target.
|
|
nRowSpan = xSourceCell->getRowSpan();
|
|
nColSpan = xSourceCell->getColumnSpan();
|
|
nSpanInfoCol = nPropSrcCol;
|
|
}
|
|
else if( xSourceCell->isMerged() )
|
|
{
|
|
// The current cell is a middle cell in a 2D span.
|
|
// Look for the top-left cell in the span.
|
|
for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
|
|
{
|
|
CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
|
|
if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
|
|
{
|
|
nRowSpan = xMergeInfoCell->getRowSpan();
|
|
nColSpan = xMergeInfoCell->getColumnSpan();
|
|
break;
|
|
}
|
|
}
|
|
if( nRowSpan == 1 )
|
|
nRowSpan = 0;
|
|
}
|
|
|
|
// The target columns are outside the span; Start a new span.
|
|
if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
|
|
bNewSpan = true;
|
|
}
|
|
|
|
// Now copy the properties from the source to the targets
|
|
for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
|
|
{
|
|
CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
|
|
if( xTargetCell.is() )
|
|
{
|
|
if( nRowSpan > 0 )
|
|
{
|
|
if( bNewSpan )
|
|
xTargetCell->merge( 1, nRowSpan );
|
|
else
|
|
xTargetCell->setMerged();
|
|
}
|
|
xTargetCell->copyFormatFrom( xSourceCell );
|
|
}
|
|
}
|
|
|
|
if( nRowSpan > 0 )
|
|
{
|
|
--nRowSpan;
|
|
bNewSpan = false;
|
|
}
|
|
}
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
|
|
aStart.mnCol = nNewStartColumn;
|
|
aStart.mnRow = 0;
|
|
aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
|
|
aEnd.mnRow = mxTable->getRowCount() - 1;
|
|
break;
|
|
}
|
|
|
|
case SID_TABLE_INSERT_ROW:
|
|
{
|
|
TableModelNotifyGuard aGuard( mxTable.get() );
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
|
|
}
|
|
|
|
Reference< XTableRows > xRows( mxTable->getRows() );
|
|
const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
|
|
const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
|
|
xRows->insertByIndex( nNewRowStart, nNewRows );
|
|
|
|
for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
|
|
{
|
|
Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
|
|
setPropertyValue( sSize,
|
|
Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
|
|
getPropertyValue( sSize ) );
|
|
}
|
|
|
|
// Copy the cell properties
|
|
sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
|
|
sal_Int32 nColSpan = 0;
|
|
bool bNewSpan = false;
|
|
|
|
for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
|
|
{
|
|
CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
|
|
|
|
if (!xSourceCell.is())
|
|
continue;
|
|
|
|
// When we insert new ROWs, we want to copy COLUMN spans.
|
|
if( nColSpan == 0 )
|
|
{
|
|
// we are not in a span yet. Let's find out if the current cell is in a span.
|
|
sal_Int32 nRowSpan = sal_Int32();
|
|
sal_Int32 nSpanInfoRow = sal_Int32();
|
|
|
|
if( xSourceCell->getColumnSpan() > 1 )
|
|
{
|
|
// The current cell is the top-left cell in a span.
|
|
// Get the span info and propagate it to the target.
|
|
nColSpan = xSourceCell->getColumnSpan();
|
|
nRowSpan = xSourceCell->getRowSpan();
|
|
nSpanInfoRow = nPropSrcRow;
|
|
}
|
|
else if( xSourceCell->isMerged() )
|
|
{
|
|
// The current cell is a middle cell in a 2D span.
|
|
// Look for the top-left cell in the span.
|
|
for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
|
|
{
|
|
CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
|
|
if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
|
|
{
|
|
nColSpan = xMergeInfoCell->getColumnSpan();
|
|
nRowSpan = xMergeInfoCell->getRowSpan();
|
|
break;
|
|
}
|
|
}
|
|
if( nColSpan == 1 )
|
|
nColSpan = 0;
|
|
}
|
|
|
|
// Inserted rows are outside the span; Start a new span.
|
|
if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
|
|
bNewSpan = true;
|
|
}
|
|
|
|
// Now copy the properties from the source to the targets
|
|
for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
|
|
{
|
|
CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
|
|
if( xTargetCell.is() )
|
|
{
|
|
if( nColSpan > 0 )
|
|
{
|
|
if( bNewSpan )
|
|
xTargetCell->merge( nColSpan, 1 );
|
|
else
|
|
xTargetCell->setMerged();
|
|
}
|
|
xTargetCell->copyFormatFrom( xSourceCell );
|
|
}
|
|
}
|
|
|
|
if( nColSpan > 0 )
|
|
{
|
|
--nColSpan;
|
|
bNewSpan = false;
|
|
}
|
|
}
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
|
|
aStart.mnCol = 0;
|
|
aStart.mnRow = nNewRowStart;
|
|
aEnd.mnCol = mxTable->getColumnCount() - 1;
|
|
aEnd.mnRow = aStart.mnRow + nNewRows - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
StartSelection( aStart );
|
|
UpdateSelection( aEnd );
|
|
}
|
|
|
|
|
|
void SvxTableController::onDelete( sal_uInt16 nSId )
|
|
{
|
|
rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
|
|
if( !pTableObj || !mxTable.is() )
|
|
return;
|
|
|
|
if( nSId == SID_TABLE_DELETE_TABLE )
|
|
{
|
|
if( pTableObj->IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
mrView.DeleteMarkedObj();
|
|
}
|
|
else if( hasSelectedCells() )
|
|
{
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
if( pTableObj->IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
RemoveSelection();
|
|
|
|
bool bDeleteTable = false;
|
|
switch( nSId )
|
|
{
|
|
case SID_TABLE_DELETE_COL:
|
|
{
|
|
const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
|
|
if( nRemovedColumns == mxTable->getColumnCount() )
|
|
{
|
|
bDeleteTable = true;
|
|
}
|
|
else
|
|
{
|
|
Reference< XTableColumns > xCols( mxTable->getColumns() );
|
|
xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
|
|
EditCell(aStart, nullptr, TblAction::NONE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SID_TABLE_DELETE_ROW:
|
|
{
|
|
const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
|
|
if( nRemovedRows == mxTable->getRowCount() )
|
|
{
|
|
bDeleteTable = true;
|
|
}
|
|
else
|
|
{
|
|
Reference< XTableRows > xRows( mxTable->getRows() );
|
|
xRows->removeByIndex( aStart.mnRow, nRemovedRows );
|
|
EditCell(aStart, nullptr, TblAction::NONE);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( bDeleteTable )
|
|
mrView.DeleteMarkedObj();
|
|
else
|
|
UpdateTableShape();
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::onSelect( sal_uInt16 nSId )
|
|
{
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
const sal_Int32 nRowCount = mxTable->getRowCount();
|
|
const sal_Int32 nColCount = mxTable->getColumnCount();
|
|
if( !(nRowCount && nColCount) )
|
|
return;
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
switch( nSId )
|
|
{
|
|
case SID_TABLE_SELECT_ALL:
|
|
aEnd.mnCol = 0; aEnd.mnRow = 0;
|
|
aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
|
|
break;
|
|
case SID_TABLE_SELECT_COL:
|
|
aEnd.mnRow = nRowCount - 1;
|
|
aStart.mnRow = 0;
|
|
break;
|
|
case SID_TABLE_SELECT_ROW:
|
|
aEnd.mnCol = nColCount - 1;
|
|
aStart.mnCol = 0;
|
|
break;
|
|
}
|
|
|
|
StartSelection( aEnd );
|
|
gotoCell( aStart, true, nullptr );
|
|
}
|
|
|
|
SvxBoxItem SvxTableController::TextDistancesToSvxBoxItem(const SfxItemSet& rAttrSet)
|
|
{
|
|
// merge drawing layer text distance items into SvxBoxItem used by the dialog
|
|
SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
|
|
aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
|
|
aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
|
|
aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
|
|
aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
|
|
return aBoxItem;
|
|
}
|
|
|
|
void SvxTableController::SvxBoxItemToTextDistances(const SvxBoxItem& pOriginalItem, SfxItemSet& rAttrSet)
|
|
{
|
|
const SvxBoxItem* pNewItem( rAttrSet.GetItemIfSet( SDRATTR_TABLE_BORDER ) );
|
|
if ( !pNewItem )
|
|
return;
|
|
|
|
if( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) != pOriginalItem.GetDistance( SvxBoxItemLine::LEFT ) )
|
|
rAttrSet.Put(makeSdrTextLeftDistItem( pNewItem->GetDistance( SvxBoxItemLine::LEFT ) ) );
|
|
|
|
if( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) != pOriginalItem.GetDistance( SvxBoxItemLine::RIGHT ) )
|
|
rAttrSet.Put(makeSdrTextRightDistItem( pNewItem->GetDistance( SvxBoxItemLine::RIGHT ) ) );
|
|
|
|
if( pNewItem->GetDistance( SvxBoxItemLine::TOP ) != pOriginalItem.GetDistance( SvxBoxItemLine::TOP ) )
|
|
rAttrSet.Put(makeSdrTextUpperDistItem( pNewItem->GetDistance( SvxBoxItemLine::TOP ) ) );
|
|
|
|
if( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) != pOriginalItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
|
|
rAttrSet.Put(makeSdrTextLowerDistItem( pNewItem->GetDistance( SvxBoxItemLine::BOTTOM ) ) );
|
|
}
|
|
|
|
void SvxTableController::onFormatTable(const SfxRequest& rReq)
|
|
{
|
|
if(!mxTableObj.get().is())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const SfxItemSet* pArgs = rReq.GetArgs();
|
|
|
|
if(pArgs)
|
|
return;
|
|
|
|
SfxItemSet aNewAttr(rModel.GetItemPool());
|
|
|
|
// merge drawing layer text distance items into SvxBoxItem used by the dialog
|
|
auto xBoxItem(std::make_shared<SvxBoxItem>(TextDistancesToSvxBoxItem(aNewAttr)));
|
|
auto xBoxInfoItem(std::make_shared<SvxBoxInfoItem>(aNewAttr.Get(SDRATTR_TABLE_BORDER_INNER)));
|
|
|
|
MergeAttrFromSelectedCells(aNewAttr, false);
|
|
FillCommonBorderAttrFromSelectedCells(*xBoxItem, *xBoxInfoItem);
|
|
aNewAttr.Put(*xBoxItem);
|
|
aNewAttr.Put(*xBoxInfoItem);
|
|
|
|
// Fill in shadow properties.
|
|
const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
|
|
for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
|
|
{
|
|
if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
aNewAttr.Put(rTableItemSet.Get(nWhich));
|
|
}
|
|
|
|
SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
|
|
VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
|
|
rReq.GetFrameWeld(),
|
|
aNewAttr,
|
|
rModel, false) );
|
|
|
|
// Even Cancel Button is returning positive(101) value,
|
|
xDlg->StartExecuteAsync([xDlg, this, xBoxItem, xBoxInfoItem](int nResult){
|
|
if (nResult == RET_OK)
|
|
{
|
|
SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
|
|
|
|
//Only properties that were unchanged by the dialog appear in this
|
|
//itemset. We had constructed these two properties from other
|
|
//ones, so if they were not changed, then forcible set them back to
|
|
//their originals in the new result set so we can decompose that
|
|
//unchanged state back to their input properties
|
|
if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
|
|
{
|
|
aNewSet.Put(*xBoxItem);
|
|
}
|
|
if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
|
|
{
|
|
aNewSet.Put(*xBoxInfoItem);
|
|
}
|
|
|
|
SvxBoxItemToTextDistances(*xBoxItem, aNewSet);
|
|
|
|
if (checkTableObject() && mxTable.is())
|
|
{
|
|
// Create a single undo action when applying the result of the dialog.
|
|
SdrTableObj& rTableObject(*mxTableObj.get());
|
|
SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
|
|
bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
|
|
if (bUndo)
|
|
{
|
|
rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
|
|
}
|
|
|
|
this->SetAttrToSelectedCells(aNewSet, false);
|
|
|
|
this->SetAttrToSelectedShape(aNewSet);
|
|
|
|
if (bUndo)
|
|
{
|
|
rSdrModel.EndUndo();
|
|
}
|
|
}
|
|
}
|
|
|
|
xDlg->disposeOnce();
|
|
});
|
|
}
|
|
|
|
void SvxTableController::Execute( SfxRequest& rReq )
|
|
{
|
|
const sal_uInt16 nSId = rReq.GetSlot();
|
|
switch( nSId )
|
|
{
|
|
case SID_TABLE_INSERT_ROW:
|
|
case SID_TABLE_INSERT_COL:
|
|
onInsert( nSId, rReq.GetArgs() );
|
|
break;
|
|
case SID_TABLE_DELETE_ROW:
|
|
case SID_TABLE_DELETE_COL:
|
|
case SID_TABLE_DELETE_TABLE:
|
|
onDelete( nSId );
|
|
break;
|
|
case SID_TABLE_SELECT_ALL:
|
|
case SID_TABLE_SELECT_COL:
|
|
case SID_TABLE_SELECT_ROW:
|
|
onSelect( nSId );
|
|
break;
|
|
case SID_FORMAT_TABLE_DLG:
|
|
onFormatTable( rReq );
|
|
break;
|
|
|
|
case SID_FRAME_LINESTYLE:
|
|
case SID_FRAME_LINECOLOR:
|
|
case SID_ATTR_BORDER:
|
|
{
|
|
const SfxItemSet* pArgs = rReq.GetArgs();
|
|
if( pArgs )
|
|
ApplyBorderAttr( *pArgs );
|
|
}
|
|
break;
|
|
|
|
case SID_ATTR_FILL_STYLE:
|
|
{
|
|
const SfxItemSet* pArgs = rReq.GetArgs();
|
|
if( pArgs )
|
|
SetAttributes( *pArgs, false );
|
|
}
|
|
break;
|
|
|
|
case SID_TABLE_MERGE_CELLS:
|
|
MergeMarkedCells();
|
|
break;
|
|
|
|
case SID_TABLE_SPLIT_CELLS:
|
|
SplitMarkedCells(rReq);
|
|
break;
|
|
|
|
case SID_TABLE_MINIMAL_COLUMN_WIDTH:
|
|
DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
|
|
break;
|
|
|
|
case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
|
|
DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
|
|
break;
|
|
|
|
case SID_TABLE_DISTRIBUTE_COLUMNS:
|
|
DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
|
|
break;
|
|
|
|
case SID_TABLE_MINIMAL_ROW_HEIGHT:
|
|
DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
|
|
break;
|
|
|
|
case SID_TABLE_OPTIMAL_ROW_HEIGHT:
|
|
DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
|
|
break;
|
|
|
|
case SID_TABLE_DISTRIBUTE_ROWS:
|
|
DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
|
|
break;
|
|
|
|
case SID_TABLE_VERT_BOTTOM:
|
|
case SID_TABLE_VERT_CENTER:
|
|
case SID_TABLE_VERT_NONE:
|
|
SetVertical( nSId );
|
|
break;
|
|
|
|
case SID_AUTOFORMAT:
|
|
case SID_TABLE_SORT_DIALOG:
|
|
case SID_TABLE_AUTOSUM:
|
|
default:
|
|
break;
|
|
|
|
case SID_TABLE_STYLE:
|
|
SetTableStyle( rReq.GetArgs() );
|
|
break;
|
|
|
|
case SID_TABLE_STYLE_SETTINGS:
|
|
SetTableStyleSettings( rReq.GetArgs() );
|
|
break;
|
|
case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
|
|
changeTableEdge(rReq);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
|
|
{
|
|
if(!checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
|
|
if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
|
|
return;
|
|
|
|
const SfxStringItem* pArg = &pArgs->Get( SID_TABLE_STYLE );
|
|
if( !(pArg && mxTable.is()) )
|
|
return;
|
|
|
|
try
|
|
{
|
|
Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
|
|
Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
|
|
Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
|
|
|
|
if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
|
|
{
|
|
// found table style with the same name
|
|
Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
|
|
|
|
const bool bUndo = rModel.IsUndoEnabled();
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
|
|
rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
|
|
}
|
|
|
|
rTableObj.setTableStyle( xNewTableStyle );
|
|
|
|
const sal_Int32 nRowCount = mxTable->getRowCount();
|
|
const sal_Int32 nColCount = mxTable->getColumnCount();
|
|
for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() )
|
|
{
|
|
SfxItemSet aSet( xCell->GetItemSet() );
|
|
bool bChanges = false;
|
|
SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
|
|
SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
|
|
if (pStyleSheet)
|
|
{
|
|
const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
|
|
|
|
for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
|
|
{
|
|
if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
|
|
{
|
|
aSet.ClearItem( nWhich );
|
|
bChanges = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bChanges )
|
|
{
|
|
if( bUndo )
|
|
xCell->AddUndo();
|
|
|
|
xCell->SetMergedItemSetAndBroadcast( aSet, true );
|
|
}
|
|
}
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("svx.table", "");
|
|
}
|
|
}
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
}
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("svx.table", "");
|
|
}
|
|
}
|
|
|
|
void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
|
|
{
|
|
if(!checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
|
|
TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
|
|
const SfxBoolItem *pPoolItem=nullptr;
|
|
|
|
if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTROWSTYLE, false)) )
|
|
aSettings.mbUseFirstRow = pPoolItem->GetValue();
|
|
|
|
if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTROWSTYLE, false)) )
|
|
aSettings.mbUseLastRow = pPoolItem->GetValue();
|
|
|
|
if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGROWSTYLE, false)) )
|
|
aSettings.mbUseRowBanding = pPoolItem->GetValue();
|
|
|
|
if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTCOLUMNSTYLE, false)) )
|
|
aSettings.mbUseFirstColumn = pPoolItem->GetValue();
|
|
|
|
if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTCOLUMNSTYLE, false)) )
|
|
aSettings.mbUseLastColumn = pPoolItem->GetValue();
|
|
|
|
if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGCOLUMNSTYLE, false)) )
|
|
aSettings.mbUseColumnBanding = pPoolItem->GetValue();
|
|
|
|
if( aSettings == rTableObj.getTableStyleSettings() )
|
|
return;
|
|
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
|
|
rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
|
|
}
|
|
|
|
rTableObj.setTableStyleSettings( aSettings );
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
}
|
|
|
|
void SvxTableController::SetVertical( sal_uInt16 nSId )
|
|
{
|
|
if(!checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
|
|
TableModelNotifyGuard aGuard( mxTable.get() );
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if (bUndo)
|
|
{
|
|
rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
|
|
}
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
|
|
|
|
switch( nSId )
|
|
{
|
|
case SID_TABLE_VERT_BOTTOM:
|
|
eAdj = SDRTEXTVERTADJUST_BOTTOM;
|
|
break;
|
|
case SID_TABLE_VERT_CENTER:
|
|
eAdj = SDRTEXTVERTADJUST_CENTER;
|
|
break;
|
|
//case SID_TABLE_VERT_NONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
SdrTextVertAdjustItem aItem( eAdj );
|
|
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() )
|
|
{
|
|
if (bUndo)
|
|
xCell->AddUndo();
|
|
SfxItemSet aSet(xCell->GetItemSet());
|
|
aSet.Put(aItem);
|
|
xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateTableShape();
|
|
|
|
if (bUndo)
|
|
rModel.EndUndo();
|
|
}
|
|
|
|
void SvxTableController::MergeMarkedCells()
|
|
{
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
|
|
if( pTableObj )
|
|
{
|
|
if( pTableObj->IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
TableModelNotifyGuard aGuard( mxTable.get() );
|
|
MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
|
|
}
|
|
}
|
|
|
|
void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
|
|
{
|
|
if(!checkTableObject() || !mxTable.is())
|
|
return;
|
|
|
|
SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
|
|
VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
|
|
|
|
xDlg->StartExecuteAsync([xDlg, this](int) {
|
|
const sal_Int32 nCount = xDlg->GetCount() - 1;
|
|
|
|
if( nCount < 1 )
|
|
return;
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
|
|
const sal_Int32 nRowCount = mxTable->getRowCount();
|
|
const sal_Int32 nColCount = mxTable->getColumnCount();
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
|
|
if( rTableObj.IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
TableModelNotifyGuard aGuard( mxTable.get() );
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
|
|
}
|
|
|
|
if( xDlg->IsHorizontal() )
|
|
{
|
|
xRange->split( 0, nCount );
|
|
}
|
|
else
|
|
{
|
|
xRange->split( nCount, 0 );
|
|
}
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
|
|
aEnd.mnRow += mxTable->getRowCount() - nRowCount;
|
|
aEnd.mnCol += mxTable->getColumnCount() - nColCount;
|
|
|
|
setSelectedCells( aStart, aEnd );
|
|
|
|
xDlg->disposeOnce();
|
|
});
|
|
}
|
|
|
|
void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
|
|
{
|
|
if(!checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
|
|
}
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
}
|
|
|
|
void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
|
|
{
|
|
if(!checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
|
|
}
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
}
|
|
|
|
bool SvxTableController::HasMarked() const
|
|
{
|
|
return mbCellSelectionMode && mxTable.is();
|
|
}
|
|
|
|
bool SvxTableController::DeleteMarked()
|
|
{
|
|
if(!checkTableObject() || !HasMarked())
|
|
return false;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
bool bDeleteTable = false;
|
|
|
|
if (bUndo)
|
|
rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
|
|
const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
|
|
if( nRemovedColumns == mxTable->getColumnCount() && nRemovedRows == mxTable->getRowCount())
|
|
{
|
|
bDeleteTable = true;
|
|
}
|
|
else
|
|
{
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if (xCell.is() && xCell->hasText())
|
|
{
|
|
if (bUndo)
|
|
xCell->AddUndo();
|
|
xCell->SetOutlinerParaObject(std::nullopt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bDeleteTable)
|
|
mrView.DeleteMarkedObj();
|
|
|
|
if (bUndo)
|
|
rModel.EndUndo();
|
|
|
|
if (!bDeleteTable)
|
|
UpdateTableShape();
|
|
return true;
|
|
}
|
|
|
|
bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
|
|
{
|
|
if( hasSelectedCells() )
|
|
{
|
|
rpStyleSheet = nullptr;
|
|
|
|
if( mxTable.is() )
|
|
{
|
|
SfxStyleSheet* pRet=nullptr;
|
|
bool b1st=true;
|
|
|
|
CellPos aStart, aEnd;
|
|
const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
|
|
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() )
|
|
{
|
|
SfxStyleSheet* pSS=xCell->GetStyleSheet();
|
|
if(b1st)
|
|
{
|
|
pRet=pSS;
|
|
}
|
|
else if(pRet != pSS)
|
|
{
|
|
return true;
|
|
}
|
|
b1st=false;
|
|
}
|
|
}
|
|
}
|
|
rpStyleSheet = pRet;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
|
|
{
|
|
if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
|
|
{
|
|
if( mxTable.is() )
|
|
{
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() )
|
|
xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
|
|
}
|
|
}
|
|
|
|
UpdateTableShape();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SvxTableController::changeTableEdge(const SfxRequest& rReq)
|
|
{
|
|
if (!checkTableObject())
|
|
return;
|
|
|
|
const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
|
|
const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
|
|
const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
|
|
|
|
if (!(pType && pIndex && pOffset))
|
|
return;
|
|
|
|
const OUString sType = pType->GetValue();
|
|
const sal_uInt16 nIndex = pIndex->GetValue();
|
|
const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
|
|
sal_Int32 nEdgeIndex = -1;
|
|
bool bHorizontal = sType.startsWith("row");
|
|
|
|
if (sType == "column-left" || sType == "row-left")
|
|
{
|
|
nEdgeIndex = 0;
|
|
}
|
|
else if (sType == "column-right")
|
|
{
|
|
// Number of edges = number of columns + 1
|
|
nEdgeIndex = rTableObj.getColumnCount();
|
|
}
|
|
else if (sType == "row-right")
|
|
{
|
|
// Number of edges = number of rows + 1
|
|
nEdgeIndex = rTableObj.getRowCount();
|
|
}
|
|
else if (sType == "column-middle" || sType == "row-middle")
|
|
{
|
|
nEdgeIndex = nIndex + 1;
|
|
}
|
|
|
|
if (nEdgeIndex < 0)
|
|
return;
|
|
|
|
TableModelNotifyGuard aGuard(mxTable.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
if (bUndo)
|
|
{
|
|
auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
|
|
rModel.BegUndo(pUndoObject->GetComment());
|
|
|
|
auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
|
|
if (pGeoUndo)
|
|
pGeoUndo->SetSkipChangeLayout(true);
|
|
|
|
rModel.AddUndo(std::move(pUndoObject));
|
|
}
|
|
tools::Rectangle aBoundRect;
|
|
if (rTableObj.GetUserCall())
|
|
aBoundRect = rTableObj.GetLastBoundRect();
|
|
rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
|
|
rTableObj.SetChanged();
|
|
rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
|
|
if (bUndo)
|
|
rModel.EndUndo();
|
|
}
|
|
|
|
// internals
|
|
|
|
|
|
bool SvxTableController::checkTableObject()
|
|
{
|
|
return mxTableObj.get().is();
|
|
}
|
|
|
|
|
|
SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
|
|
{
|
|
const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
|
|
const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
|
|
const bool bTextEdit = mrView.IsTextEdit();
|
|
|
|
TblAction nAction = TblAction::HandledByView;
|
|
|
|
rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
|
|
if( !pTableObj )
|
|
return nAction;
|
|
|
|
// handle special keys
|
|
const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
|
|
switch( nCode )
|
|
{
|
|
case awt::Key::ESCAPE: // handle escape
|
|
{
|
|
if( bTextEdit )
|
|
{
|
|
// escape during text edit ends text edit
|
|
nAction = TblAction::StopTextEdit;
|
|
}
|
|
if( mbCellSelectionMode )
|
|
{
|
|
// escape with selected cells removes selection
|
|
nAction = TblAction::RemoveSelection;
|
|
}
|
|
break;
|
|
}
|
|
case awt::Key::RETURN: // handle return
|
|
{
|
|
if( !bMod1 && !bMod2 && !bTextEdit )
|
|
{
|
|
// when not already editing, return starts text edit
|
|
setSelectionStart( SdrTableObj::getFirstCell() );
|
|
nAction = TblAction::EditCell;
|
|
}
|
|
break;
|
|
}
|
|
case awt::Key::F2: // f2 toggles text edit
|
|
{
|
|
if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
|
|
{
|
|
}
|
|
else if( bTextEdit )
|
|
{
|
|
// f2 during text edit stops text edit
|
|
nAction = TblAction::StopTextEdit;
|
|
}
|
|
else if( mbCellSelectionMode )
|
|
{
|
|
// f2 with selected cells removes selection
|
|
nAction = TblAction::RemoveSelection;
|
|
}
|
|
else
|
|
{
|
|
// f2 with no selection and no text edit starts text edit
|
|
setSelectionStart( SdrTableObj::getFirstCell() );
|
|
nAction = TblAction::EditCell;
|
|
}
|
|
break;
|
|
}
|
|
case awt::Key::HOME:
|
|
case awt::Key::NUM7:
|
|
{
|
|
if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
|
|
{
|
|
if( bMod1 && !bMod2 )
|
|
{
|
|
// ctrl + home jumps to first cell
|
|
nAction = TblAction::GotoFirstCell;
|
|
}
|
|
else if( !bMod1 && bMod2 )
|
|
{
|
|
// alt + home jumps to first column
|
|
nAction = TblAction::GotoFirstColumn;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case awt::Key::END:
|
|
case awt::Key::NUM1:
|
|
{
|
|
if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
|
|
{
|
|
if( bMod1 && !bMod2 )
|
|
{
|
|
// ctrl + end jumps to last cell
|
|
nAction = TblAction::GotoLastCell;
|
|
}
|
|
else if( !bMod1 && bMod2 )
|
|
{
|
|
// alt + home jumps to last column
|
|
nAction = TblAction::GotoLastColumn;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case awt::Key::TAB:
|
|
{
|
|
if( bTextEdit || mbCellSelectionMode )
|
|
nAction = TblAction::Tab;
|
|
break;
|
|
}
|
|
|
|
case awt::Key::UP:
|
|
case awt::Key::NUM8:
|
|
case awt::Key::DOWN:
|
|
case awt::Key::NUM2:
|
|
case awt::Key::LEFT:
|
|
case awt::Key::NUM4:
|
|
case awt::Key::RIGHT:
|
|
case awt::Key::NUM6:
|
|
{
|
|
|
|
if( !bMod1 && bMod2 )
|
|
{
|
|
if(bTextEdit || mbCellSelectionMode)
|
|
{
|
|
if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
|
|
{
|
|
nAction = TblAction::GotoLeftCell;
|
|
break;
|
|
}
|
|
else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
|
|
{
|
|
nAction = TblAction::GotoRightCell;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bTextMove = false;
|
|
OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
|
|
if( pOLV )
|
|
{
|
|
RemoveSelection();
|
|
// during text edit, check if we navigate out of the cell
|
|
ESelection aOldSelection = pOLV->GetSelection();
|
|
pOLV->PostKeyEvent(rKEvt);
|
|
bTextMove = aOldSelection == pOLV->GetSelection();
|
|
if( !bTextMove )
|
|
{
|
|
nAction = TblAction::NONE;
|
|
}
|
|
}
|
|
|
|
if( mbCellSelectionMode || bTextMove )
|
|
{
|
|
// no text edit, navigate in cells if selection active
|
|
switch( nCode )
|
|
{
|
|
case awt::Key::LEFT:
|
|
case awt::Key::NUM4:
|
|
nAction = TblAction::GotoLeftCell;
|
|
break;
|
|
case awt::Key::RIGHT:
|
|
case awt::Key::NUM6:
|
|
nAction = TblAction::GotoRightCell;
|
|
break;
|
|
case awt::Key::DOWN:
|
|
case awt::Key::NUM2:
|
|
nAction = TblAction::GotoDownCell;
|
|
break;
|
|
case awt::Key::UP:
|
|
case awt::Key::NUM8:
|
|
nAction = TblAction::GotoUpCell;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case awt::Key::PAGEUP:
|
|
if( bMod2 )
|
|
nAction = TblAction::GotoFirstRow;
|
|
break;
|
|
|
|
case awt::Key::PAGEDOWN:
|
|
if( bMod2 )
|
|
nAction = TblAction::GotoLastRow;
|
|
break;
|
|
}
|
|
return nAction;
|
|
}
|
|
|
|
bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
|
|
{
|
|
rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
|
|
if( !pTableObj )
|
|
return false;
|
|
|
|
switch( nAction )
|
|
{
|
|
case TblAction::GotoFirstCell:
|
|
{
|
|
gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoLeftCell:
|
|
{
|
|
gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoRightCell:
|
|
{
|
|
gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoLastCell:
|
|
{
|
|
gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoFirstColumn:
|
|
{
|
|
CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
|
|
gotoCell( aPos, bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoLastColumn:
|
|
{
|
|
CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
|
|
gotoCell( aPos, bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoFirstRow:
|
|
{
|
|
CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
|
|
gotoCell( aPos, bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoUpCell:
|
|
{
|
|
gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoDownCell:
|
|
{
|
|
gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::GotoLastRow:
|
|
{
|
|
CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
|
|
gotoCell( aPos, bSelect, pWindow, nAction );
|
|
break;
|
|
}
|
|
|
|
case TblAction::EditCell:
|
|
EditCell( getSelectionStart(), pWindow, nAction );
|
|
break;
|
|
|
|
case TblAction::StopTextEdit:
|
|
StopTextEdit();
|
|
break;
|
|
|
|
case TblAction::RemoveSelection:
|
|
RemoveSelection();
|
|
break;
|
|
|
|
case TblAction::Tab:
|
|
{
|
|
if( bSelect )
|
|
gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
|
|
else
|
|
{
|
|
CellPos aSelectionEnd( getSelectionEnd() );
|
|
CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
|
|
if( aSelectionEnd == aNextCell )
|
|
{
|
|
onInsert( SID_TABLE_INSERT_ROW );
|
|
aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
|
|
}
|
|
gotoCell( aNextCell, false, pWindow, nAction );
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return nAction != TblAction::HandledByView;
|
|
}
|
|
|
|
|
|
void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
|
|
{
|
|
auto pTable = mxTableObj.get();
|
|
if( pTable && pTable->IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
if( bSelect )
|
|
{
|
|
maCursorLastPos = rPos;
|
|
if( pTable )
|
|
pTable->setActiveCell( rPos );
|
|
|
|
if( !mbCellSelectionMode )
|
|
{
|
|
setSelectedCells( maCursorFirstPos, rPos );
|
|
}
|
|
else
|
|
{
|
|
UpdateSelection( rPos );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemoveSelection();
|
|
EditCell( rPos, pWindow, nAction );
|
|
}
|
|
}
|
|
|
|
|
|
const CellPos& SvxTableController::getSelectionStart()
|
|
{
|
|
checkCell( maCursorFirstPos );
|
|
return maCursorFirstPos;
|
|
}
|
|
|
|
|
|
void SvxTableController::setSelectionStart( const CellPos& rPos )
|
|
{
|
|
maCursorFirstPos = rPos;
|
|
}
|
|
|
|
|
|
const CellPos& SvxTableController::getSelectionEnd()
|
|
{
|
|
checkCell( maCursorLastPos );
|
|
return maCursorLastPos;
|
|
}
|
|
|
|
|
|
void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
|
|
{
|
|
if(!checkTableObject() || !mxTable.is())
|
|
return;
|
|
|
|
try
|
|
{
|
|
Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
|
|
|
|
if( xRange->isMergeable() )
|
|
{
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
{
|
|
rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
|
|
rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
|
|
}
|
|
|
|
xRange->merge();
|
|
mbHasJustMerged = true;
|
|
setSelectedCells( maCursorFirstPos, maCursorFirstPos );
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
}
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "svx.table", "" );
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::checkCell( CellPos& rPos ) const
|
|
{
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
if( rPos.mnCol >= mxTable->getColumnCount() )
|
|
rPos.mnCol = mxTable->getColumnCount()-1;
|
|
|
|
if( rPos.mnRow >= mxTable->getRowCount() )
|
|
rPos.mnRow = mxTable->getRowCount()-1;
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("svx.table", "");
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::findMergeOrigin( CellPos& rPos )
|
|
{
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
try
|
|
{
|
|
Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
|
|
if( xCell->isMerged() )
|
|
{
|
|
::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
|
|
}
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION("svx.table", "");
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
|
|
{
|
|
SdrPageView* pPV(mrView.GetSdrPageView());
|
|
|
|
if(nullptr == pPV || !checkTableObject())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
|
|
if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
|
|
return;
|
|
|
|
bool bEmptyOutliner = false;
|
|
|
|
if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
|
|
{
|
|
::Outliner* pOutl = mrView.GetTextEditOutliner();
|
|
sal_Int32 nParaCnt = pOutl->GetParagraphCount();
|
|
Paragraph* p1stPara = pOutl->GetParagraph( 0 );
|
|
|
|
if(nParaCnt==1 && p1stPara)
|
|
{
|
|
// with only one paragraph
|
|
if (pOutl->GetText(p1stPara).isEmpty())
|
|
{
|
|
bEmptyOutliner = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
CellPos aPos( rPos );
|
|
findMergeOrigin( aPos );
|
|
|
|
if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
|
|
return;
|
|
|
|
if( rTableObj.IsTextEditActive() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
rTableObj.setActiveCell( aPos );
|
|
|
|
// create new outliner, owner will be the SdrObjEditView
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
|
|
|
|
if (pOutl && rTableObj.IsVerticalWriting())
|
|
pOutl->SetVertical( true );
|
|
|
|
if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
|
|
return;
|
|
|
|
maCursorLastPos = maCursorFirstPos = rPos;
|
|
|
|
OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
|
|
|
|
// Move cursor to end of text
|
|
ESelection aNewSelection;
|
|
|
|
const WritingMode eMode = rTableObj.GetWritingMode();
|
|
if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
|
|
{
|
|
const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
|
|
((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
|
|
|
|
if( bLast )
|
|
aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
|
|
}
|
|
pOLV->SetSelection(aNewSelection);
|
|
}
|
|
|
|
|
|
void SvxTableController::StopTextEdit()
|
|
{
|
|
if(mrView.IsTextEdit())
|
|
{
|
|
mrView.SdrEndTextEdit();
|
|
mrView.SetCurrentObj(SdrObjKind::Table);
|
|
mrView.SetEditMode(SdrViewEditMode::Edit);
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
|
|
{
|
|
if( mbCellSelectionMode )
|
|
{
|
|
checkCell( maCursorFirstPos );
|
|
checkCell( maCursorLastPos );
|
|
|
|
rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
|
|
rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
|
|
rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
|
|
rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
|
|
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
bool bExt = false;
|
|
do
|
|
{
|
|
bExt = false;
|
|
for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
|
|
{
|
|
Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
|
|
if( !xCell.is() )
|
|
continue;
|
|
|
|
if( xCell->isMerged() )
|
|
{
|
|
CellPos aPos( nCol, nRow );
|
|
findMergeOrigin( aPos );
|
|
if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
|
|
{
|
|
rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
|
|
rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
|
|
bExt = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
|
|
{
|
|
rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
|
|
rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
|
|
bExt = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(bExt);
|
|
}
|
|
else if(mrView.IsTextEdit())
|
|
{
|
|
rFirst = getSelectionStart();
|
|
findMergeOrigin( rFirst );
|
|
rLast = rFirst;
|
|
|
|
if( mxTable.is() )
|
|
{
|
|
Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
|
|
if( xCell.is() )
|
|
{
|
|
rLast.mnCol += xCell->getColumnSpan() - 1;
|
|
rLast.mnRow += xCell->getRowSpan() - 1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rFirst.mnCol = 0;
|
|
rFirst.mnRow = 0;
|
|
if( mxTable.is() )
|
|
{
|
|
rLast.mnRow = mxTable->getRowCount()-1;
|
|
rLast.mnCol = mxTable->getColumnCount()-1;
|
|
}
|
|
else
|
|
{
|
|
rLast.mnRow = 0;
|
|
rLast.mnCol = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::StartSelection( const CellPos& rPos )
|
|
{
|
|
StopTextEdit();
|
|
mbCellSelectionMode = true;
|
|
maCursorLastPos = maCursorFirstPos = rPos;
|
|
mrView.MarkListHasChanged();
|
|
}
|
|
|
|
|
|
void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
|
|
{
|
|
StopTextEdit();
|
|
mbCellSelectionMode = true;
|
|
maCursorFirstPos = rStart;
|
|
UpdateSelection( rEnd );
|
|
}
|
|
|
|
|
|
bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
|
|
{
|
|
if(!checkTableObject() || !mxTable.is())
|
|
return false;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
|
|
if (mrView.IsTextEdit())
|
|
return true;
|
|
|
|
CellPos aStart, aEnd;
|
|
|
|
if(hasSelectedCells())
|
|
{
|
|
getSelectedCells(aStart, aEnd);
|
|
}
|
|
else
|
|
{
|
|
aStart.mnRow = 0;
|
|
aStart.mnCol = 0;
|
|
aEnd.mnRow = mxTable->getRowCount() - 1;
|
|
aEnd.mnCol = mxTable->getColumnCount() - 1;
|
|
}
|
|
|
|
for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
|
|
{
|
|
for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
|
|
{
|
|
CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
|
|
if (xCell.is())
|
|
{
|
|
if (rModel.IsUndoEnabled())
|
|
xCell->AddUndo();
|
|
|
|
SfxItemSet aCellSet(xCell->GetItemSet());
|
|
if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
|
|
{
|
|
xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateTableShape();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void SvxTableController::UpdateSelection( const CellPos& rPos )
|
|
{
|
|
maCursorLastPos = rPos;
|
|
mrView.MarkListHasChanged();
|
|
}
|
|
|
|
|
|
void SvxTableController::clearSelection()
|
|
{
|
|
RemoveSelection();
|
|
}
|
|
|
|
|
|
void SvxTableController::selectAll()
|
|
{
|
|
if( mxTable.is() )
|
|
{
|
|
CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
|
|
if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
|
|
{
|
|
CellPos aPos1;
|
|
setSelectedCells( aPos1, aPos2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::RemoveSelection()
|
|
{
|
|
if( mbCellSelectionMode )
|
|
{
|
|
mbCellSelectionMode = false;
|
|
mrView.MarkListHasChanged();
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::onTableModified()
|
|
{
|
|
if( mnUpdateEvent == nullptr )
|
|
mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
|
|
}
|
|
|
|
|
|
void SvxTableController::updateSelectionOverlay()
|
|
{
|
|
// There is no need to update selection overlay after merging cells
|
|
// since the selection overlay should remain the same
|
|
if ( mbHasJustMerged )
|
|
return;
|
|
|
|
destroySelectionOverlay();
|
|
if( !mbCellSelectionMode )
|
|
return;
|
|
|
|
rtl::Reference<sdr::table::SdrTableObj> pTableObj = mxTableObj.get();
|
|
if( !pTableObj )
|
|
return;
|
|
|
|
sdr::overlay::OverlayObjectCell::RangeVector aRanges;
|
|
|
|
tools::Rectangle aStartRect, aEndRect;
|
|
CellPos aStart,aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
pTableObj->getCellBounds( aStart, aStartRect );
|
|
|
|
basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
|
|
a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
|
|
|
|
findMergeOrigin( aEnd );
|
|
pTableObj->getCellBounds( aEnd, aEndRect );
|
|
a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
|
|
a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
|
|
aRanges.push_back( a2DRange );
|
|
|
|
::Color aHighlight( COL_BLUE );
|
|
OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
|
|
if( pOutDev )
|
|
aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
|
|
|
|
const sal_uInt32 nCount = mrView.PaintWindowCount();
|
|
for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
|
|
{
|
|
SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
|
|
if( pPaintWindow )
|
|
{
|
|
const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
|
|
if( xOverlayManager.is() )
|
|
{
|
|
std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, std::vector(aRanges) ));
|
|
|
|
xOverlayManager->add(*pOverlay);
|
|
mpSelectionOverlay.emplace();
|
|
mpSelectionOverlay->append(std::move(pOverlay));
|
|
}
|
|
}
|
|
}
|
|
|
|
// If tiled rendering, emit callbacks for sdr table selection.
|
|
if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
|
|
return;
|
|
|
|
tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
|
|
|
|
if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
|
|
aSelection = o3tl::toTwips(aSelection, o3tl::Length::mm100);
|
|
|
|
if(SfxViewShell* pViewShell = SfxViewShell::Current())
|
|
{
|
|
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString());
|
|
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString());
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::destroySelectionOverlay()
|
|
{
|
|
if( !mpSelectionOverlay )
|
|
return;
|
|
|
|
mpSelectionOverlay.reset();
|
|
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
// Clear the LOK text selection so far provided by this table.
|
|
if(SfxViewShell* pViewShell = SfxViewShell::Current())
|
|
{
|
|
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY"_ostr);
|
|
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY"_ostr);
|
|
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY"_ostr);
|
|
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY"_ostr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
|
|
{
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
CellPos aStart, aEnd;
|
|
const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
|
|
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() && !xCell->isMerged() )
|
|
{
|
|
const SfxItemSet& rSet = xCell->GetItemSet();
|
|
SfxWhichIter aIter(rSet);
|
|
sal_uInt16 nWhich(aIter.FirstWhich());
|
|
while(nWhich)
|
|
{
|
|
SfxItemState nState = aIter.GetItemState(false);
|
|
if(!bOnlyHardAttr)
|
|
{
|
|
if(SfxItemState::INVALID == nState)
|
|
rAttr.InvalidateItem(nWhich);
|
|
else
|
|
rAttr.MergeValue(rSet.Get(nWhich), true);
|
|
}
|
|
else if(SfxItemState::SET == nState)
|
|
{
|
|
const SfxPoolItem& rItem = rSet.Get(nWhich);
|
|
rAttr.MergeValue(rItem, true);
|
|
}
|
|
|
|
nWhich = aIter.NextWhich();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
|
|
{
|
|
if( pNew )
|
|
{
|
|
const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
|
|
if( pOld )
|
|
{
|
|
SvxBorderLine aNewLine( *pNew );
|
|
aNewLine.SetColor( pOld->GetColor() );
|
|
rNewFrame.SetLine( &aNewLine, nLine );
|
|
return;
|
|
}
|
|
}
|
|
rNewFrame.SetLine( pNew, nLine );
|
|
}
|
|
|
|
|
|
static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
|
|
{
|
|
if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
|
|
{
|
|
// current cell is outside the selection
|
|
|
|
if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
|
|
{
|
|
if (nCellPosFlags & CellPosFlag::Upper)
|
|
{
|
|
if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
|
|
rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
|
|
}
|
|
else if (nCellPosFlags & CellPosFlag::Lower)
|
|
{
|
|
if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
|
|
rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
|
|
}
|
|
}
|
|
else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
|
|
{
|
|
if (nCellPosFlags & CellPosFlag::Before)
|
|
{
|
|
if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
|
|
rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
|
|
}
|
|
else if (nCellPosFlags & CellPosFlag::After)
|
|
{
|
|
if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
|
|
rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// current cell is inside the selection
|
|
|
|
if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
|
|
: pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
|
|
rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
|
|
|
|
if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
|
|
rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
|
|
|
|
if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
|
|
rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
|
|
|
|
if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
|
|
rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
|
|
|
|
// apply distance to borders
|
|
if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
|
|
for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
|
|
rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
|
|
}
|
|
}
|
|
|
|
|
|
static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
|
|
{
|
|
const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
|
|
if( pSourceLine )
|
|
{
|
|
SvxBorderLine aLine( *pSourceLine );
|
|
aLine.SetColor( rColor );
|
|
rNewFrame.SetLine( &aLine, nLine );
|
|
}
|
|
}
|
|
|
|
|
|
static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
|
|
{
|
|
const Color aColor( pLineColorItem->GetValue() );
|
|
|
|
if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
|
|
ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
|
|
|
|
if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
|
|
ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
|
|
|
|
if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
|
|
ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
|
|
|
|
if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
|
|
ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
|
|
}
|
|
|
|
|
|
static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
|
|
{
|
|
if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
|
|
{
|
|
if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
|
|
{
|
|
if (nCellPosFlags & CellPosFlag::Upper)
|
|
{
|
|
if( rNewFrame.GetBottom() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
|
|
}
|
|
else if (nCellPosFlags & CellPosFlag::Lower)
|
|
{
|
|
if( rNewFrame.GetTop() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
|
|
}
|
|
}
|
|
else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
|
|
{
|
|
if (nCellPosFlags & CellPosFlag::Before)
|
|
{
|
|
if( rNewFrame.GetRight() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
|
|
}
|
|
else if (nCellPosFlags & CellPosFlag::After)
|
|
{
|
|
if( rNewFrame.GetLeft() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( rNewFrame.GetBottom() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
|
|
if( rNewFrame.GetTop() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
|
|
if( rNewFrame.GetRight() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
|
|
if( rNewFrame.GetLeft() )
|
|
ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
|
|
{
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
const sal_Int32 nRowCount = mxTable->getRowCount();
|
|
const sal_Int32 nColCount = mxTable->getColumnCount();
|
|
if( !(nRowCount && nColCount) )
|
|
return;
|
|
|
|
const SvxBoxItem* pBoxItem = nullptr;
|
|
if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
|
|
pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
|
|
|
|
const SvxBoxInfoItem* pBoxInfoItem = nullptr;
|
|
if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
|
|
pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
|
|
|
|
const SvxColorItem* pLineColorItem = nullptr;
|
|
if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
|
|
pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
|
|
|
|
const SvxBorderLine* pBorderLineItem = nullptr;
|
|
if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
|
|
pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
|
|
|
|
if( pBoxInfoItem && !pBoxItem )
|
|
{
|
|
const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
|
|
pBoxItem = &gaEmptyBoxItem;
|
|
}
|
|
else if( pBoxItem && !pBoxInfoItem )
|
|
{
|
|
const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
|
|
pBoxInfoItem = &gaEmptyBoxInfoItem;
|
|
}
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
|
|
const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
|
|
|
|
for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
|
|
{
|
|
CellPosFlag nRowFlags = CellPosFlag::NONE;
|
|
nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
|
|
nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
|
|
nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
|
|
nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
|
|
|
|
for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( !xCell.is() )
|
|
continue;
|
|
|
|
const SfxItemSet& rSet = xCell->GetItemSet();
|
|
const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
|
|
|
|
SvxBoxItem aNewFrame( *pOldOuter );
|
|
|
|
CellPosFlag nCellPosFlags = nRowFlags;
|
|
nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
|
|
nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
|
|
nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
|
|
nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
|
|
|
|
if( pBoxItem && pBoxInfoItem )
|
|
ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
|
|
|
|
if( pLineColorItem )
|
|
ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
|
|
|
|
if( pBorderLineItem )
|
|
ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
|
|
|
|
if (aNewFrame != *pOldOuter)
|
|
{
|
|
SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
|
|
aAttr.Put(aNewFrame);
|
|
xCell->SetMergedItemSetAndBroadcast( aAttr, false );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SvxTableController::UpdateTableShape()
|
|
{
|
|
rtl::Reference<SdrObject> pTableObj = mxTableObj.get();
|
|
if( pTableObj )
|
|
{
|
|
pTableObj->ActionChanged();
|
|
pTableObj->BroadcastObjectChange();
|
|
}
|
|
updateSelectionOverlay();
|
|
}
|
|
|
|
|
|
void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
|
|
{
|
|
if(!checkTableObject() || !mxTable.is())
|
|
return;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
|
|
aAttr.Put(rAttr);
|
|
|
|
const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
|
|
|
|
if( bFrame )
|
|
{
|
|
aAttr.ClearItem( SDRATTR_TABLE_BORDER );
|
|
aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
|
|
}
|
|
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() )
|
|
{
|
|
if( bUndo )
|
|
xCell->AddUndo();
|
|
xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bFrame )
|
|
{
|
|
ApplyBorderAttr( rAttr );
|
|
}
|
|
|
|
UpdateTableShape();
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
}
|
|
|
|
void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
|
|
{
|
|
if (!checkTableObject() || !mxTable.is())
|
|
return;
|
|
|
|
// Filter out non-shadow items from rAttr.
|
|
SfxItemSetFixed<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST> aSet(*rAttr.GetPool());
|
|
aSet.Put(rAttr);
|
|
|
|
if (!aSet.Count())
|
|
{
|
|
// If there are no items to be applied on the shape, then don't set anything, it would
|
|
// terminate text edit without a good reason, which affects undo/redo.
|
|
return;
|
|
}
|
|
|
|
// Set shadow items on the marked shape.
|
|
mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
|
|
}
|
|
|
|
bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
|
|
{
|
|
if( mxTableObj.get().is() && hasSelectedCells() )
|
|
{
|
|
MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
|
|
|
|
if( mrView.IsTextEdit() )
|
|
{
|
|
OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
|
|
if(pTextEditOutlinerView)
|
|
{
|
|
// FALSE= consider InvalidItems not as the default, but as "holes"
|
|
rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
|
|
{
|
|
if( mbCellSelectionMode || mrView.IsTextEdit() )
|
|
{
|
|
SetAttrToSelectedCells( rSet, bReplaceAll );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
rtl::Reference<SdrObject> SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
|
|
{
|
|
rtl::Reference<SdrTableObj> pRetval;
|
|
sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
|
|
|
|
if(nullptr == pCurrentSdrTableObj)
|
|
{
|
|
return pRetval;
|
|
}
|
|
|
|
if(!mxTableObj.get().is())
|
|
{
|
|
return pRetval;
|
|
}
|
|
|
|
// get selection and create full selection
|
|
CellPos aStart, aEnd;
|
|
const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
|
|
|
|
getSelectedCells(aStart, aEnd);
|
|
|
|
// compare to see if we have a partial selection
|
|
if(aStart != aFullStart || aEnd != aFullEnd)
|
|
{
|
|
// create full clone
|
|
pRetval = SdrObject::Clone(*pCurrentSdrTableObj, rTargetModel);
|
|
|
|
// limit SdrObject's TableModel to partial selection
|
|
pRetval->CropTableModelToSelection(aStart, aEnd);
|
|
}
|
|
|
|
return pRetval;
|
|
}
|
|
|
|
bool SvxTableController::PasteObjModel( const SdrModel& rModel )
|
|
{
|
|
if( mxTableObj.get().is() && (rModel.GetPageCount() >= 1) )
|
|
{
|
|
const SdrPage* pPastePage = rModel.GetPage(0);
|
|
if( pPastePage && pPastePage->GetObjCount() == 1 )
|
|
{
|
|
SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
|
|
if( pPasteTableObj )
|
|
{
|
|
return PasteObject( pPasteTableObj );
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
|
|
{
|
|
if( !pPasteTableObj )
|
|
return false;
|
|
|
|
Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
|
|
if( !xPasteTable.is() )
|
|
return false;
|
|
|
|
if( !mxTable.is() )
|
|
return false;
|
|
|
|
sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
|
|
sal_Int32 nPasteRows = xPasteTable->getRowCount();
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
|
|
if( mrView.IsTextEdit() )
|
|
mrView.SdrEndTextEdit(true);
|
|
|
|
sal_Int32 nColumns = mxTable->getColumnCount();
|
|
sal_Int32 nRows = mxTable->getRowCount();
|
|
|
|
const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
|
|
if( nMissing > 0 )
|
|
{
|
|
Reference< XTableRows > xRows( mxTable->getRows() );
|
|
xRows->insertByIndex( nRows, nMissing );
|
|
nRows = mxTable->getRowCount();
|
|
}
|
|
|
|
nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
|
|
nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
|
|
|
|
// copy cell contents
|
|
for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
|
|
{
|
|
for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
|
|
{
|
|
CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
|
|
if( xTargetCell.is() && !xTargetCell->isMerged() )
|
|
{
|
|
CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
|
|
if (xSourceCell.is())
|
|
{
|
|
xTargetCell->AddUndo();
|
|
// Prevent cell span exceeding the pasting range.
|
|
if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
|
|
xTargetCell->replaceContentAndFormatting(xSourceCell);
|
|
else
|
|
xTargetCell->cloneFrom(xSourceCell);
|
|
|
|
nCol += xSourceCell->getColumnSpan() - 1;
|
|
nTargetCol += xTargetCell->getColumnSpan();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateTableShape();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SvxTableController::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, sal_Int16 nDepth,
|
|
bool bNoCharacterFormats, bool bNoParagraphFormats)
|
|
{
|
|
if(!mbCellSelectionMode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(!checkTableObject())
|
|
return false;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
|
|
const bool bUndo(rModel.IsUndoEnabled());
|
|
|
|
if( bUndo )
|
|
rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
|
|
|
|
CellPos aStart, aEnd;
|
|
getSelectedCells( aStart, aEnd );
|
|
const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
|
|
|
|
for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
|
|
{
|
|
for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( xCell.is() )
|
|
{
|
|
if (bUndo)
|
|
xCell->AddUndo();
|
|
SdrText* pText = xCell.get();
|
|
SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, nDepth, bNoCharacterFormats, bNoParagraphFormats );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( bFrame )
|
|
{
|
|
ApplyBorderAttr( rFormatSet );
|
|
}
|
|
|
|
UpdateTableShape();
|
|
|
|
if( bUndo )
|
|
rModel.EndUndo();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
|
|
{
|
|
mnUpdateEvent = nullptr;
|
|
|
|
if( mbCellSelectionMode )
|
|
{
|
|
CellPos aStart( maCursorFirstPos );
|
|
CellPos aEnd( maCursorLastPos );
|
|
checkCell(aStart);
|
|
checkCell(aEnd);
|
|
if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
|
|
{
|
|
setSelectedCells( aStart, aEnd );
|
|
}
|
|
}
|
|
|
|
updateSelectionOverlay();
|
|
mbHasJustMerged = false;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
struct LinesState
|
|
{
|
|
LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
|
|
: rBoxItem(rBoxItem_)
|
|
, rBoxInfoItem(rBoxInfoItem_)
|
|
, bDistanceIndeterminate(false)
|
|
{
|
|
aBorderSet.fill(false);
|
|
aInnerLineSet.fill(false);
|
|
aBorderIndeterminate.fill(false);
|
|
aInnerLineIndeterminate.fill(false);
|
|
aDistanceSet.fill(false);
|
|
aDistance.fill(0);
|
|
}
|
|
|
|
SvxBoxItem& rBoxItem;
|
|
SvxBoxInfoItem& rBoxInfoItem;
|
|
o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
|
|
o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
|
|
o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
|
|
o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
|
|
o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
|
|
o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
|
|
bool bDistanceIndeterminate;
|
|
};
|
|
|
|
class BoxItemWrapper
|
|
{
|
|
public:
|
|
BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
|
|
|
|
const SvxBorderLine* getLine() const;
|
|
void setLine(const SvxBorderLine* pLine);
|
|
|
|
private:
|
|
SvxBoxItem& m_rBoxItem;
|
|
SvxBoxInfoItem& m_rBoxInfoItem;
|
|
const SvxBoxItemLine m_nBorderLine;
|
|
const SvxBoxInfoItemLine m_nInnerLine;
|
|
const bool m_bBorder;
|
|
};
|
|
|
|
BoxItemWrapper::BoxItemWrapper(
|
|
SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
|
|
const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
|
|
: m_rBoxItem(rBoxItem)
|
|
, m_rBoxInfoItem(rBoxInfoItem)
|
|
, m_nBorderLine(nBorderLine)
|
|
, m_nInnerLine(nInnerLine)
|
|
, m_bBorder(bBorder)
|
|
{
|
|
}
|
|
|
|
const SvxBorderLine* BoxItemWrapper::getLine() const
|
|
{
|
|
if (m_bBorder)
|
|
return m_rBoxItem.GetLine(m_nBorderLine);
|
|
else
|
|
return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
|
|
}
|
|
|
|
void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
|
|
{
|
|
if (m_bBorder)
|
|
m_rBoxItem.SetLine(pLine, m_nBorderLine);
|
|
else
|
|
m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
|
|
}
|
|
|
|
void lcl_MergeBorderLine(
|
|
LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
|
|
SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
|
|
{
|
|
const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
|
|
BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
|
|
bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
|
|
|
|
if (rbSet)
|
|
{
|
|
bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
|
|
if (!rbIndeterminate)
|
|
{
|
|
const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
|
|
if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
|
|
{
|
|
aBoxItem.setLine(nullptr);
|
|
rbIndeterminate = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aBoxItem.setLine(pLine);
|
|
rbSet = true;
|
|
}
|
|
}
|
|
|
|
void lcl_MergeBorderOrInnerLine(
|
|
LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
|
|
SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
|
|
{
|
|
if (bBorder)
|
|
lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
|
|
else
|
|
{
|
|
const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
|
|
lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
|
|
}
|
|
}
|
|
|
|
void lcl_MergeDistance(
|
|
LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
|
|
{
|
|
if (rLinesState.aDistanceSet[nIndex])
|
|
{
|
|
if (!rLinesState.bDistanceIndeterminate)
|
|
rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
|
|
}
|
|
else
|
|
{
|
|
rLinesState.aDistance[nIndex] = nDistance;
|
|
rLinesState.aDistanceSet[nIndex] = true;
|
|
}
|
|
}
|
|
|
|
void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
|
|
{
|
|
if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
|
|
{
|
|
// current cell is outside the selection
|
|
|
|
if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
|
|
{
|
|
if (nCellPosFlags & CellPosFlag::Upper)
|
|
lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
|
|
else if (nCellPosFlags & CellPosFlag::Lower)
|
|
lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
|
|
}
|
|
else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
|
|
{
|
|
if (nCellPosFlags & CellPosFlag::Before)
|
|
lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
|
|
else if (nCellPosFlags & CellPosFlag::After)
|
|
lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
|
|
}
|
|
|
|
// NOTE: inner distances for cells outside the selected range
|
|
// are not relevant -> we ignore them.
|
|
}
|
|
else
|
|
{
|
|
// current cell is inside the selection
|
|
|
|
lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
|
|
lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
|
|
lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
|
|
lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
|
|
|
|
lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
|
|
lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
|
|
lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
|
|
lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
|
|
{
|
|
if( !mxTable.is() )
|
|
return;
|
|
|
|
const sal_Int32 nRowCount = mxTable->getRowCount();
|
|
const sal_Int32 nColCount = mxTable->getColumnCount();
|
|
if( !(nRowCount && nColCount) )
|
|
return;
|
|
|
|
CellPos aStart, aEnd;
|
|
const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
|
|
|
|
// We are adding one more row/column around the block of selected cells.
|
|
// We will be checking the adjoining border of these too.
|
|
const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
|
|
const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
|
|
|
|
rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
|
|
LinesState aLinesState( rBoxItem, rBoxInfoItem );
|
|
|
|
/* Here we go through all the selected cells (enhanced by
|
|
* the adjoining row/column on each side) and determine the
|
|
* lines for presentation. The algorithm is simple:
|
|
* 1. if a border or inner line is set (or unset) in all
|
|
* cells to the same value, it will be used.
|
|
* 2. if a border or inner line is set only in some cells,
|
|
* it will be set to indeterminate state (SetValid() on
|
|
* rBoxInfoItem).
|
|
*/
|
|
for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
|
|
{
|
|
CellPosFlag nRowFlags = CellPosFlag::NONE;
|
|
nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
|
|
nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
|
|
nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
|
|
nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
|
|
|
|
for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
|
|
{
|
|
CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
|
|
if( !xCell.is() )
|
|
continue;
|
|
|
|
CellPosFlag nCellPosFlags = nRowFlags;
|
|
nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
|
|
nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
|
|
nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
|
|
nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
|
|
|
|
const SfxItemSet& rSet = xCell->GetItemSet();
|
|
SvxBoxItem aCellBoxItem(TextDistancesToSvxBoxItem(rSet));
|
|
lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
|
|
}
|
|
}
|
|
|
|
if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
|
|
if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
|
|
if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
|
|
if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
|
|
if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
|
|
if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
|
|
|
|
if (!aLinesState.bDistanceIndeterminate)
|
|
{
|
|
if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
|
|
aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
|
|
if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
|
|
aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
|
|
if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
|
|
aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
|
|
if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
|
|
aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
|
|
aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
|
|
}
|
|
}
|
|
|
|
bool SvxTableController::selectRow( sal_Int32 row )
|
|
{
|
|
if( !mxTable.is() )
|
|
return false;
|
|
CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
|
|
StartSelection( aEnd );
|
|
gotoCell( aStart, true, nullptr );
|
|
return true;
|
|
}
|
|
|
|
bool SvxTableController::selectColumn( sal_Int32 column )
|
|
{
|
|
if( !mxTable.is() )
|
|
return false;
|
|
CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
|
|
StartSelection( aEnd );
|
|
gotoCell( aStart, true, nullptr );
|
|
return true;
|
|
}
|
|
|
|
bool SvxTableController::deselectRow( sal_Int32 row )
|
|
{
|
|
if( !mxTable.is() )
|
|
return false;
|
|
CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
|
|
StartSelection( aEnd );
|
|
gotoCell( aStart, false, nullptr );
|
|
return true;
|
|
}
|
|
|
|
bool SvxTableController::deselectColumn( sal_Int32 column )
|
|
{
|
|
if( !mxTable.is() )
|
|
return false;
|
|
CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
|
|
StartSelection( aEnd );
|
|
gotoCell( aStart, false, nullptr );
|
|
return true;
|
|
}
|
|
|
|
bool SvxTableController::isRowSelected( sal_Int32 nRow )
|
|
{
|
|
if( hasSelectedCells() )
|
|
{
|
|
CellPos aFirstPos, aLastPos;
|
|
getSelectedCells( aFirstPos, aLastPos );
|
|
if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
|
|
{
|
|
if( hasSelectedCells() )
|
|
{
|
|
CellPos aFirstPos, aLastPos;
|
|
getSelectedCells( aFirstPos, aLastPos );
|
|
if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SvxTableController::isRowHeader()
|
|
{
|
|
if(!checkTableObject())
|
|
return false;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
|
|
|
|
return aSettings.mbUseFirstRow;
|
|
}
|
|
|
|
bool SvxTableController::isColumnHeader()
|
|
{
|
|
if(!checkTableObject())
|
|
return false;
|
|
|
|
SdrTableObj& rTableObj(*mxTableObj.get());
|
|
TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
|
|
|
|
return aSettings.mbUseFirstColumn;
|
|
}
|
|
|
|
bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
|
|
{
|
|
rtl::Reference<SdrTableObj> pTableObj = mxTableObj.get();
|
|
if (pTableObj->GetObjIdentifier() != SdrObjKind::Table)
|
|
return false;
|
|
|
|
CellPos aCellPos;
|
|
if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
|
|
{
|
|
// Position is a table cell.
|
|
if (mbCellSelectionMode)
|
|
{
|
|
// We have a table selection already: adjust the point or the mark.
|
|
if (bPoint)
|
|
setSelectedCells(maCursorFirstPos, aCellPos);
|
|
else
|
|
setSelectedCells(aCellPos, maCursorLastPos);
|
|
return true;
|
|
}
|
|
else if (aCellPos != maMouseDownPos)
|
|
{
|
|
// No selection, but rPosition is at another cell: start table selection.
|
|
StartSelection(maMouseDownPos);
|
|
// Update graphic selection, should be hidden now.
|
|
mrView.AdjustMarkHdl();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|