Quelle CustomAnimationPane.cxx
Sprache: 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 <com/sun/star/presentation/EffectPresetClass.hpp>
#include <com/sun/star/animations/XAnimationNodeSupplier.hpp>
#include <com/sun/star/animations/AnimationNodeType.hpp>
#include <com/sun/star/animations/ParallelTimeContainer.hpp>
#include <com/sun/star/view/XSelectionSupplier.hpp>
#include <com/sun/star/document/XActionLockable.hpp>
#include <com/sun/star/drawing/XDrawView.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/presentation/EffectCommands.hpp>
#include <com/sun/star/animations/AnimationTransformType.hpp>
#include <com/sun/star/text/XTextRangeCompare.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <comphelper/processfactory.hxx>
#include <comphelper/scopeguard.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/viewfrm.hxx>
#include <tools/debug.hxx>
#include "STLPropertySet.hxx"
#include <CustomAnimationPane.hxx>
#include "CustomAnimationDialog.hxx"
#include <CustomAnimationList.hxx>
#include "motionpathtag.hxx"
#include <CustomAnimationPreset.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/sequence.hxx>
#include <sfx2/frame.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <svx/svxids.hrc>
#include <DrawDocShell.hxx>
#include <ViewShellBase.hxx>
#include <DrawViewShell.hxx>
#include <DrawController.hxx>
#include <sdresid.hxx>
#include <drawview.hxx>
#include <slideshow.hxx>
#include <undoanim.hxx>
#include <optsitem.hxx>
#include <sdmod.hxx>
#include <framework/FrameworkHelper.hxx>
#include <EventMultiplexer.hxx>
#include <strings.hrc>
#include <sdpage.hxx>
#include <app.hrc>
#include <svx/strings.hrc>
#include <svx/dialmgr.hxx>
#include <algorithm>
#include <memory>
using namespace ::com::sun::star;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using ::com::sun::star::view::XSelectionSupplier;
using ::com::sun::star::beans::XPropertySet;
using ::com::sun::star::container::XIndexAccess;
using ::com::sun::star::container::XEnumerationAccess;
using ::com::sun::star::container::XEnumeration;
using ::com::sun::star::text::XText;
using ::sd::framework::FrameworkHelper;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Exception;
namespace sd {
void fillRepeatComboBox(weld::ComboBox& rBox)
{
OUString aNone( SdResId( STR_CUSTOMANIMATION_REPEAT_NONE ) );
rBox.append_text(aNone);
rBox.append_text(OUString::number(2 ));
rBox.append_text(OUString::number(3 ));
rBox.append_text(OUString::number(4 ));
rBox.append_text(OUString::number(5 ));
rBox.append_text(OUString::number(10 ));
OUString aUntilClick( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_NEXT_CLICK ) );
rBox.append_text(aUntilClick);
OUString aEndOfSlide( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_END_OF_SLIDE ) );
rBox.append_text(aEndOfSlide);
}
CustomAnimationPane::CustomAnimationPane( weld::Widget* pParent, ViewShellBase& rBase )
: PanelLayout(pParent, u"CustomAnimationsPanel" _ustr, u"modules/simpress/ui/customanimationspanel.ui" _ustr)
, mrBase(rBase)
// load resources
, mxFTAnimation(m_xBuilder->weld_label(u"effectlabel" _ustr))
, mxCustomAnimationList(new CustomAnimationList(m_xBuilder->weld_tree_view(u"custom_animation_list" _ustr),
m_xBuilder->weld_label(u"custom_animation_label" _ustr),
m_xBuilder->weld_widget(u"custom_animation_label_parent" _ustr)))
, mxPBAddEffect(m_xBuilder->weld_button(u"add_effect" _ustr))
, mxPBRemoveEffect(m_xBuilder->weld_button(u"remove_effect" _ustr))
, mxPBMoveUp(m_xBuilder->weld_button(u"move_up" _ustr))
, mxPBMoveDown(m_xBuilder->weld_button(u"move_down" _ustr))
, mxFTCategory(m_xBuilder->weld_label(u"categorylabel" _ustr))
, mxLBCategory(m_xBuilder->weld_combo_box(u"categorylb" _ustr))
, mxFTEffect(m_xBuilder->weld_label(u"effect_label" _ustr))
, mxLBAnimation(m_xBuilder->weld_tree_view(u"effect_list" _ustr))
, mxFTStart(m_xBuilder->weld_label(u"start_effect" _ustr))
, mxLBStart(m_xBuilder->weld_combo_box(u"start_effect_list" _ustr))
, mxFTProperty(m_xBuilder->weld_label(u"effect_property" _ustr))
, mxPlaceholderBox(m_xBuilder->weld_container(u"placeholder" _ustr))
, mxPBPropertyMore(m_xBuilder->weld_button(u"more_properties" _ustr))
, mxFTDuration(m_xBuilder->weld_label(u"effect_duration" _ustr))
, mxCBXDuration(m_xBuilder->weld_metric_spin_button(u"anim_duration" _ustr, FieldUnit::SECOND))
, mxFTStartDelay(m_xBuilder->weld_label(u"delay_label" _ustr))
, mxMFStartDelay(m_xBuilder->weld_metric_spin_button(u"delay_value" _ustr, FieldUnit::SECOND))
, mxCBAutoPreview(m_xBuilder->weld_check_button(u"auto_preview" _ustr))
, mxPBPlay(m_xBuilder->weld_button(u"play" _ustr))
, maIdle("sd idle treeview select" )
, mnLastSelectedAnimation(-1 )
, mnPropertyType(nPropertyTypeNone)
, mnCurvePathPos(-1 )
, mnPolygonPathPos(-1 )
, mnFreeformPathPos(-1 )
, maLateInitTimer("sd CustomAnimationPane maLateInitTimer" )
{
initialize();
}
css::ui::LayoutSize CustomAnimationPane::GetHeightForWidth(const sal_Int32 /*nWidth*/)
{
sal_Int32 nMinimumHeight = get_preferred_size().Height();
return css::ui::LayoutSize(nMinimumHeight, -1 , nMinimumHeight);
}
void CustomAnimationPane::initialize()
{
mxLBAnimation->connect_selection_changed(LINK(this , CustomAnimationPane, AnimationSelectHdl));
mxCustomAnimationList->setController(this );
mxCustomAnimationList->set_size_request(mxCustomAnimationList->get_approximate_digit_width() * 15 ,
mxCustomAnimationList->get_height_rows(4 ));
mxLBAnimation->set_size_request(mxLBAnimation->get_approximate_digit_width() * 15 ,
mxLBAnimation->get_height_rows(4 ));
maStrProperty = mxFTProperty->get_label();
mxPBAddEffect->connect_clicked( LINK( this , CustomAnimationPane, implClickHdl ) );
mxPBRemoveEffect->connect_clicked( LINK( this , CustomAnimationPane, implClickHdl ) );
mxLBStart->connect_changed( LINK( this , CustomAnimationPane, implControlListBoxHdl ) );
mxCBXDuration->connect_value_changed(LINK( this , CustomAnimationPane, DurationModifiedHdl));
mxPBPropertyMore->connect_clicked( LINK( this , CustomAnimationPane, implClickHdl ) );
mxPBMoveUp->connect_clicked( LINK( this , CustomAnimationPane, implClickHdl ) );
mxPBMoveDown->connect_clicked( LINK( this , CustomAnimationPane, implClickHdl ) );
mxPBPlay->connect_clicked( LINK( this , CustomAnimationPane, implClickHdl ) );
mxCBAutoPreview->connect_toggled( LINK( this , CustomAnimationPane, implToggleHdl ) );
mxLBCategory->connect_changed( LINK(this , CustomAnimationPane, UpdateAnimationLB) );
mxMFStartDelay->connect_value_changed( LINK(this , CustomAnimationPane, DelayModifiedHdl) );
mxMFStartDelay->connect_focus_out(LINK( this , CustomAnimationPane, DelayLoseFocusHdl));
maIdle.SetPriority(TaskPriority::DEFAULT );
maIdle.SetInvokeHandler(LINK(this , CustomAnimationPane, SelectionHandler));
maStrModify = mxFTEffect->get_label();
// get current controller and initialize listeners
try
{
mxView = mrBase.GetDrawController();
addListener();
}
catch ( Exception& )
{
TOOLS_WARN_EXCEPTION( "sd" , "sd::CustomAnimationPane::CustomAnimationPane()" );
}
// tdf#137637 keep user selection during initialization
ScopeLockGuard aGuard(maSelectionLock);
// get current page and update custom animation list
onChangeCurrentPage();
// Wait a short time before the presets list is created. This gives the
// system time to paint the control.
maLateInitTimer.SetTimeout(100 );
maLateInitTimer.SetInvokeHandler(LINK(this , CustomAnimationPane, lateInitCallback));
maLateInitTimer.Start();
}
CustomAnimationPane::~CustomAnimationPane()
{
maLateInitTimer.Stop();
removeListener();
MotionPathTagVector aTags;
aTags.swap( maMotionPathTags );
for (auto const & tag : aTags)
tag->Dispose();
mxPBAddEffect.reset();
mxPBRemoveEffect.reset();
mxFTEffect.reset();
mxFTStart.reset();
mxLBStart.reset();
mxLBSubControl.reset();
mxFTProperty.reset();
mxPlaceholderBox.reset();
mxPBPropertyMore.reset();
mxFTDuration.reset();
mxCBXDuration.reset();
mxFTStartDelay.reset();
mxMFStartDelay.reset();
mxCustomAnimationList.reset();
mxPBMoveUp.reset();
mxPBMoveDown.reset();
mxPBPlay.reset();
mxCBAutoPreview.reset();
mxFTCategory.reset();
mxLBCategory.reset();
mxFTAnimation.reset();
mxLBAnimation.reset();
}
void CustomAnimationPane::addUndo()
{
SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager();
if ( pManager )
{
SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
if ( pPage )
pManager->AddUndoAction( std::make_unique<UndoAnimation>( mrBase.GetDocShell()->GetDoc(), pPage ) );
}
}
void CustomAnimationPane::addListener()
{
Link<tools::EventMultiplexerEvent&,void > aLink( LINK(this ,CustomAnimationPane,EventMultiplexerListener) );
mrBase.GetEventMultiplexer()->AddEventListener(aLink);
}
void CustomAnimationPane::removeListener()
{
Link<tools::EventMultiplexerEvent&,void > aLink( LINK(this ,CustomAnimationPane,EventMultiplexerListener) );
mrBase.GetEventMultiplexer()->RemoveEventListener( aLink );
}
IMPL_LINK(CustomAnimationPane,EventMultiplexerListener,
tools::EventMultiplexerEvent&, rEvent, void )
{
switch (rEvent.meEventId)
{
case EventMultiplexerEventId::EditViewSelection:
onSelectionChanged();
break ;
case EventMultiplexerEventId::CurrentPageChanged:
onChangeCurrentPage();
break ;
case EventMultiplexerEventId::MainViewAdded:
// At this moment the controller may not yet been set at model
// or ViewShellBase. Take it from the view shell passed with
// the event.
if (auto pMainViewShell = mrBase.GetMainViewShell().get())
{
if ( pMainViewShell->GetShellType() == ViewShell::ST_IMPRESS )
{
mxView = mrBase.GetDrawController();
onSelectionChanged();
onChangeCurrentPage();
break ;
}
}
[[fallthrough]];
case EventMultiplexerEventId::MainViewRemoved:
mxView = nullptr;
mxCurrentPage = nullptr;
updateControls();
break ;
case EventMultiplexerEventId::Disposing:
mxView.clear();
onSelectionChanged();
onChangeCurrentPage();
break ;
case EventMultiplexerEventId::EndTextEdit:
if (mpMainSequence && rEvent.mpUserData)
mxCustomAnimationList->update( mpMainSequence );
break ;
default : break ;
}
}
static sal_Int32 getPropertyType( std::u16string_view rProperty )
{
if ( rProperty == u"Direction" )
return nPropertyTypeDirection;
if ( rProperty == u"Spokes" )
return nPropertyTypeSpokes;
if ( rProperty == u"Zoom" )
return nPropertyTypeZoom;
if ( rProperty == u"Accelerate" )
return nPropertyTypeAccelerate;
if ( rProperty == u"Decelerate" )
return nPropertyTypeDecelerate;
if ( rProperty == u"Color1" )
return nPropertyTypeFirstColor;
if ( rProperty == u"Color2" )
return nPropertyTypeSecondColor;
if ( rProperty == u"FillColor" )
return nPropertyTypeFillColor;
if ( rProperty == u"ColorStyle" )
return nPropertyTypeColorStyle;
if ( rProperty == u"AutoReverse" )
return nPropertyTypeAutoReverse;
if ( rProperty == u"FontStyle" )
return nPropertyTypeFont;
if ( rProperty == u"CharColor" )
return nPropertyTypeCharColor;
if ( rProperty == u"CharHeight" )
return nPropertyTypeCharHeight;
if ( rProperty == u"CharDecoration" )
return nPropertyTypeCharDecoration;
if ( rProperty == u"LineColor" )
return nPropertyTypeLineColor;
if ( rProperty == u"Rotate" )
return nPropertyTypeRotate;
if ( rProperty == u"Transparency" )
return nPropertyTypeTransparency;
if ( rProperty == u"Color" )
return nPropertyTypeColor;
if ( rProperty == u"Scale" )
return nPropertyTypeScale;
return nPropertyTypeNone;
}
OUString getPropertyName( sal_Int32 nPropertyType )
{
switch ( nPropertyType )
{
case nPropertyTypeDirection:
return SdResId(STR_CUSTOMANIMATION_DIRECTION_PROPERTY);
case nPropertyTypeSpokes:
return SdResId(STR_CUSTOMANIMATION_SPOKES_PROPERTY);
case nPropertyTypeFirstColor:
return SdResId(STR_CUSTOMANIMATION_FIRST_COLOR_PROPERTY);
case nPropertyTypeSecondColor:
return SdResId(STR_CUSTOMANIMATION_SECOND_COLOR_PROPERTY);
case nPropertyTypeZoom:
return SdResId(STR_CUSTOMANIMATION_ZOOM_PROPERTY);
case nPropertyTypeFillColor:
return SdResId(STR_CUSTOMANIMATION_FILL_COLOR_PROPERTY);
case nPropertyTypeColorStyle:
return SdResId(STR_CUSTOMANIMATION_STYLE_PROPERTY);
case nPropertyTypeFont:
return SdResId(STR_CUSTOMANIMATION_FONT_PROPERTY);
case nPropertyTypeCharHeight:
return SdResId(STR_CUSTOMANIMATION_SIZE_PROPERTY);
case nPropertyTypeCharColor:
return SdResId(STR_CUSTOMANIMATION_FONT_COLOR_PROPERTY);
case nPropertyTypeCharHeightStyle:
return SdResId(STR_CUSTOMANIMATION_FONT_SIZE_STYLE_PROPERTY);
case nPropertyTypeCharDecoration:
return SdResId(STR_CUSTOMANIMATION_FONT_STYLE_PROPERTY);
case nPropertyTypeLineColor:
return SdResId(STR_CUSTOMANIMATION_LINE_COLOR_PROPERTY);
case nPropertyTypeRotate:
return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY);
case nPropertyTypeColor:
return SdResId(STR_CUSTOMANIMATION_COLOR_PROPERTY);
case nPropertyTypeTransparency:
return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY);
case nPropertyTypeScale:
return SdResId(STR_CUSTOMANIMATION_SCALE_PROPERTY);
}
return OUString();
}
void CustomAnimationPane::updateControls()
{
mxFTDuration->set_sensitive(mxView.is());
mxCBXDuration->set_sensitive(mxView.is());
mxCustomAnimationList->set_sensitive(mxView.is());
if (comphelper::LibreOfficeKit::isActive())
{
mxPBPlay->hide();
mxCBAutoPreview->set_active(false );
mxCBAutoPreview->hide();
}
else
{
mxPBPlay->set_sensitive(mxView.is());
mxCBAutoPreview->set_sensitive(mxView.is());
}
if (!mxView.is())
{
mxPBAddEffect->set_sensitive(false );
mxPBRemoveEffect->set_sensitive(false );
mxFTStart->set_sensitive(false );
mxLBStart->set_sensitive(false );
mxPBPropertyMore->set_sensitive(false );
mxPlaceholderBox->set_sensitive(false );
mxFTProperty->set_sensitive(false );
mxFTCategory->set_sensitive(false );
mxLBCategory->set_sensitive(false );
mxFTAnimation->set_sensitive(false );
mxLBAnimation->set_sensitive(false );
mxFTStartDelay->set_sensitive(false );
mxMFStartDelay->set_sensitive(false );
mxLBAnimation->clear();
mnLastSelectedAnimation = -1 ;
mxCustomAnimationList->clear();
return ;
}
const int nSelectionCount = maListSelection.size();
mxPBAddEffect->set_sensitive( maViewSelection.hasValue() );
mxPBRemoveEffect->set_sensitive(nSelectionCount != 0 );
bool bIsSelected = (nSelectionCount > 0 );
if (bIsSelected)
{
mxFTAnimation->set_sensitive(true );
mxLBAnimation->set_sensitive(true );
}
else
{
mxFTAnimation->set_sensitive(false );
mxLBAnimation->set_sensitive(false );
mxLBAnimation->clear();
mnLastSelectedAnimation = -1 ;
}
mxLBCategory->set_sensitive(bIsSelected);
mxFTCategory->set_sensitive(bIsSelected);
mxFTStart->set_sensitive(nSelectionCount > 0 );
mxLBStart->set_sensitive(nSelectionCount > 0 );
mxPlaceholderBox->set_sensitive(nSelectionCount > 0 );
mxPBPropertyMore->set_sensitive(nSelectionCount > 0 );
mxFTStartDelay->set_sensitive(nSelectionCount > 0 );
mxMFStartDelay->set_sensitive(nSelectionCount > 0 );
mxFTProperty->set_label(maStrProperty);
sal_Int32 nOldPropertyType = mnPropertyType;
mnPropertyType = nPropertyTypeNone;
if (bIsSelected)
{
CustomAnimationEffectPtr pEffect = maListSelection.front();
OUString aUIName( CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId( pEffect->getPresetId() ) );
OUString aTemp( maStrModify );
if ( !aUIName.isEmpty() )
{
aTemp += " " + aUIName;
mxFTEffect->set_label( aTemp );
}
Any aValue;
CustomAnimationPresetPtr pDescriptor = CustomAnimationPresets::getCustomAnimationPresets().getEffectDescriptor( pEffect->getPresetId() );
if (pDescriptor)
{
std::vector<OUString> aProperties( pDescriptor->getProperties() );
if ( !aProperties.empty() )
{
mnPropertyType = getPropertyType( aProperties.front() );
mxFTProperty->set_label( getPropertyName( mnPropertyType ) );
aValue = getProperty1Value( mnPropertyType, pEffect );
}
}
sal_Int32 nNewPropertyType = mnPropertyType;
// if there is no value, then the control will be disabled, just show a disabled Direction box in that
// case to have something to fill the space
if (!aValue.hasValue())
nNewPropertyType = nPropertyTypeDirection;
if (!mxLBSubControl || nOldPropertyType != nNewPropertyType)
{
// for LOK destroy old widgets first
mxLBSubControl.reset(nullptr);
// then create new control, to keep correct pointers for actions
mxLBSubControl = SdPropertySubControl::create(nNewPropertyType, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), aValue, pEffect->getPresetId(), LINK(this , CustomAnimationPane, implPropertyHdl));
}
else
{
mxLBSubControl->setValue(aValue, pEffect->getPresetId());
}
bool bEnable = aValue.hasValue();
mxPlaceholderBox->set_sensitive( bEnable );
mxFTProperty->set_sensitive( bEnable );
if (!pDescriptor)
{
mxPBPropertyMore->set_sensitive( false );
mxFTStartDelay->set_sensitive( false );
mxMFStartDelay->set_sensitive( false );
}
sal_Int32 nCategoryPos = -1 ;
switch (pEffect->getPresetClass())
{
case EffectPresetClass::ENTRANCE: nCategoryPos = 0 ; break ;
case EffectPresetClass::EMPHASIS: nCategoryPos = 1 ; break ;
case EffectPresetClass::EXIT : nCategoryPos = 2 ; break ;
case EffectPresetClass::MOTIONPATH: nCategoryPos = 3 ; break ;
default :
break ;
}
switch (pEffect->getCommand())
{
case EffectCommands::TOGGLEPAUSE:
case EffectCommands::STOP:
case EffectCommands::PLAY:
nCategoryPos = 4 ; break ;
default :
break ;
}
mxLBCategory->set_active(nCategoryPos);
fillAnimationLB( pEffect->hasText() );
OUString rsPresetId = pEffect->getPresetId();
sal_Int32 nAnimationPos = mxLBAnimation->n_children();
while ( nAnimationPos-- )
{
auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nAnimationPos));
if (pEntryData)
{
CustomAnimationPresetPtr& pPtr = *pEntryData;
if ( pPtr && pPtr->getPresetId() == rsPresetId )
{
mxLBAnimation->select( nAnimationPos );
mnLastSelectedAnimation = nAnimationPos;
break ;
}
}
}
// If preset id is missing and category is motion path.
if (nAnimationPos < 0 && nCategoryPos == 3 )
{
if (rsPresetId == "libo-motionpath-curve" )
{
mxLBAnimation->select(mnCurvePathPos);
mnLastSelectedAnimation = mnCurvePathPos;
}
else if (rsPresetId == "libo-motionpath-polygon" )
{
mxLBAnimation->select(mnPolygonPathPos);
mnLastSelectedAnimation = mnPolygonPathPos;
}
else if (rsPresetId == "libo-motionpath-freeform-line" )
{
mxLBAnimation->select(mnFreeformPathPos);
mnLastSelectedAnimation = mnFreeformPathPos;
}
}
sal_uInt16 nPos = 0 xffff;
sal_Int16 nNodeType = pEffect->getNodeType();
switch ( nNodeType )
{
case EffectNodeType::ON_CLICK: nPos = 0 ; break ;
case EffectNodeType::WITH_PREVIOUS: nPos = 1 ; break ;
case EffectNodeType::AFTER_PREVIOUS: nPos = 2 ; break ;
}
mxLBStart->set_active( nPos );
double fDuration = pEffect->getDuration();
const bool bHasSpeed = fDuration > 0 .001 ;
mxFTDuration->set_sensitive(bHasSpeed);
mxCBXDuration->set_sensitive(bHasSpeed);
if ( bHasSpeed )
{
mxCBXDuration->set_value(fDuration*100 .0 , FieldUnit::NONE);
}
mxPBPropertyMore->set_sensitive(true );
mxFTStartDelay->set_sensitive(true );
mxMFStartDelay->set_sensitive(true );
double fBegin = pEffect->getBegin();
mxMFStartDelay->set_value(fBegin*10 .0 , FieldUnit::NONE);
}
else
{
// use an empty direction box to fill the space
if (!mxLBSubControl || (nOldPropertyType != nPropertyTypeDirection && nOldPropertyType != nPropertyTypeNone))
{
// for LOK destroy old widgets first
mxLBSubControl.reset(nullptr);
// then create new control, to keep correct pointers for actions
mxLBSubControl = SdPropertySubControl::create(nPropertyTypeDirection, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), uno::Any(), OUString(), LINK(this , CustomAnimationPane, implPropertyHdl));
}
else
mxLBSubControl->setValue(uno::Any(), OUString());
mxPlaceholderBox->set_sensitive(false );
mxFTProperty->set_sensitive(false );
mxFTStartDelay->set_sensitive(false );
mxMFStartDelay->set_sensitive(false );
mxPBPropertyMore->set_sensitive(false );
mxFTDuration->set_sensitive(false );
mxCBXDuration->set_sensitive(false );
mxCBXDuration->set_text(OUString());
mxFTEffect->set_label(maStrModify);
}
bool bEnableUp = true ;
bool bEnableDown = true ;
if ( nSelectionCount == 0 )
{
bEnableUp = false ;
bEnableDown = false ;
}
else
{
if ( mpMainSequence->find( maListSelection.front() ) == mpMainSequence->getBegin() )
bEnableUp = false ;
EffectSequence::iterator aIter( mpMainSequence->find( maListSelection.back() ) );
if ( aIter == mpMainSequence->getEnd() )
{
bEnableDown = false ;
}
else
{
do
{
++aIter;
}
while ( (aIter != mpMainSequence->getEnd()) && !(mxCustomAnimationList->isExpanded(*aIter) ) );
if ( aIter == mpMainSequence->getEnd() )
bEnableDown = false ;
}
if ( bEnableUp || bEnableDown )
{
MainSequenceRebuildGuard aGuard( mpMainSequence );
EffectSequenceHelper* pSequence = nullptr;
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
if ( pEffect )
{
if ( pSequence == nullptr )
{
pSequence = pEffect->getEffectSequence();
}
else
{
if ( pSequence != pEffect->getEffectSequence() )
{
bEnableUp = false ;
bEnableDown = false ;
break ;
}
}
}
}
}
}
mxPBMoveUp->set_sensitive(mxView.is() && bEnableUp);
mxPBMoveDown->set_sensitive(mxView.is() && bEnableDown);
SdOptions* pOptions = SdModule::get()->GetSdOptions(DocumentType::Impress);
mxCBAutoPreview->set_active(pOptions->IsPreviewChangedEffects());
updateMotionPathTags();
}
static bool updateMotionPathImpl( CustomAnimationPane& rPane, ::sd::View& rView, EffectSequence::iterator aIter, const EffectSequence::iterator& aEnd, MotionPathTagVector& rOldTags, MotionPathTagVector& rNewTags )
{
bool bChanges = false ;
while ( aIter != aEnd )
{
CustomAnimationEffectPtr pEffect( *aIter++ );
if ( pEffect && pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH )
{
rtl::Reference< MotionPathTag > xMotionPathTag;
// first try to find if there is already a tag for this
auto aMIter = std::find_if(rOldTags.begin(), rOldTags.end(),
[&pEffect](const rtl::Reference<MotionPathTag>& xTag) { return xTag->getEffect() == pEffect; });
if (aMIter != rOldTags.end())
{
rtl::Reference< MotionPathTag > xTag( *aMIter );
if ( !xTag->isDisposed() )
{
xMotionPathTag = std::move(xTag);
rOldTags.erase( aMIter );
}
}
// if not found, create new one
if ( !xMotionPathTag.is() )
{
xMotionPathTag.set( new MotionPathTag( rPane, rView, pEffect ) );
bChanges = true ;
}
if ( xMotionPathTag.is() )
rNewTags.push_back( xMotionPathTag );
}
}
return bChanges;
}
void CustomAnimationPane::updateMotionPathTags()
{
bool bChanges = false ;
MotionPathTagVector aTags;
aTags.swap( maMotionPathTags );
::sd::View* pView = nullptr;
if ( mxView.is() )
{
std::shared_ptr<ViewShell> xViewShell( mrBase.GetMainViewShell() );
if ( xViewShell )
pView = xViewShell->GetView();
}
if (mpMainSequence && pView)
{
bChanges = updateMotionPathImpl( *this , *pView, mpMainSequence->getBegin(), mpMainSequence->getEnd(), aTags, maMotionPathTags );
auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector();
for (InteractiveSequencePtr const & pIS : rInteractiveSequenceVector)
{
bChanges |= updateMotionPathImpl( *this , *pView, pIS->getBegin(), pIS->getEnd(), aTags, maMotionPathTags );
}
}
if ( !aTags.empty() )
{
bChanges = true ;
for ( rtl::Reference< MotionPathTag >& xTag : aTags )
{
xTag->Dispose();
}
}
if ( bChanges && pView )
pView->updateHandles();
}
void CustomAnimationPane::onSelectionChanged()
{
if ( maSelectionLock.isLocked() )
return ;
ScopeLockGuard aGuard( maSelectionLock );
if ( mxView.is() ) try
{
maViewSelection = mxView->getSelection();
mxCustomAnimationList->onSelectionChanged( maViewSelection );
updateControls();
}
catch ( Exception& )
{
TOOLS_WARN_EXCEPTION( "sd" , "sd::CustomAnimationPane::onSelectionChanged()" );
}
}
void CustomAnimationPane::onDoubleClick()
{
showOptions();
}
void CustomAnimationPane::onContextMenu(const OUString &rIdent)
{
if (rIdent == "onclick" )
onChangeStart( EffectNodeType::ON_CLICK );
else if (rIdent == "withprev" )
onChangeStart( EffectNodeType::WITH_PREVIOUS );
else if (rIdent == "afterprev" )
onChangeStart( EffectNodeType::AFTER_PREVIOUS );
else if (rIdent == "options" )
showOptions();
else if (rIdent == "timing" )
showOptions(u"timing" _ustr);
else if (rIdent == "remove" )
onRemove();
else if (rIdent == "create" && maViewSelection.hasValue())
onAdd();
updateControls();
}
static void addValue( const std::unique_ptr<STLPropertySet>& pSet, sal_Int32 nHandle, const Any& rValue )
{
switch ( pSet->getPropertyState( nHandle ) )
{
case STLPropertyState::Ambiguous:
// value is already ambiguous, do nothing
break ;
case STLPropertyState::Direct:
// set to ambiguous if existing value is different
if ( rValue != pSet->getPropertyValue( nHandle ) )
pSet->setPropertyState( nHandle, STLPropertyState::Ambiguous );
break ;
case STLPropertyState::Default :
// just set new value
pSet->setPropertyValue( nHandle, rValue );
break ;
}
}
static sal_Int32 calcMaxParaDepth( const Reference< XShape >& xTargetShape )
{
sal_Int32 nMaxParaDepth = -1 ;
if ( xTargetShape.is() )
{
Reference< XEnumerationAccess > xText( xTargetShape, UNO_QUERY );
if ( xText.is() )
{
Reference< XPropertySet > xParaSet;
Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW );
while ( xEnumeration->hasMoreElements() )
{
xEnumeration->nextElement() >>= xParaSet;
if ( xParaSet.is() )
{
sal_Int32 nParaDepth = 0 ;
xParaSet->getPropertyValue( u"NumberingLevel" _ustr ) >>= nParaDepth;
if ( nParaDepth > nMaxParaDepth )
nMaxParaDepth = nParaDepth;
}
}
}
}
return nMaxParaDepth + 1 ;
}
Any CustomAnimationPane::getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect )
{
switch ( nType )
{
case nPropertyTypeDirection:
case nPropertyTypeSpokes:
case nPropertyTypeZoom:
return Any( pEffect->getPresetSubType() );
case nPropertyTypeColor:
case nPropertyTypeFillColor:
case nPropertyTypeFirstColor:
case nPropertyTypeSecondColor:
case nPropertyTypeCharColor:
case nPropertyTypeLineColor:
{
const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1 ;
return pEffect->getColor( nIndex );
}
case nPropertyTypeFont:
return pEffect->getProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To );
case nPropertyTypeCharHeight:
{
static constexpr OUString aAttributeName( u"CharHeight" _ustr );
Any aValue( pEffect->getProperty( AnimationNodeType::SET, aAttributeName, EValue::To ) );
if ( !aValue.hasValue() )
aValue = pEffect->getProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To );
return aValue;
}
case nPropertyTypeRotate:
return pEffect->getTransformationProperty( AnimationTransformType::ROTATE, EValue::By);
case nPropertyTypeTransparency:
return pEffect->getProperty( AnimationNodeType::SET, u"Opacity" , EValue::To );
case nPropertyTypeScale:
return pEffect->getTransformationProperty( AnimationTransformType::SCALE, EValue::By );
case nPropertyTypeCharDecoration:
{
Sequence< Any > aValues{
pEffect->getProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To ),
pEffect->getProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To ),
pEffect->getProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To )
};
return Any( aValues );
}
}
Any aAny;
return aAny;
}
bool CustomAnimationPane::setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const Any& rValue )
{
bool bEffectChanged = false ;
switch ( nType )
{
case nPropertyTypeDirection:
case nPropertyTypeSpokes:
case nPropertyTypeZoom:
{
OUString aPresetSubType;
rValue >>= aPresetSubType;
if ( aPresetSubType != pEffect->getPresetSubType() )
{
CustomAnimationPresets::getCustomAnimationPresets().changePresetSubType( pEffect, aPresetSubType );
bEffectChanged = true ;
}
}
break ;
case nPropertyTypeFillColor:
case nPropertyTypeColor:
case nPropertyTypeFirstColor:
case nPropertyTypeSecondColor:
case nPropertyTypeCharColor:
case nPropertyTypeLineColor:
{
const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1 ;
Any aOldColor( pEffect->getColor( nIndex ) );
if ( aOldColor != rValue )
{
pEffect->setColor( nIndex, rValue );
bEffectChanged = true ;
}
}
break ;
case nPropertyTypeFont:
bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To, rValue );
break ;
case nPropertyTypeCharHeight:
{
static constexpr OUString aAttributeName( u"CharHeight" _ustr );
bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, aAttributeName, EValue::To, rValue );
if ( !bEffectChanged )
bEffectChanged = pEffect->setProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To, rValue );
}
break ;
case nPropertyTypeRotate:
bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::ROTATE, EValue::By , rValue );
break ;
case nPropertyTypeTransparency:
bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"Opacity" , EValue::To, rValue );
break ;
case nPropertyTypeScale:
bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::SCALE, EValue::By, rValue );
break ;
case nPropertyTypeCharDecoration:
{
Sequence< Any > aValues(3 );
rValue >>= aValues;
bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To, aValues[0 ] );
bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To, aValues[1 ] );
bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To, aValues[2 ] );
}
break ;
}
return bEffectChanged;
}
static bool hasVisibleShape( const Reference< XShape >& xShape )
{
try
{
const OUString sShapeType( xShape->getShapeType() );
if ( sShapeType == "com.sun.star.presentation.TitleTextShape" || sShapeType == "com.sun.star.presentation.OutlinerShape" ||
sShapeType == "com.sun.star.presentation.SubtitleShape" || sShapeType == "com.sun.star.drawing.TextShape" )
{
Reference< XPropertySet > xSet( xShape, UNO_QUERY_THROW );
FillStyle eFillStyle;
xSet->getPropertyValue( u"FillStyle" _ustr ) >>= eFillStyle;
css::drawing::LineStyle eLineStyle;
xSet->getPropertyValue( u"LineStyle" _ustr ) >>= eLineStyle;
return eFillStyle != FillStyle_NONE || eLineStyle != css::drawing::LineStyle_NONE;
}
}
catch ( Exception& )
{
}
return true ;
}
std::unique_ptr<STLPropertySet> CustomAnimationPane::createSelectionSet()
{
std::unique_ptr<STLPropertySet> pSet = CustomAnimationDialog::createDefaultSet();
pSet->setPropertyValue( nHandleCurrentPage, Any( mxCurrentPage ) );
sal_Int32 nMaxParaDepth = 0 ;
// get options from selected effects
const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets());
for ( CustomAnimationEffectPtr& pEffect : maListSelection )
{
EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
if ( !pEffectSequence )
pEffectSequence = mpMainSequence.get();
if ( pEffect->hasText() )
{
sal_Int32 n = calcMaxParaDepth(pEffect->getTargetShape());
if ( n > nMaxParaDepth )
nMaxParaDepth = n;
}
addValue( pSet, nHandleHasAfterEffect, Any( pEffect->hasAfterEffect() ) );
addValue( pSet, nHandleAfterEffectOnNextEffect, Any( pEffect->IsAfterEffectOnNext() ) );
addValue( pSet, nHandleDimColor, pEffect->getDimColor() );
addValue( pSet, nHandleIterateType, Any( pEffect->getIterateType() ) );
// convert absolute time to percentage value
// This calculation is done in float to avoid some rounding artifacts.
float fIterateInterval = static_cast <float >(pEffect->getIterateInterval());
if ( pEffect->getDuration() )
fIterateInterval = static_cast <float >(fIterateInterval / pEffect->getDuration() );
fIterateInterval *= 100 .0 ;
addValue( pSet, nHandleIterateInterval, Any( static_cast <double >(fIterateInterval) ) );
addValue( pSet, nHandleBegin, Any( pEffect->getBegin() ) );
addValue( pSet, nHandleDuration, Any( pEffect->getDuration() ) );
addValue( pSet, nHandleStart, Any( pEffect->getNodeType() ) );
addValue( pSet, nHandleRepeat, pEffect->getRepeatCount() );
addValue( pSet, nHandleEnd, pEffect->getEnd() );
addValue( pSet, nHandleRewind, Any( pEffect->getFill() ) );
addValue( pSet, nHandlePresetId, Any( pEffect->getPresetId() ) );
addValue( pSet, nHandleHasText, Any( pEffect->hasText() ) );
addValue( pSet, nHandleHasVisibleShape, Any( hasVisibleShape( pEffect->getTargetShape() ) ) );
Any aSoundSource;
if ( pEffect->getAudio().is() )
{
aSoundSource = pEffect->getAudio()->getSource();
addValue( pSet, nHandleSoundVolume, Any( pEffect->getAudio()->getVolume() ) );
// todo addValue( pSet, nHandleSoundEndAfterSlide, makeAny( pEffect->getAudio()->getEndAfterSlide() ) );
// this is now stored at the XCommand parameter sequence
}
else if ( pEffect->getCommand() == EffectCommands::STOPAUDIO )
{
aSoundSource <<= true ;
}
addValue( pSet, nHandleSoundURL, aSoundSource );
sal_Int32 nGroupId = pEffect->getGroupId();
CustomAnimationTextGroupPtr pTextGroup;
if ( nGroupId != -1 )
pTextGroup = pEffectSequence->findGroup( nGroupId );
addValue( pSet, nHandleTextGrouping, Any( pTextGroup ? pTextGroup->getTextGrouping() : sal_Int32(-1 ) ) );
addValue( pSet, nHandleAnimateForm, Any( !pTextGroup || pTextGroup->getAnimateForm() ) );
addValue( pSet, nHandleTextGroupingAuto, Any( pTextGroup ? pTextGroup->getTextGroupingAuto() : -1 .0 ) );
addValue( pSet, nHandleTextReverse, Any( pTextGroup && pTextGroup->getTextReverse() ) );
if ( pEffectSequence->getSequenceType() == EffectNodeType::INTERACTIVE_SEQUENCE )
{
InteractiveSequence* pIS = static_cast < InteractiveSequence* >( pEffectSequence );
addValue( pSet, nHandleTrigger, Any( pIS->getTriggerShape() ) );
}
CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( pEffect->getPresetId() );
if ( pDescriptor )
{
sal_Int32 nType = nPropertyTypeNone;
std::vector<OUString> aProperties( pDescriptor->getProperties() );
if ( !aProperties.empty() )
nType = getPropertyType( aProperties.front() );
if ( nType != nPropertyTypeNone )
{
addValue( pSet, nHandleProperty1Type, Any( nType ) );
addValue( pSet, nHandleProperty1Value, getProperty1Value( nType, pEffect ) );
}
if ( pDescriptor->hasProperty( u"Accelerate" ) )
{
addValue( pSet, nHandleAccelerate, Any( pEffect->getAcceleration() ) );
}
if ( pDescriptor->hasProperty( u"Decelerate" ) )
{
addValue( pSet, nHandleDecelerate, Any( pEffect->getDecelerate() ) );
}
if ( pDescriptor->hasProperty( u"AutoReverse" ) )
{
addValue( pSet, nHandleAutoReverse, Any( pEffect->getAutoReverse() ) );
}
}
}
addValue( pSet, nHandleMaxParaDepth, Any( nMaxParaDepth ) );
return pSet;
}
void CustomAnimationPane::changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet )
{
// change selected effect
bool bChanged = false ;
MainSequenceRebuildGuard aGuard( mpMainSequence );
for ( CustomAnimationEffectPtr& pEffect : maListSelection )
{
DBG_ASSERT( pEffect->getEffectSequence(), "sd::CustomAnimationPane::changeSelection(), dead effect in selection!" );
if ( !pEffect->getEffectSequence() )
continue ;
double fDuration = 0 .0 ; // we might need this for iterate-interval
if ( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct )
{
pResultSet->getPropertyValue( nHandleDuration ) >>= fDuration;
}
else
{
fDuration = pEffect->getDuration();
}
if ( pResultSet->getPropertyState( nHandleIterateType ) == STLPropertyState::Direct )
{
sal_Int16 nIterateType = 0 ;
pResultSet->getPropertyValue( nHandleIterateType ) >>= nIterateType;
if ( pEffect->getIterateType() != nIterateType )
{
pEffect->setIterateType( nIterateType );
bChanged = true ;
}
}
if ( pEffect->getIterateType() )
{
if ( pResultSet->getPropertyState( nHandleIterateInterval ) == STLPropertyState::Direct )
{
double fIterateInterval = 0 .0 ;
pResultSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval;
if ( pEffect->getIterateInterval() != fIterateInterval )
{
const double f = fIterateInterval * pEffect->getDuration() / 100 ;
pEffect->setIterateInterval( f );
bChanged = true ;
}
}
}
double fBegin = 0 .0 ;
if ( pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct )
pResultSet->getPropertyValue( nHandleBegin ) >>= fBegin;
else
fBegin = pEffect->getBegin();
if ( pEffect->getBegin() != fBegin && pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct)
{
pEffect->setBegin( fBegin );
bChanged = true ;
}
if ( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct )
{
if ( pEffect->getDuration() != fDuration )
{
pEffect->setDuration( fDuration );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleStart ) == STLPropertyState::Direct )
{
sal_Int16 nNodeType = 0 ;
pResultSet->getPropertyValue( nHandleStart ) >>= nNodeType;
if ( pEffect->getNodeType() != nNodeType )
{
pEffect->setNodeType( nNodeType );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleRepeat ) == STLPropertyState::Direct )
{
Any aRepeatCount( pResultSet->getPropertyValue( nHandleRepeat ) );
if ( aRepeatCount != pEffect->getRepeatCount() )
{
pEffect->setRepeatCount( aRepeatCount );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleEnd ) == STLPropertyState::Direct )
{
Any aEndValue( pResultSet->getPropertyValue( nHandleEnd ) );
if ( pEffect->getEnd() != aEndValue )
{
pEffect->setEnd( aEndValue );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleRewind ) == STLPropertyState::Direct )
{
sal_Int16 nFill = 0 ;
pResultSet->getPropertyValue( nHandleRewind ) >>= nFill;
if ( pEffect->getFill() != nFill )
{
pEffect->setFill( nFill );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleHasAfterEffect ) == STLPropertyState::Direct )
{
bool bHasAfterEffect = false ;
if ( pResultSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect )
{
if ( pEffect->hasAfterEffect() != bHasAfterEffect )
{
pEffect->setHasAfterEffect( bHasAfterEffect );
bChanged = true ;
}
}
}
if ( pResultSet->getPropertyState( nHandleAfterEffectOnNextEffect ) == STLPropertyState::Direct )
{
bool bAfterEffectOnNextEffect = false ;
if ( (pResultSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextEffect)
&& (pEffect->IsAfterEffectOnNext() != bAfterEffectOnNextEffect) )
{
pEffect->setAfterEffectOnNext( bAfterEffectOnNextEffect );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Direct )
{
Any aDimColor( pResultSet->getPropertyValue( nHandleDimColor ) );
if ( pEffect->getDimColor() != aDimColor )
{
pEffect->setDimColor( aDimColor );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct )
{
double fAccelerate = 0 .0 ;
pResultSet->getPropertyValue( nHandleAccelerate ) >>= fAccelerate;
if ( pEffect->getAcceleration() != fAccelerate )
{
pEffect->setAcceleration( fAccelerate );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleDecelerate ) == STLPropertyState::Direct )
{
double fDecelerate = 0 .0 ;
pResultSet->getPropertyValue( nHandleDecelerate ) >>= fDecelerate;
if ( pEffect->getDecelerate() != fDecelerate )
{
pEffect->setDecelerate( fDecelerate );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleAutoReverse ) == STLPropertyState::Direct )
{
bool bAutoReverse = false ;
pResultSet->getPropertyValue( nHandleAutoReverse ) >>= bAutoReverse;
if ( pEffect->getAutoReverse() != bAutoReverse )
{
pEffect->setAutoReverse( bAutoReverse );
bChanged = true ;
}
}
if ( pResultSet->getPropertyState( nHandleProperty1Value ) == STLPropertyState::Direct )
{
sal_Int32 nType = 0 ;
pOldSet->getPropertyValue( nHandleProperty1Type ) >>= nType;
bChanged |= setProperty1Value( nType, pEffect, pResultSet->getPropertyValue( nHandleProperty1Value ) );
}
if ( pResultSet->getPropertyState( nHandleSoundURL ) == STLPropertyState::Direct )
{
const Any aSoundSource( pResultSet->getPropertyValue( nHandleSoundURL ) );
if ( aSoundSource.getValueType() == ::cppu::UnoType<sal_Bool>::get() )
{
pEffect->setStopAudio();
bChanged = true ;
}
else
{
OUString aSoundURL;
aSoundSource >>= aSoundURL;
if ( !aSoundURL.isEmpty() )
{
if ( !pEffect->getAudio().is() )
{
pEffect->createAudio( aSoundSource );
bChanged = true ;
}
else
{
if ( pEffect->getAudio()->getSource() != aSoundSource )
{
pEffect->getAudio()->setSource( aSoundSource );
bChanged = true ;
}
}
}
else
{
if ( pEffect->getAudio().is() || pEffect->getStopAudio() )
{
pEffect->removeAudio();
bChanged = true ;
}
}
}
}
if ( pResultSet->getPropertyState( nHandleTrigger ) == STLPropertyState::Direct )
{
Reference< XShape > xTriggerShape;
pResultSet->getPropertyValue( nHandleTrigger ) >>= xTriggerShape;
bChanged |= mpMainSequence->setTrigger( pEffect, xTriggerShape );
}
}
const bool bHasTextGrouping = pResultSet->getPropertyState( nHandleTextGrouping ) == STLPropertyState::Direct;
const bool bHasAnimateForm = pResultSet->getPropertyState( nHandleAnimateForm ) == STLPropertyState::Direct;
const bool bHasTextGroupingAuto = pResultSet->getPropertyState( nHandleTextGroupingAuto ) == STLPropertyState::Direct;
const bool bHasTextReverse = pResultSet->getPropertyState( nHandleTextReverse ) == STLPropertyState::Direct;
if ( bHasTextGrouping || bHasAnimateForm || bHasTextGroupingAuto || bHasTextReverse )
{
// we need to do a second pass for text grouping options
// since changing them can cause effects to be removed
// or replaced, we do this after we applied all other options
// above
sal_Int32 nTextGrouping = 0 ;
bool bAnimateForm = true , bTextReverse = false ;
double fTextGroupingAuto = -1 .0 ;
if ( bHasTextGrouping )
pResultSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping;
else
pOldSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping;
if ( bHasAnimateForm )
pResultSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm;
else
pOldSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm;
if ( bHasTextGroupingAuto )
pResultSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto;
else
pOldSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto;
if ( bHasTextReverse )
pResultSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse;
else
pOldSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse;
EffectSequence const aSelectedEffects( maListSelection );
for ( CustomAnimationEffectPtr const & pEffect : aSelectedEffects )
{
EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
if ( !pEffectSequence )
pEffectSequence = mpMainSequence.get();
sal_Int32 nGroupId = pEffect->getGroupId();
CustomAnimationTextGroupPtr pTextGroup;
if ( nGroupId != -1 )
{
// use existing group
pTextGroup = pEffectSequence->findGroup( nGroupId );
}
else
{
// somethings changed so we need a group now
pTextGroup = pEffectSequence->createTextGroup( pEffect, nTextGrouping, fTextGroupingAuto, bAnimateForm, bTextReverse );
bChanged = true ;
}
//#i119988#
/************************************************************************/
/*
Note , the setAnimateForm means set the animation from TextGroup to Object ' s Shape
And on the UI in means " Animate attached shape " in " Effect Option " dialog
The setTextGrouping means set animation to Object ' s Text ,
the nTextGrouping is Text Animation Type
nTextGrouping = - 1 is " As one Object " , means no text animation .
The previous call order first do the setTextGrouping and then do the setAnimateForm ,
that will cause such defect : in the setTextGrouping , the effect has been removed ,
but in setAnimateForm still need this effect , then a NULL pointer of that effect will
be gotten , and cause crash .
[ ] bHasAnimateForm means the UI has changed , bAnimateForm is it value
So if create a new textgroup animation , the following animation will never be run !
Since the \ A1 \ B0Animate attached shape \ A1 \ B1 is default checked .
And the bHasAnimateForm default is false , and if user uncheck it the value bAnimateForm will be false ,
it same as the TextGroup \ A1 \ AFs default value , also could not be run setAnimateForm .
if ( bHasAnimateForm )
{
if ( pTextGroup - > getAnimateForm ( ) ! = bAnimateForm )
{
pEffectSequence - > setAnimateForm ( pTextGroup , bAnimateForm ) ;
bChanged = true ;
}
}
In setTextGrouping , there are three case :
1 . Create new text effects for empty TextGroup
2 . Remove all text effects of TextGroup ( nTextGrouping = = - 1 )
3 . Change all the text effects \ A1 \ AF start type
So here is the right logic :
If set the animation from text to shape and remove text animation ,
should do setAnimateForm first , then do setTextGrouping .
Other case , do setTextGrouping first , then do setAnimateForm .
*/
/************************************************************************/
bool bDoSetAnimateFormFirst = false ;
bool bNeedDoSetAnimateForm = false ;
if ( bHasAnimateForm )
{
if ( pTextGroup && pTextGroup->getAnimateForm() != bAnimateForm )
{
if ( (pTextGroup->getTextGrouping() >= 0 ) && (nTextGrouping == -1 ) )
{
bDoSetAnimateFormFirst = true ;
}
bNeedDoSetAnimateForm = true ;
}
}
if (bDoSetAnimateFormFirst)
{
pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
bChanged = true ;
}
if ( bHasTextGrouping )
{
if ( pTextGroup && pTextGroup->getTextGrouping() != nTextGrouping )
{
pEffectSequence->setTextGrouping( pTextGroup, nTextGrouping );
// All the effects of the outline object is removed so we need to
// put it back. OTOH, the shape object that still has effects
// in the text group is fine.
if (nTextGrouping == -1 && pTextGroup->getEffects().empty())
{
pEffect->setTarget(Any(pEffect->getTargetShape()));
pEffect->setGroupId(-1 );
mpMainSequence->append(pEffect);
}
bChanged = true ;
}
}
if (!bDoSetAnimateFormFirst && bNeedDoSetAnimateForm)
{
if ( pTextGroup )
{
pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
bChanged = true ;
}
}
if ( bHasTextGroupingAuto )
{
if ( pTextGroup && pTextGroup->getTextGroupingAuto() != fTextGroupingAuto )
{
pEffectSequence->setTextGroupingAuto( pTextGroup, fTextGroupingAuto );
bChanged = true ;
}
}
if ( bHasTextReverse )
{
if ( pTextGroup && pTextGroup->getTextReverse() != bTextReverse )
{
pEffectSequence->setTextReverse( pTextGroup, bTextReverse );
bChanged = true ;
}
}
}
}
if ( bChanged )
{
mpMainSequence->rebuild();
updateControls();
mrBase.GetDocShell()->SetModified();
}
}
void CustomAnimationPane::showOptions(const OUString& rPage)
{
std::unique_ptr<STLPropertySet> xSet = createSelectionSet();
auto xDlg = std::make_shared<CustomAnimationDialog>(GetFrameWeld(), std::move(xSet), rPage);
weld::DialogController::runAsync(xDlg, [xDlg, this ](sal_Int32 nResult){
if (nResult )
{
addUndo();
changeSelection(xDlg->getResultSet(), xDlg->getPropertySet());
updateControls();
}
});
}
void CustomAnimationPane::onChangeCurrentPage()
{
if ( !mxView.is() )
return ;
try
{
Reference< XDrawPage > xNewPage( mxView->getCurrentPage() );
if ( xNewPage != mxCurrentPage )
{
mxCurrentPage = std::move(xNewPage);
SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
if ( pPage )
{
mpMainSequence = pPage->getMainSequence();
mxCustomAnimationList->update( mpMainSequence );
}
updateControls();
}
}
catch ( Exception& )
{
TOOLS_WARN_EXCEPTION( "sd" , "sd::CustomAnimationPane::onChangeCurrentPage()" );
}
}
static bool getTextSelection(const Reference< XTextRange >& xSelectedText, Reference< XShape >& xShape, std::vector< sal_Int16 >& rParaList )
{
if ( xSelectedText.is() ) try
{
xShape.set( xSelectedText->getText(), UNO_QUERY_THROW );
css::uno::Reference<css::document::XActionLockable> xLockable(xShape, css::uno::UNO_QUERY);
if (xLockable.is())
xLockable->addActionLock();
comphelper::ScopeGuard aGuard([&xLockable]()
{
if (xLockable.is())
xLockable->removeActionLock();
});
Reference< XTextRangeCompare > xTextRangeCompare( xShape, UNO_QUERY_THROW );
Reference< XEnumerationAccess > xParaEnumAccess( xShape, UNO_QUERY_THROW );
Reference< XEnumeration > xParaEnum( xParaEnumAccess->createEnumeration(), UNO_SET_THROW );
Reference< XTextRange > xRange;
Reference< XTextRange > xStart( xSelectedText->getStart() );
Reference< XTextRange > xEnd( xSelectedText->getEnd() );
if (xTextRangeCompare->compareRegionEnds(xStart, xEnd) < 0 )
std::swap(xStart, xEnd);
sal_Int16 nPara = 0 ;
while ( xParaEnum->hasMoreElements() )
{
xParaEnum->nextElement() >>= xRange;
// break if start of selection is prior to end of current paragraph
if ( xRange.is() && (xTextRangeCompare->compareRegionEnds( xStart, xRange ) >= 0 ) )
break ;
nPara++;
}
while ( xRange.is() )
{
if ( xRange.is() && !xRange->getString().isEmpty() )
rParaList.push_back( nPara );
// break if end of selection is before or at end of current paragraph
if ( xRange.is() && xTextRangeCompare->compareRegionEnds( xEnd, xRange ) >= 0 )
break ;
nPara++;
if ( xParaEnum->hasMoreElements() )
xParaEnum->nextElement() >>= xRange;
else
xRange.clear();
}
return true ;
}
catch ( Exception& )
{
TOOLS_WARN_EXCEPTION( "sd" , "sd::CustomAnimationPane::getTextSelection()" );
}
return false ;
}
namespace
{
Reference<XShape> getTargetShape(const Any& rTarget)
{
Reference<XShape> xShape;
rTarget >>= xShape;
if ( !xShape.is() )
{
ParagraphTarget aParaTarget;
if (rTarget >>= aParaTarget)
xShape = aParaTarget.Shape;
}
return xShape;
}
}
void CustomAnimationPane::onAdd()
{
bool bHasText = true ;
// first create vector of targets for dialog preview
std::vector< Any > aTargets;
// gather shapes from the selection
maViewSelection = mxView->getSelection();
if (Reference<XIndexAccess> xShapes; maViewSelection >>= xShapes)
{
sal_Int32 nCount = xShapes->getCount();
aTargets.reserve( nCount );
for ( sal_Int32 nIndex = 0 ; nIndex < nCount; nIndex++ )
{
Any aTarget( xShapes->getByIndex( nIndex ) );
aTargets.push_back( aTarget );
if ( bHasText )
{
Reference< XText > xText;
aTarget >>= xText;
if ( !xText.is() || xText->getString().isEmpty() )
bHasText = false ;
}
}
}
else if (Reference<XText> xText; maViewSelection >>= xText)
{
aTargets.push_back( maViewSelection );
if ( !xText.is() || xText->getString().isEmpty() )
bHasText = false ;
}
else if (Reference<XTextCursor> xCursor; maViewSelection >>= xCursor)
{
Reference< XShape > xShape;
std::vector< sal_Int16 > aParaList;
if (getTextSelection(xCursor, xShape, aParaList))
{
ParagraphTarget aParaTarget;
aParaTarget.Shape = std::move(xShape);
for ( const auto & rPara : aParaList )
{
aParaTarget.Paragraph = rPara;
aTargets.push_back( Any( aParaTarget ) );
}
}
}
else
{
OSL_FAIL("sd::CustomAnimationPane::onAdd(), unknown view selection!" );
return ;
}
CustomAnimationPresetPtr pDescriptor;
mxFTCategory->set_sensitive(true );
mxFTAnimation->set_sensitive(true );
bool bCategoryReset = false ;
if (!mxLBCategory->get_sensitive() || mxLBCategory->get_active() == -1 )
{
mxLBCategory->set_sensitive(true );
mxLBCategory->set_active(0 );
bCategoryReset = true ;
}
if (bCategoryReset || !mxLBAnimation->get_sensitive() ||
mxLBAnimation->get_selected_index() == -1 )
{
mxLBAnimation->set_sensitive(true );
sal_Int32 nFirstEffect = fillAnimationLB(bHasText);
if (nFirstEffect == -1 )
return ;
mxLBAnimation->select(nFirstEffect);
mnLastSelectedAnimation = nFirstEffect;
}
auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_selected_id());
if (pEntryData)
pDescriptor = *pEntryData;
if ( pDescriptor )
{
const double fDuration = pDescriptor->getDuration();
mxCBXDuration->set_value(fDuration*100 .0 , FieldUnit::NONE);
bool bHasSpeed = pDescriptor->getDuration() > 0 .001 ;
mxCBXDuration->set_sensitive( bHasSpeed );
mxFTDuration->set_sensitive( bHasSpeed );
mxCustomAnimationList->unselect_all();
// gather shapes from the selection
bool bFirst = true ;
for ( const auto & rTarget : aTargets )
{
css::uno::Reference<css::document::XActionLockable> xLockable(getTargetShape(rTarget), css::uno::UNO_QUERY);
if (xLockable.is())
xLockable->addActionLock();
comphelper::ScopeGuard aGuard([&xLockable]()
{
if (xLockable.is())
xLockable->removeActionLock();
});
CustomAnimationEffectPtr pCreated = mpMainSequence->append( pDescriptor, rTarget, fDuration );
// if only one shape with text and no fill or outline is selected, animate only by first level paragraphs
if ( bHasText && (aTargets.size() == 1 ) )
{
Reference< XShape > xShape( rTarget, UNO_QUERY );
if ( xShape.is() && !hasVisibleShape( xShape ) )
{
mpMainSequence->createTextGroup( pCreated, 1 , -1 .0 , false , false );
}
}
if ( bFirst )
bFirst = false ;
else
pCreated->setNodeType( EffectNodeType::WITH_PREVIOUS );
if ( pCreated )
mxCustomAnimationList->select( pCreated );
}
}
PathKind ePathKind = getCreatePathKind();
if (ePathKind != PathKind::NONE)
{
createPath( ePathKind, aTargets, 0 .0 );
updateMotionPathTags();
}
addUndo();
mrBase.GetDocShell()->SetModified();
updateControls();
if (!SlideShow::IsInteractiveSlideshow(&mrBase)) // IASS
SlideShow::Stop( mrBase );
}
void CustomAnimationPane::onRemove()
{
if ( maListSelection.empty() )
return ;
addUndo();
MainSequenceRebuildGuard aGuard( mpMainSequence );
EffectSequence aList( maListSelection );
for ( CustomAnimationEffectPtr& pEffect : aList )
{
if ( pEffect->getEffectSequence() )
pEffect->getEffectSequence()->remove( pEffect );
}
maListSelection.clear();
mrBase.GetDocShell()->SetModified();
}
void CustomAnimationPane::remove( CustomAnimationEffectPtr const & pEffect )
{
if ( pEffect->getEffectSequence() )
{
addUndo();
pEffect->getEffectSequence()->remove( pEffect );
mrBase.GetDocShell()->SetModified();
}
}
void CustomAnimationPane::onChangeStart()
{
sal_Int16 nNodeType;
switch ( mxLBStart->get_active() )
{
case 0 : nNodeType = EffectNodeType::ON_CLICK; break ;
case 1 : nNodeType = EffectNodeType::WITH_PREVIOUS; break ;
case 2 : nNodeType = EffectNodeType::AFTER_PREVIOUS; break ;
default :
return ;
}
onChangeStart( nNodeType );
}
void CustomAnimationPane::onChangeStart( sal_Int16 nNodeType )
{
addUndo();
MainSequenceRebuildGuard aGuard( mpMainSequence );
bool bNeedRebuild = false ;
for ( CustomAnimationEffectPtr& pEffect : maListSelection )
{
if ( pEffect->getNodeType() != nNodeType )
{
pEffect->setNodeType( nNodeType );
bNeedRebuild = true ;
}
}
if ( bNeedRebuild )
{
mpMainSequence->rebuild();
updateControls();
mrBase.GetDocShell()->SetModified();
}
}
void CustomAnimationPane::onChangeSpeed()
{
double fDuration = getDuration();
if (fDuration < 0 )
return ;
else
{
addUndo();
MainSequenceRebuildGuard aGuard( mpMainSequence );
// change selected effect
for ( CustomAnimationEffectPtr& pEffect : maListSelection )
{
pEffect->setDuration( fDuration );
}
mpMainSequence->rebuild();
updateControls();
mrBase.GetDocShell()->SetModified();
}
}
double CustomAnimationPane::getDuration() const
{
double fDuration = 0 ;
if (!mxCBXDuration->get_text().isEmpty())
fDuration = mxCBXDuration->get_value(FieldUnit::NONE) / 100 .0 ;
return fDuration;
}
PathKind CustomAnimationPane::getCreatePathKind() const
{
PathKind eKind = PathKind::NONE;
if (mxLBAnimation->count_selected_rows() == 1 &&
mxLBCategory->get_active() == gnMotionPathPos)
{
const sal_Int32 nPos = mxLBAnimation->get_selected_index();
if ( nPos == mnCurvePathPos )
{
eKind = PathKind::CURVE;
}
else if ( nPos == mnPolygonPathPos )
{
eKind = PathKind::POLYGON;
}
else if ( nPos == mnFreeformPathPos )
{
eKind = PathKind::FREEFORM;
}
}
return eKind;
}
void CustomAnimationPane::createPath( PathKind eKind, std::vector< Any >& rTargets, double fDuration)
{
sal_uInt16 nSID = 0 ;
switch ( eKind )
{
case PathKind::CURVE: nSID = SID_DRAW_BEZIER_NOFILL; break ;
case PathKind::POLYGON: nSID = SID_DRAW_POLYGON_NOFILL; break ;
case PathKind::FREEFORM: nSID = SID_DRAW_FREELINE_NOFILL; break ;
default : break ;
}
if ( !nSID )
return ;
DrawViewShell* pViewShell = dynamic_cast < DrawViewShell* >(
FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get());
if ( pViewShell )
{
DrawView* pView = pViewShell->GetDrawView();
if ( pView )
pView->UnmarkAllObj();
std::vector< Any > aTargets( 1 , Any( fDuration ) );
aTargets.insert( aTargets.end(), rTargets.begin(), rTargets.end() );
Sequence< Any > aTargetSequence( comphelper::containerToSequence( aTargets ) );
const SfxUnoAnyItem aItem( SID_ADD_MOTION_PATH, Any( aTargetSequence ) );
pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( nSID, SfxCallMode::ASYNCHRON, {&aItem} );
}
}
/// this link is called when the property box is modified by the user
IMPL_LINK_NOARG(CustomAnimationPane, implPropertyHdl, LinkParamNone*, void )
{
if (!mxLBSubControl)
return ;
addUndo();
MainSequenceRebuildGuard aGuard( mpMainSequence );
const Any aValue(mxLBSubControl->getValue());
bool bNeedUpdate = false ;
// change selected effect
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
if ( setProperty1Value( mnPropertyType, pEffect, aValue ) )
bNeedUpdate = true ;
}
if ( bNeedUpdate )
{
mpMainSequence->rebuild();
updateControls();
mrBase.GetDocShell()->SetModified();
}
onPreview( false );
}
IMPL_LINK_NOARG(CustomAnimationPane, DelayModifiedHdl, weld::MetricSpinButton&, void )
{
addUndo();
}
IMPL_LINK_NOARG(CustomAnimationPane, DelayLoseFocusHdl, weld::Widget&, void )
{
double fBegin = mxMFStartDelay->get_value(FieldUnit::NONE);
//sequence rebuild only when the control loses focus
MainSequenceRebuildGuard aGuard( mpMainSequence );
// change selected effect
for ( CustomAnimationEffectPtr& pEffect : maListSelection )
{
pEffect->setBegin( fBegin/10 .0 );
}
mpMainSequence->rebuild();
updateControls();
mrBase.GetDocShell()->SetModified();
}
IMPL_LINK_NOARG(CustomAnimationPane, AnimationSelectHdl, weld::TreeView&, void )
{
maIdle.Start();
}
IMPL_LINK_NOARG(CustomAnimationPane, SelectionHandler, Timer*, void )
{
if (mxLBAnimation->has_mouse_grab()) // tdf#136474 try again later
{
maIdle.Start();
return ;
}
int nSelected = mxLBAnimation->get_selected_index();
if (nSelected == -1 )
return ;
// tdf#99137, the selected entry may also be a subcategory title, so not an effect
// just skip it and move to the next one in this case
if (mxLBAnimation->get_text_emphasis(nSelected, 0 ))
{
if (nSelected == 0 || nSelected > mnLastSelectedAnimation)
mxLBAnimation->select(++nSelected);
else
mxLBAnimation->select(--nSelected);
}
mnLastSelectedAnimation = nSelected;
CustomAnimationPresetPtr* pPreset = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nSelected));
PathKind ePathKind = getCreatePathKind();
if ( ePathKind != PathKind::NONE )
{
std::vector< Any > aTargets;
MainSequenceRebuildGuard aGuard( mpMainSequence );
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
aTargets.push_back( pEffect->getTarget() );
EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
if ( !pEffectSequence )
pEffectSequence = mpMainSequence.get();
// delete the old animation, new one will be appended
// by createPath and SID_ADD_MOTION_PATH therein
pEffectSequence->remove( pEffect );
}
createPath( ePathKind, aTargets, 0 .0 );
updateMotionPathTags();
return ;
}
CustomAnimationPresetPtr pDescriptor(*pPreset);
const double fDuration = (*pPreset)->getDuration();
MainSequenceRebuildGuard aGuard( mpMainSequence );
// get selected effect
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
// Dispose the deprecated motion path tag. It will be rebuilt later.
if (pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH)
{
for (auto const & xTag: maMotionPathTags)
{
if (xTag->getEffect() == pEffect && !xTag->isDisposed())
xTag->Dispose();
}
}
EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
if ( !pEffectSequence )
pEffectSequence = mpMainSequence.get();
pEffectSequence->replace( pEffect, pDescriptor, fDuration );
}
addUndo();
onPreview(false );
}
IMPL_LINK_NOARG(CustomAnimationPane, UpdateAnimationLB, weld::ComboBox&, void )
{
//FIXME: first effect only? what if there is more?
bool bHasText = false ;
if (!maListSelection.empty())
{
CustomAnimationEffectPtr pEffect = maListSelection.front();
bHasText = pEffect && pEffect->hasText();
}
fillAnimationLB(bHasText);
}
IMPL_LINK_NOARG(CustomAnimationPane, DurationModifiedHdl, weld::MetricSpinButton&, void )
{
if (!mxCBXDuration->get_text().isEmpty())
{
double duration_value = static_cast <double >(mxCBXDuration->get_value(FieldUnit::NONE));
if (duration_value <= 0 .0 )
{
mxCBXDuration->set_value(1 , FieldUnit::NONE);
}
onChangeSpeed();
}
}
namespace
{
void InsertCategory(weld::TreeView& rLBAnimation, const OUString& rMotionPathLabel)
{
int nRow = rLBAnimation.n_children();
rLBAnimation.append_text(rMotionPathLabel);
rLBAnimation.set_text_emphasis(nRow, true , 0 );
rLBAnimation.set_text_align(nRow, 0 .5 , 0 );
}
}
sal_Int32 CustomAnimationPane::fillAnimationLB( bool bHasText )
{
PresetCategoryList rCategoryList;
sal_uInt16 nPosition = mxLBCategory->get_active();
const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets());
switch (nPosition)
{
case 0 :rCategoryList = rPresets.getEntrancePresets();break ;
case 1 :rCategoryList = rPresets.getEmphasisPresets();break ;
case 2 :rCategoryList = rPresets.getExitPresets();break ;
case 3 :rCategoryList = rPresets.getMotionPathsPresets();break ;
case 4 :rCategoryList = rPresets.getMiscPresets();break ;
}
sal_Int32 nFirstEffect = -1 ;
int nOldEntryCount = mxLBAnimation->n_children();
int nOldScrollPos = mxLBAnimation->vadjustment_get_value();
mxLBAnimation->freeze();
mxLBAnimation->clear();
mnLastSelectedAnimation = -1 ;
if (nPosition == gnMotionPathPos)
{
OUString sMotionPathLabel( SdResId( STR_CUSTOMANIMATION_USERPATH ) );
InsertCategory(*mxLBAnimation, sMotionPathLabel);
mnCurvePathPos = mxLBAnimation->n_children();
mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulCOMBLINE) );
mxLBAnimation->set_text_emphasis(mnCurvePathPos, false , 0 );
mnPolygonPathPos = mnCurvePathPos + 1 ;
mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulPOLY) );
mxLBAnimation->set_text_emphasis(mnPolygonPathPos, false , 0 );
mnFreeformPathPos = mnPolygonPathPos + 1 ;
mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulFREELINE) );
mxLBAnimation->set_text_emphasis(mnFreeformPathPos, false , 0 );
}
for (const PresetCategoryPtr& pCategory : rCategoryList)
{
if ( pCategory )
{
InsertCategory(*mxLBAnimation, pCategory->maLabel);
int nPos = mxLBAnimation->n_children();
std::vector< CustomAnimationPresetPtr > aSortedVector =
pCategory->maEffects;
for ( const CustomAnimationPresetPtr& pDescriptor : aSortedVector )
{
// ( !isTextOnly || ( isTextOnly && bHasText ) ) <=> !isTextOnly || bHasText
if ( pDescriptor && ( !pDescriptor->isTextOnly() || bHasText ) )
{
auto pCustomPtr = new CustomAnimationPresetPtr(pDescriptor);
OUString sId = weld::toId(pCustomPtr);
mxLBAnimation->append(sId, pDescriptor->getLabel());
mxLBAnimation->set_text_emphasis(nPos, false , 0 );
if (nFirstEffect == -1 )
nFirstEffect = nPos;
++nPos;
}
}
}
}
mxLBAnimation->thaw();
if (mxLBAnimation->n_children() == nOldEntryCount)
mxLBAnimation->vadjustment_set_value(nOldScrollPos);
return nFirstEffect;
}
IMPL_LINK(CustomAnimationPane, implToggleHdl, weld::Toggleable&, rBtn, void )
{
implControlHdl(&rBtn);
}
IMPL_LINK(CustomAnimationPane, implClickHdl, weld::Button&, rBtn, void )
{
implControlHdl(&rBtn);
}
IMPL_LINK( CustomAnimationPane, implControlListBoxHdl, weld::ComboBox&, rListBox, void )
{
implControlHdl(&rListBox);
}
/// this link is called when one of the controls is modified
void CustomAnimationPane::implControlHdl(const weld::Widget* pControl)
{
if (pControl == mxPBAddEffect.get())
onAdd();
else if (pControl == mxPBRemoveEffect.get())
onRemove();
else if (pControl == mxLBStart.get())
onChangeStart();
else if (pControl == mxPBPropertyMore.get())
showOptions();
else if (pControl == mxPBMoveUp.get())
moveSelection( true );
else if (pControl == mxPBMoveDown.get())
moveSelection( false );
else if (pControl == mxPBPlay.get())
onPreview( true );
else if (pControl == mxCBAutoPreview.get())
{
SdOptions* pOptions = SdModule::get()->GetSdOptions(DocumentType::Impress);
pOptions->SetPreviewChangedEffects(mxCBAutoPreview->get_active());
}
}
IMPL_LINK_NOARG(CustomAnimationPane, lateInitCallback, Timer *, void )
{
// Call getPresets() to initiate the (expensive) construction of the
// presets list.
CustomAnimationPresets::getCustomAnimationPresets();
// update selection and control states
onSelectionChanged();
}
void CustomAnimationPane::moveSelection( bool bUp )
{
if ( maListSelection.empty() )
return ;
EffectSequenceHelper* pSequence = maListSelection.front()->getEffectSequence();
if ( pSequence == nullptr )
return ;
addUndo();
bool bChanged = false ;
MainSequenceRebuildGuard aGuard( mpMainSequence );
EffectSequence& rEffectSequence = pSequence->getSequence();
if ( bUp )
{
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
EffectSequence::iterator aUpEffectPos( pSequence->find( pEffect ) );
// coverity[copy_paste_error : FALSE] - this is correct, checking if it exists
if ( aUpEffectPos != rEffectSequence.end() )
{
EffectSequence::iterator aInsertPos( rEffectSequence.erase( aUpEffectPos ) );
if ( aInsertPos != rEffectSequence.begin() )
{
--aInsertPos;
while ( (aInsertPos != rEffectSequence.begin()) && !mxCustomAnimationList->isExpanded(*aInsertPos))
--aInsertPos;
rEffectSequence.insert( aInsertPos, pEffect );
}
else
{
rEffectSequence.push_front( pEffect );
}
bChanged = true ;
}
}
}
else
{
EffectSequence::reverse_iterator aIter( maListSelection.rbegin() );
const EffectSequence::reverse_iterator aEnd( maListSelection.rend() );
while ( aIter != aEnd )
{
CustomAnimationEffectPtr pEffect = *aIter++;
EffectSequence::iterator aDownEffectPos( pSequence->find( pEffect ) );
// coverity[copy_paste_error : FALSE] - this is correct, checking if it exists
if ( aDownEffectPos != rEffectSequence.end() )
{
EffectSequence::iterator aInsertPos( rEffectSequence.erase( aDownEffectPos ) );
if ( aInsertPos != rEffectSequence.end() )
{
++aInsertPos;
// Advance over rolled-up (un-expanded) items, unless we just moved it there.
while ( (aInsertPos != rEffectSequence.end())
&& !mxCustomAnimationList->isExpanded(*aInsertPos)
&& (std::find(maListSelection.begin(), maListSelection.end(), *aInsertPos)
== maListSelection.end())
)
++aInsertPos;
rEffectSequence.insert( aInsertPos, pEffect );
}
else
{
rEffectSequence.push_back( pEffect );
}
bChanged = true ;
}
}
}
if ( bChanged )
{
mpMainSequence->rebuild();
updateControls();
mrBase.GetDocShell()->SetModified();
}
}
void CustomAnimationPane::onPreview( bool bForcePreview )
{
if (!bForcePreview && !mxCBAutoPreview->get_active())
return ;
// No preview in LOK.
if (comphelper::LibreOfficeKit::isActive())
return ;
if ( maListSelection.empty() )
{
rtl::Reference< MotionPathTag > xMotionPathTag;
auto aIter = std::find_if(maMotionPathTags.begin(), maMotionPathTags.end(),
[](const MotionPathTagVector::value_type& rxMotionPathTag) { return rxMotionPathTag->isSelected(); });
if (aIter != maMotionPathTags.end())
xMotionPathTag = *aIter;
if ( xMotionPathTag.is() )
{
MainSequencePtr pSequence = std::make_shared<MainSequence>();
pSequence->append( xMotionPathTag->getEffect()->clone() );
preview( pSequence->getRootNode() );
}
else
{
Reference< XAnimationNodeSupplier > xNodeSupplier( mxCurrentPage, UNO_QUERY );
if ( !xNodeSupplier.is() )
return ;
preview( xNodeSupplier->getAnimationNode() );
}
}
else
{
MainSequencePtr pSequence = std::make_shared<MainSequence>();
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
pSequence->append( pEffect->clone() );
}
preview( pSequence->getRootNode() );
}
}
void CustomAnimationPane::preview( const Reference< XAnimationNode >& xAnimationNode )
{
Reference< XParallelTimeContainer > xRoot = ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() );
Sequence< css::beans::NamedValue > aUserData
{ { u"node-type" _ustr, css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } };
xRoot->setUserData( aUserData );
xRoot->appendChild( xAnimationNode );
SlideShow::StartPreview( mrBase, mxCurrentPage, xRoot );
}
// ICustomAnimationListController
void CustomAnimationPane::onSelect()
{
maListSelection = mxCustomAnimationList->getSelection();
updateControls();
// mark shapes from selected effects
if ( maSelectionLock.isLocked() )
return ;
// tdf#145030 if nothing is selected in the effects list, leave the selection of
// objects in the slide untouched
if (maListSelection.empty())
return ;
ScopeLockGuard aGuard( maSelectionLock );
DrawViewShell* pViewShell = dynamic_cast < DrawViewShell* >(
FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get());
DrawView* pView = pViewShell ? pViewShell->GetDrawView() : nullptr;
if ( pView )
{
pView->UnmarkAllObj();
for ( const CustomAnimationEffectPtr& pEffect : maListSelection )
{
Reference< XShape > xShape( pEffect->getTargetShape() );
SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
if ( pObj )
pView->MarkObj(pObj, pView->GetSdrPageView());
}
}
}
// ICustomAnimationListController
// pEffectInsertBefore may be null if moving to end of list.
void CustomAnimationPane::onDragNDropComplete(std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore)
{
if ( !mpMainSequence )
return ;
addUndo();
MainSequenceRebuildGuard aGuard( mpMainSequence );
// Move all selected effects
for ( auto const & pEffectDragged : pEffectsDragged )
{
// Move this dragged effect and any hidden sub-effects
EffectSequence::iterator aIter = mpMainSequence->find( pEffectDragged );
const EffectSequence::iterator aEnd( mpMainSequence->getEnd() );
while ( aIter != aEnd )
{
CustomAnimationEffectPtr pEffect = *aIter++;
// Update model with new location (function triggers a rebuild)
// target may be null, which will insert at the end.
mpMainSequence->moveToBeforeEffect( pEffect, pEffectInsertBefore );
// Done moving effect and its hidden sub-effects when *next* effect is visible.
if (aIter != aEnd && mxCustomAnimationList->isVisible(*aIter))
break ;
}
}
updateControls();
mrBase.GetDocShell()->SetModified();
}
void CustomAnimationPane::updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag )
{
MainSequenceRebuildGuard aGuard( mpMainSequence );
if ( !xTag.is() )
return ;
SdrPathObj* pPathObj = xTag->getPathObj();
CustomAnimationEffectPtr pEffect = xTag->getEffect();
if ( (pPathObj != nullptr) && pEffect )
{
SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager();
if ( pManager )
{
SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
if ( pPage )
pManager->AddUndoAction( std::make_unique<UndoAnimationPath>( mrBase.GetDocShell()->GetDoc(), pPage, pEffect->getNode() ) );
}
pEffect->updatePathFromSdrPathObj( *pPathObj );
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=97 H=93 G=94
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.67Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-01)
¤
*Eine klare Vorstellung vom Zielzustand
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1897691