3363 lines
106 KiB
C++
3363 lines
106 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 {
|
|
|
|
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;
|
|
|
|
mxTable = mxTableObj.get()->getUnoTable();
|
|
if( mxTable )
|
|
{
|
|
mxModifyListener = new SvxTableControllerModifyListener( this );
|
|
mxTable->addModifyListener( mxModifyListener );
|
|
}
|
|
}
|
|
|
|
SvxTableController::~SvxTableController()
|
|
{
|
|
if( mnUpdateEvent )
|
|
{
|
|
Application::RemoveUserEvent( mnUpdateEvent );
|
|
}
|
|
|
|
if( mxModifyListener.is() && mxTableObj.get() )
|
|
{
|
|
rtl::Reference< TableModel > xTable( mxTableObj.get()->getUnoTable() );
|
|
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( mxTable->getCell( nPropSrcCol, nRow ) );
|
|
|
|
// 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( mxTable->getCell( nSpanInfoCol, nRow ) );
|
|
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( mxTable->getCell( nNewStartColumn + nOffset, nRow ) );
|
|
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( mxTable->getCell( nCol, nPropSrcRow ) );
|
|
|
|
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( mxTable->getCell( nCol, nSpanInfoRow ) );
|
|
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( mxTable->getCell( nCol, nNewRowStart + nOffset ) );
|
|
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=std::move(xBoxItem),
|
|
xBoxInfoItem=std::move(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( u"table"_ustr ), 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( mxTable->getCell( nCol, nRow ) );
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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::AtEnd();
|
|
}
|
|
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(mxTable->getCell(nCol, nRow));
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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));
|
|
}
|
|
else if(SfxItemState::SET == nState)
|
|
{
|
|
const SfxPoolItem& rItem = rSet.Get(nWhich);
|
|
rAttr.MergeValue(rItem);
|
|
}
|
|
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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;
|
|
|
|
const rtl::Reference< TableModel >& xPasteTable( pPasteTableObj->getUnoTable() );
|
|
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( mxTable->getCell( nTargetCol, aStart.mnRow + nRow ) );
|
|
if( xTargetCell.is() && !xTargetCell->isMerged() )
|
|
{
|
|
CellRef xSourceCell(xPasteTable->getCell(nCol, nRow));
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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( mxTable->getCell( nCol, nRow ) );
|
|
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: */
|