android: Update app-specific/MIME type icons
[LibreOffice.git] / sd / source / ui / animations / CustomAnimationPane.cxx
blob2276fa91b3aa4aafa9646014feeafa825bdb726e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/presentation/EffectPresetClass.hpp>
21 #include <com/sun/star/animations/XAnimationNodeSupplier.hpp>
22 #include <com/sun/star/animations/AnimationNodeType.hpp>
23 #include <com/sun/star/animations/ParallelTimeContainer.hpp>
24 #include <com/sun/star/view/XSelectionSupplier.hpp>
25 #include <com/sun/star/document/XActionLockable.hpp>
26 #include <com/sun/star/drawing/XDrawView.hpp>
27 #include <com/sun/star/drawing/XShape.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/presentation/EffectNodeType.hpp>
30 #include <com/sun/star/presentation/EffectCommands.hpp>
31 #include <com/sun/star/animations/AnimationTransformType.hpp>
32 #include <com/sun/star/text/XTextRangeCompare.hpp>
33 #include <com/sun/star/container/XEnumerationAccess.hpp>
34 #include <com/sun/star/container/XIndexAccess.hpp>
35 #include <com/sun/star/presentation/ParagraphTarget.hpp>
36 #include <com/sun/star/text/XText.hpp>
37 #include <com/sun/star/drawing/LineStyle.hpp>
38 #include <com/sun/star/drawing/FillStyle.hpp>
39 #include <comphelper/processfactory.hxx>
40 #include <comphelper/scopeguard.hxx>
41 #include <sfx2/dispatch.hxx>
42 #include <sfx2/viewfrm.hxx>
43 #include <tools/debug.hxx>
44 #include "STLPropertySet.hxx"
45 #include <CustomAnimationPane.hxx>
46 #include "CustomAnimationDialog.hxx"
47 #include <CustomAnimationList.hxx>
48 #include "motionpathtag.hxx"
49 #include <CustomAnimationPreset.hxx>
51 #include <comphelper/lok.hxx>
52 #include <comphelper/sequence.hxx>
53 #include <sfx2/frame.hxx>
54 #include <comphelper/diagnose_ex.hxx>
56 #include <svx/svxids.hrc>
57 #include <DrawDocShell.hxx>
58 #include <ViewShellBase.hxx>
59 #include <DrawViewShell.hxx>
60 #include <DrawController.hxx>
61 #include <sdresid.hxx>
62 #include <drawview.hxx>
63 #include <slideshow.hxx>
64 #include <undoanim.hxx>
65 #include <optsitem.hxx>
66 #include <sdmod.hxx>
67 #include <framework/FrameworkHelper.hxx>
69 #include <EventMultiplexer.hxx>
71 #include <strings.hrc>
72 #include <sdpage.hxx>
73 #include <app.hrc>
75 #include <svx/strings.hrc>
76 #include <svx/dialmgr.hxx>
78 #include <algorithm>
79 #include <memory>
81 using namespace ::com::sun::star;
82 using namespace ::com::sun::star::animations;
83 using namespace ::com::sun::star::presentation;
84 using namespace ::com::sun::star::text;
86 using namespace ::com::sun::star::uno;
87 using namespace ::com::sun::star::drawing;
88 using ::com::sun::star::view::XSelectionSupplier;
89 using ::com::sun::star::beans::XPropertySet;
90 using ::com::sun::star::container::XIndexAccess;
91 using ::com::sun::star::container::XEnumerationAccess;
92 using ::com::sun::star::container::XEnumeration;
93 using ::com::sun::star::text::XText;
94 using ::sd::framework::FrameworkHelper;
95 using ::com::sun::star::uno::UNO_QUERY;
96 using ::com::sun::star::uno::UNO_QUERY_THROW;
97 using ::com::sun::star::uno::Any;
98 using ::com::sun::star::uno::Reference;
99 using ::com::sun::star::uno::Exception;
101 namespace sd {
103 void fillRepeatComboBox(weld::ComboBox& rBox)
105 OUString aNone( SdResId( STR_CUSTOMANIMATION_REPEAT_NONE ) );
106 rBox.append_text(aNone);
107 rBox.append_text(OUString::number(2));
108 rBox.append_text(OUString::number(3));
109 rBox.append_text(OUString::number(4));
110 rBox.append_text(OUString::number(5));
111 rBox.append_text(OUString::number(10));
113 OUString aUntilClick( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_NEXT_CLICK ) );
114 rBox.append_text(aUntilClick);
116 OUString aEndOfSlide( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_END_OF_SLIDE ) );
117 rBox.append_text(aEndOfSlide);
120 CustomAnimationPane::CustomAnimationPane( weld::Widget* pParent, ViewShellBase& rBase )
121 : PanelLayout(pParent, "CustomAnimationsPanel", "modules/simpress/ui/customanimationspanel.ui")
122 , mrBase(rBase)
123 // load resources
124 , mxFTAnimation(m_xBuilder->weld_label("effectlabel"))
125 , mxCustomAnimationList(new CustomAnimationList(m_xBuilder->weld_tree_view("custom_animation_list"),
126 m_xBuilder->weld_label("custom_animation_label"),
127 m_xBuilder->weld_widget("custom_animation_label_parent")))
128 , mxPBAddEffect(m_xBuilder->weld_button("add_effect"))
129 , mxPBRemoveEffect(m_xBuilder->weld_button("remove_effect"))
130 , mxPBMoveUp(m_xBuilder->weld_button("move_up"))
131 , mxPBMoveDown(m_xBuilder->weld_button("move_down"))
132 , mxFTCategory(m_xBuilder->weld_label("categorylabel"))
133 , mxLBCategory(m_xBuilder->weld_combo_box("categorylb"))
134 , mxFTEffect(m_xBuilder->weld_label("effect_label"))
135 , mxLBAnimation(m_xBuilder->weld_tree_view("effect_list"))
136 , mxFTStart(m_xBuilder->weld_label("start_effect"))
137 , mxLBStart(m_xBuilder->weld_combo_box("start_effect_list"))
138 , mxFTProperty(m_xBuilder->weld_label("effect_property"))
139 , mxPlaceholderBox(m_xBuilder->weld_container("placeholder"))
140 , mxPBPropertyMore(m_xBuilder->weld_button("more_properties"))
141 , mxFTDuration(m_xBuilder->weld_label("effect_duration"))
142 , mxCBXDuration(m_xBuilder->weld_metric_spin_button("anim_duration", FieldUnit::SECOND))
143 , mxFTStartDelay(m_xBuilder->weld_label("delay_label"))
144 , mxMFStartDelay(m_xBuilder->weld_metric_spin_button("delay_value", FieldUnit::SECOND))
145 , mxCBAutoPreview(m_xBuilder->weld_check_button("auto_preview"))
146 , mxPBPlay(m_xBuilder->weld_button("play"))
147 , maIdle("sd idle treeview select")
148 , mnLastSelectedAnimation(-1)
149 , mnPropertyType(nPropertyTypeNone)
150 , mnCurvePathPos(-1)
151 , mnPolygonPathPos(-1)
152 , mnFreeformPathPos(-1)
153 , maLateInitTimer("sd CustomAnimationPane maLateInitTimer")
155 initialize();
158 css::ui::LayoutSize CustomAnimationPane::GetHeightForWidth(const sal_Int32 /*nWidth*/)
160 sal_Int32 nMinimumHeight = get_preferred_size().Height();
161 return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight);
164 void CustomAnimationPane::initialize()
166 mxLBAnimation->connect_changed(LINK(this, CustomAnimationPane, AnimationSelectHdl));
167 mxCustomAnimationList->setController( static_cast<ICustomAnimationListController*> ( this ) );
168 mxCustomAnimationList->set_size_request(mxCustomAnimationList->get_approximate_digit_width() * 15,
169 mxCustomAnimationList->get_height_rows(4));
171 mxLBAnimation->set_size_request(mxLBAnimation->get_approximate_digit_width() * 15,
172 mxLBAnimation->get_height_rows(4));
174 maStrProperty = mxFTProperty->get_label();
176 mxPBAddEffect->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) );
177 mxPBRemoveEffect->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) );
178 mxLBStart->connect_changed( LINK( this, CustomAnimationPane, implControlListBoxHdl ) );
179 mxCBXDuration->connect_value_changed(LINK( this, CustomAnimationPane, DurationModifiedHdl));
180 mxPBPropertyMore->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) );
181 mxPBMoveUp->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) );
182 mxPBMoveDown->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) );
183 mxPBPlay->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) );
184 mxCBAutoPreview->connect_toggled( LINK( this, CustomAnimationPane, implToggleHdl ) );
185 mxLBCategory->connect_changed( LINK(this, CustomAnimationPane, UpdateAnimationLB) );
186 mxMFStartDelay->connect_value_changed( LINK(this, CustomAnimationPane, DelayModifiedHdl) );
187 mxMFStartDelay->connect_focus_out(LINK( this, CustomAnimationPane, DelayLoseFocusHdl));
189 maIdle.SetPriority(TaskPriority::DEFAULT);
190 maIdle.SetInvokeHandler(LINK(this, CustomAnimationPane, SelectionHandler));
192 maStrModify = mxFTEffect->get_label();
194 // get current controller and initialize listeners
197 mxView.set(mrBase.GetController(), UNO_QUERY);
198 addListener();
200 catch( Exception& )
202 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::CustomAnimationPane()" );
205 // tdf#137637 keep user selection during initialization
206 ScopeLockGuard aGuard(maSelectionLock);
207 // get current page and update custom animation list
208 onChangeCurrentPage();
210 // Wait a short time before the presets list is created. This gives the
211 // system time to paint the control.
212 maLateInitTimer.SetTimeout(100);
213 maLateInitTimer.SetInvokeHandler(LINK(this, CustomAnimationPane, lateInitCallback));
214 maLateInitTimer.Start();
217 CustomAnimationPane::~CustomAnimationPane()
219 maLateInitTimer.Stop();
221 removeListener();
223 MotionPathTagVector aTags;
224 aTags.swap( maMotionPathTags );
225 for (auto const& tag : aTags)
226 tag->Dispose();
228 mxPBAddEffect.reset();
229 mxPBRemoveEffect.reset();
230 mxFTEffect.reset();
231 mxFTStart.reset();
232 mxLBStart.reset();
233 mxLBSubControl.reset();
234 mxFTProperty.reset();
235 mxPlaceholderBox.reset();
236 mxPBPropertyMore.reset();
237 mxFTDuration.reset();
238 mxCBXDuration.reset();
239 mxFTStartDelay.reset();
240 mxMFStartDelay.reset();
241 mxCustomAnimationList.reset();
242 mxPBMoveUp.reset();
243 mxPBMoveDown.reset();
244 mxPBPlay.reset();
245 mxCBAutoPreview.reset();
246 mxFTCategory.reset();
247 mxLBCategory.reset();
248 mxFTAnimation.reset();
249 mxLBAnimation.reset();
252 void CustomAnimationPane::addUndo()
254 SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager();
255 if( pManager )
257 SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
258 if( pPage )
259 pManager->AddUndoAction( std::make_unique<UndoAnimation>( mrBase.GetDocShell()->GetDoc(), pPage ) );
263 void CustomAnimationPane::addListener()
265 Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,CustomAnimationPane,EventMultiplexerListener) );
266 mrBase.GetEventMultiplexer()->AddEventListener(aLink);
269 void CustomAnimationPane::removeListener()
271 Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,CustomAnimationPane,EventMultiplexerListener) );
272 mrBase.GetEventMultiplexer()->RemoveEventListener( aLink );
275 IMPL_LINK(CustomAnimationPane,EventMultiplexerListener,
276 tools::EventMultiplexerEvent&, rEvent, void)
278 switch (rEvent.meEventId)
280 case EventMultiplexerEventId::EditViewSelection:
281 onSelectionChanged();
282 break;
284 case EventMultiplexerEventId::CurrentPageChanged:
285 onChangeCurrentPage();
286 break;
288 case EventMultiplexerEventId::MainViewAdded:
289 // At this moment the controller may not yet been set at model
290 // or ViewShellBase. Take it from the view shell passed with
291 // the event.
292 if (mrBase.GetMainViewShell() != nullptr)
294 if( mrBase.GetMainViewShell()->GetShellType() == ViewShell::ST_IMPRESS )
296 mxView = mrBase.GetDrawController();
297 onSelectionChanged();
298 onChangeCurrentPage();
299 break;
302 [[fallthrough]];
303 case EventMultiplexerEventId::MainViewRemoved:
304 mxView = nullptr;
305 mxCurrentPage = nullptr;
306 updateControls();
307 break;
309 case EventMultiplexerEventId::Disposing:
310 mxView.clear();
311 onSelectionChanged();
312 onChangeCurrentPage();
313 break;
314 case EventMultiplexerEventId::EndTextEdit:
315 if (mpMainSequence && rEvent.mpUserData)
316 mxCustomAnimationList->update( mpMainSequence );
317 break;
318 default: break;
322 static sal_Int32 getPropertyType( std::u16string_view rProperty )
324 if ( rProperty == u"Direction" )
325 return nPropertyTypeDirection;
327 if ( rProperty == u"Spokes" )
328 return nPropertyTypeSpokes;
330 if ( rProperty == u"Zoom" )
331 return nPropertyTypeZoom;
333 if ( rProperty == u"Accelerate" )
334 return nPropertyTypeAccelerate;
336 if ( rProperty == u"Decelerate" )
337 return nPropertyTypeDecelerate;
339 if ( rProperty == u"Color1" )
340 return nPropertyTypeFirstColor;
342 if ( rProperty == u"Color2" )
343 return nPropertyTypeSecondColor;
345 if ( rProperty == u"FillColor" )
346 return nPropertyTypeFillColor;
348 if ( rProperty == u"ColorStyle" )
349 return nPropertyTypeColorStyle;
351 if ( rProperty == u"AutoReverse" )
352 return nPropertyTypeAutoReverse;
354 if ( rProperty == u"FontStyle" )
355 return nPropertyTypeFont;
357 if ( rProperty == u"CharColor" )
358 return nPropertyTypeCharColor;
360 if ( rProperty == u"CharHeight" )
361 return nPropertyTypeCharHeight;
363 if ( rProperty == u"CharDecoration" )
364 return nPropertyTypeCharDecoration;
366 if ( rProperty == u"LineColor" )
367 return nPropertyTypeLineColor;
369 if ( rProperty == u"Rotate" )
370 return nPropertyTypeRotate;
372 if ( rProperty == u"Transparency" )
373 return nPropertyTypeTransparency;
375 if ( rProperty == u"Color" )
376 return nPropertyTypeColor;
378 if ( rProperty == u"Scale" )
379 return nPropertyTypeScale;
381 return nPropertyTypeNone;
384 OUString getPropertyName( sal_Int32 nPropertyType )
386 switch( nPropertyType )
388 case nPropertyTypeDirection:
389 return SdResId(STR_CUSTOMANIMATION_DIRECTION_PROPERTY);
391 case nPropertyTypeSpokes:
392 return SdResId(STR_CUSTOMANIMATION_SPOKES_PROPERTY);
394 case nPropertyTypeFirstColor:
395 return SdResId(STR_CUSTOMANIMATION_FIRST_COLOR_PROPERTY);
397 case nPropertyTypeSecondColor:
398 return SdResId(STR_CUSTOMANIMATION_SECOND_COLOR_PROPERTY);
400 case nPropertyTypeZoom:
401 return SdResId(STR_CUSTOMANIMATION_ZOOM_PROPERTY);
403 case nPropertyTypeFillColor:
404 return SdResId(STR_CUSTOMANIMATION_FILL_COLOR_PROPERTY);
406 case nPropertyTypeColorStyle:
407 return SdResId(STR_CUSTOMANIMATION_STYLE_PROPERTY);
409 case nPropertyTypeFont:
410 return SdResId(STR_CUSTOMANIMATION_FONT_PROPERTY);
412 case nPropertyTypeCharHeight:
413 return SdResId(STR_CUSTOMANIMATION_SIZE_PROPERTY);
415 case nPropertyTypeCharColor:
416 return SdResId(STR_CUSTOMANIMATION_FONT_COLOR_PROPERTY);
418 case nPropertyTypeCharHeightStyle:
419 return SdResId(STR_CUSTOMANIMATION_FONT_SIZE_STYLE_PROPERTY);
421 case nPropertyTypeCharDecoration:
422 return SdResId(STR_CUSTOMANIMATION_FONT_STYLE_PROPERTY);
424 case nPropertyTypeLineColor:
425 return SdResId(STR_CUSTOMANIMATION_LINE_COLOR_PROPERTY);
427 case nPropertyTypeRotate:
428 return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY);
430 case nPropertyTypeColor:
431 return SdResId(STR_CUSTOMANIMATION_COLOR_PROPERTY);
433 case nPropertyTypeTransparency:
434 return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY);
436 case nPropertyTypeScale:
437 return SdResId(STR_CUSTOMANIMATION_SCALE_PROPERTY);
440 return OUString();
443 void CustomAnimationPane::updateControls()
445 mxFTDuration->set_sensitive(mxView.is());
446 mxCBXDuration->set_sensitive(mxView.is());
447 mxCustomAnimationList->set_sensitive(mxView.is());
448 if (comphelper::LibreOfficeKit::isActive())
450 mxPBPlay->hide();
451 mxCBAutoPreview->set_active(false);
452 mxCBAutoPreview->hide();
454 else
456 mxPBPlay->set_sensitive(mxView.is());
457 mxCBAutoPreview->set_sensitive(mxView.is());
460 if (!mxView.is())
462 mxPBAddEffect->set_sensitive(false);
463 mxPBRemoveEffect->set_sensitive(false);
464 mxFTStart->set_sensitive(false);
465 mxLBStart->set_sensitive(false);
466 mxPBPropertyMore->set_sensitive(false);
467 mxPlaceholderBox->set_sensitive(false);
468 mxFTProperty->set_sensitive(false);
469 mxFTCategory->set_sensitive(false);
470 mxLBCategory->set_sensitive(false);
471 mxFTAnimation->set_sensitive(false);
472 mxLBAnimation->set_sensitive(false);
473 mxFTStartDelay->set_sensitive(false);
474 mxMFStartDelay->set_sensitive(false);
475 mxLBAnimation->clear();
476 mnLastSelectedAnimation = -1;
477 mxCustomAnimationList->clear();
478 return;
481 const int nSelectionCount = maListSelection.size();
483 mxPBAddEffect->set_sensitive( maViewSelection.hasValue() );
484 mxPBRemoveEffect->set_sensitive(nSelectionCount != 0);
485 bool bIsSelected = (nSelectionCount > 0);
487 if(bIsSelected)
489 mxFTAnimation->set_sensitive(true);
490 mxLBAnimation->set_sensitive(true);
492 else
494 mxFTAnimation->set_sensitive(false);
495 mxLBAnimation->set_sensitive(false);
496 mxLBAnimation->clear();
497 mnLastSelectedAnimation = -1;
500 mxLBCategory->set_sensitive(bIsSelected);
501 mxFTCategory->set_sensitive(bIsSelected);
503 mxFTStart->set_sensitive(nSelectionCount > 0);
504 mxLBStart->set_sensitive(nSelectionCount > 0);
505 mxPlaceholderBox->set_sensitive(nSelectionCount > 0);
506 mxPBPropertyMore->set_sensitive(nSelectionCount > 0);
507 mxFTStartDelay->set_sensitive(nSelectionCount > 0);
508 mxMFStartDelay->set_sensitive(nSelectionCount > 0);
510 mxFTProperty->set_label(maStrProperty);
512 sal_Int32 nOldPropertyType = mnPropertyType;
514 mnPropertyType = nPropertyTypeNone;
516 if(bIsSelected)
518 CustomAnimationEffectPtr pEffect = maListSelection.front();
520 OUString aUIName( CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId( pEffect->getPresetId() ) );
522 OUString aTemp( maStrModify );
524 if( !aUIName.isEmpty() )
526 aTemp += " " + aUIName;
527 mxFTEffect->set_label( aTemp );
530 Any aValue;
531 CustomAnimationPresetPtr pDescriptor = CustomAnimationPresets::getCustomAnimationPresets().getEffectDescriptor( pEffect->getPresetId() );
532 if (pDescriptor)
534 std::vector<OUString> aProperties( pDescriptor->getProperties() );
535 if( !aProperties.empty() )
537 mnPropertyType = getPropertyType( aProperties.front() );
539 mxFTProperty->set_label( getPropertyName( mnPropertyType ) );
541 aValue = getProperty1Value( mnPropertyType, pEffect );
545 sal_Int32 nNewPropertyType = mnPropertyType;
546 // if there is no value, then the control will be disabled, just show a disabled Direction box in that
547 // case to have something to fill the space
548 if (!aValue.hasValue())
549 nNewPropertyType = nPropertyTypeDirection;
551 if (!mxLBSubControl || nOldPropertyType != nNewPropertyType)
553 // for LOK destroy old widgets first
554 mxLBSubControl.reset(nullptr);
555 // then create new control, to keep correct pointers for actions
556 mxLBSubControl = SdPropertySubControl::create(nNewPropertyType, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), aValue, pEffect->getPresetId(), LINK(this, CustomAnimationPane, implPropertyHdl));
558 else
560 mxLBSubControl->setValue(aValue, pEffect->getPresetId());
563 bool bEnable = aValue.hasValue();
564 mxPlaceholderBox->set_sensitive( bEnable );
565 mxFTProperty->set_sensitive( bEnable );
567 if (!pDescriptor)
569 mxPBPropertyMore->set_sensitive( false );
570 mxFTStartDelay->set_sensitive( false );
571 mxMFStartDelay->set_sensitive( false );
573 sal_Int32 nCategoryPos = -1;
574 switch(pEffect->getPresetClass())
576 case EffectPresetClass::ENTRANCE: nCategoryPos = 0; break;
577 case EffectPresetClass::EMPHASIS: nCategoryPos = 1; break;
578 case EffectPresetClass::EXIT: nCategoryPos = 2; break;
579 case EffectPresetClass::MOTIONPATH: nCategoryPos = 3; break;
580 default:
581 break;
583 switch(pEffect->getCommand())
585 case EffectCommands::TOGGLEPAUSE:
586 case EffectCommands::STOP:
587 case EffectCommands::PLAY:
588 nCategoryPos = 4; break;
589 default:
590 break;
592 mxLBCategory->set_active(nCategoryPos);
594 fillAnimationLB( pEffect->hasText() );
596 OUString rsPresetId = pEffect->getPresetId();
597 sal_Int32 nAnimationPos = mxLBAnimation->n_children();
598 while( nAnimationPos-- )
600 auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nAnimationPos));
601 if (pEntryData)
603 CustomAnimationPresetPtr& pPtr = *pEntryData;
604 if( pPtr && pPtr->getPresetId() == rsPresetId )
606 mxLBAnimation->select( nAnimationPos );
607 mnLastSelectedAnimation = nAnimationPos;
608 break;
613 // If preset id is missing and category is motion path.
614 if (nAnimationPos < 0 && nCategoryPos == 3)
616 if (rsPresetId == "libo-motionpath-curve")
618 mxLBAnimation->select(mnCurvePathPos);
619 mnLastSelectedAnimation = mnCurvePathPos;
621 else if (rsPresetId == "libo-motionpath-polygon")
623 mxLBAnimation->select(mnPolygonPathPos);
624 mnLastSelectedAnimation = mnPolygonPathPos;
626 else if (rsPresetId == "libo-motionpath-freeform-line")
628 mxLBAnimation->select(mnFreeformPathPos);
629 mnLastSelectedAnimation = mnFreeformPathPos;
633 sal_uInt16 nPos = 0xffff;
635 sal_Int16 nNodeType = pEffect->getNodeType();
636 switch( nNodeType )
638 case EffectNodeType::ON_CLICK: nPos = 0; break;
639 case EffectNodeType::WITH_PREVIOUS: nPos = 1; break;
640 case EffectNodeType::AFTER_PREVIOUS: nPos = 2; break;
643 mxLBStart->set_active( nPos );
645 double fDuration = pEffect->getDuration();
646 const bool bHasSpeed = fDuration > 0.001;
648 mxFTDuration->set_sensitive(bHasSpeed);
649 mxCBXDuration->set_sensitive(bHasSpeed);
651 if( bHasSpeed )
653 mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE);
656 mxPBPropertyMore->set_sensitive(true);
658 mxFTStartDelay->set_sensitive(true);
659 mxMFStartDelay->set_sensitive(true);
660 double fBegin = pEffect->getBegin();
661 mxMFStartDelay->set_value(fBegin*10.0, FieldUnit::NONE);
663 else
665 // use an empty direction box to fill the space
666 if (!mxLBSubControl || (nOldPropertyType != nPropertyTypeDirection && nOldPropertyType != nPropertyTypeNone))
668 // for LOK destroy old widgets first
669 mxLBSubControl.reset(nullptr);
670 // then create new control, to keep correct pointers for actions
671 mxLBSubControl = SdPropertySubControl::create(nPropertyTypeDirection, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), uno::Any(), OUString(), LINK(this, CustomAnimationPane, implPropertyHdl));
673 else
674 mxLBSubControl->setValue(uno::Any(), OUString());
676 mxPlaceholderBox->set_sensitive(false);
677 mxFTProperty->set_sensitive(false);
678 mxFTStartDelay->set_sensitive(false);
679 mxMFStartDelay->set_sensitive(false);
680 mxPBPropertyMore->set_sensitive(false);
681 mxFTDuration->set_sensitive(false);
682 mxCBXDuration->set_sensitive(false);
683 mxCBXDuration->set_text(OUString());
684 mxFTEffect->set_label(maStrModify);
687 bool bEnableUp = true;
688 bool bEnableDown = true;
689 if( nSelectionCount == 0 )
691 bEnableUp = false;
692 bEnableDown = false;
694 else
696 if( mpMainSequence->find( maListSelection.front() ) == mpMainSequence->getBegin() )
697 bEnableUp = false;
699 EffectSequence::iterator aIter( mpMainSequence->find( maListSelection.back() ) );
700 if( aIter == mpMainSequence->getEnd() )
702 bEnableDown = false;
704 else
708 ++aIter;
710 while( (aIter != mpMainSequence->getEnd()) && !(mxCustomAnimationList->isExpanded(*aIter) ) );
712 if( aIter == mpMainSequence->getEnd() )
713 bEnableDown = false;
716 if( bEnableUp || bEnableDown )
718 MainSequenceRebuildGuard aGuard( mpMainSequence );
720 EffectSequenceHelper* pSequence = nullptr;
721 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
723 if( pEffect )
725 if( pSequence == nullptr )
727 pSequence = pEffect->getEffectSequence();
729 else
731 if( pSequence != pEffect->getEffectSequence() )
733 bEnableUp = false;
734 bEnableDown = false;
735 break;
743 mxPBMoveUp->set_sensitive(mxView.is() && bEnableUp);
744 mxPBMoveDown->set_sensitive(mxView.is() && bEnableDown);
746 SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress);
747 mxCBAutoPreview->set_active(pOptions->IsPreviewChangedEffects());
749 updateMotionPathTags();
752 static bool updateMotionPathImpl( CustomAnimationPane& rPane, ::sd::View& rView, EffectSequence::iterator aIter, const EffectSequence::iterator& aEnd, MotionPathTagVector& rOldTags, MotionPathTagVector& rNewTags )
754 bool bChanges = false;
755 while( aIter != aEnd )
757 CustomAnimationEffectPtr pEffect( *aIter++ );
758 if( pEffect && pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH )
760 rtl::Reference< MotionPathTag > xMotionPathTag;
761 // first try to find if there is already a tag for this
762 auto aMIter = std::find_if(rOldTags.begin(), rOldTags.end(),
763 [&pEffect](const rtl::Reference<MotionPathTag>& xTag) { return xTag->getEffect() == pEffect; });
764 if (aMIter != rOldTags.end())
766 rtl::Reference< MotionPathTag > xTag( *aMIter );
767 if( !xTag->isDisposed() )
769 xMotionPathTag = xTag;
770 rOldTags.erase( aMIter );
774 // if not found, create new one
775 if( !xMotionPathTag.is() )
777 xMotionPathTag.set( new MotionPathTag( rPane, rView, pEffect ) );
778 bChanges = true;
781 if( xMotionPathTag.is() )
782 rNewTags.push_back( xMotionPathTag );
786 return bChanges;
789 void CustomAnimationPane::updateMotionPathTags()
791 bool bChanges = false;
793 MotionPathTagVector aTags;
794 aTags.swap( maMotionPathTags );
796 ::sd::View* pView = nullptr;
798 if( mxView.is() )
800 std::shared_ptr<ViewShell> xViewShell( mrBase.GetMainViewShell() );
801 if( xViewShell )
802 pView = xViewShell->GetView();
805 if (mpMainSequence && pView)
807 bChanges = updateMotionPathImpl( *this, *pView, mpMainSequence->getBegin(), mpMainSequence->getEnd(), aTags, maMotionPathTags );
809 auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector();
810 for (InteractiveSequencePtr const& pIS : rInteractiveSequenceVector)
812 bChanges |= updateMotionPathImpl( *this, *pView, pIS->getBegin(), pIS->getEnd(), aTags, maMotionPathTags );
816 if( !aTags.empty() )
818 bChanges = true;
819 for( rtl::Reference< MotionPathTag >& xTag : aTags )
821 xTag->Dispose();
825 if( bChanges && pView )
826 pView->updateHandles();
829 void CustomAnimationPane::onSelectionChanged()
831 if( maSelectionLock.isLocked() )
832 return;
834 ScopeLockGuard aGuard( maSelectionLock );
836 if( mxView.is() ) try
838 Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW );
839 maViewSelection = xSel->getSelection();
840 mxCustomAnimationList->onSelectionChanged( maViewSelection );
841 updateControls();
843 catch( Exception& )
845 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onSelectionChanged()" );
849 void CustomAnimationPane::onDoubleClick()
851 showOptions();
854 void CustomAnimationPane::onContextMenu(const OUString &rIdent)
856 if (rIdent == "onclick")
857 onChangeStart( EffectNodeType::ON_CLICK );
858 else if (rIdent == "withprev")
859 onChangeStart( EffectNodeType::WITH_PREVIOUS );
860 else if (rIdent == "afterprev")
861 onChangeStart( EffectNodeType::AFTER_PREVIOUS );
862 else if (rIdent == "options")
863 showOptions();
864 else if (rIdent == "timing")
865 showOptions("timing");
866 else if (rIdent == "remove")
867 onRemove();
868 else if (rIdent == "create" && maViewSelection.hasValue())
869 onAdd();
870 updateControls();
873 static void addValue( const std::unique_ptr<STLPropertySet>& pSet, sal_Int32 nHandle, const Any& rValue )
875 switch( pSet->getPropertyState( nHandle ) )
877 case STLPropertyState::Ambiguous:
878 // value is already ambiguous, do nothing
879 break;
880 case STLPropertyState::Direct:
881 // set to ambiguous if existing value is different
882 if( rValue != pSet->getPropertyValue( nHandle ) )
883 pSet->setPropertyState( nHandle, STLPropertyState::Ambiguous );
884 break;
885 case STLPropertyState::Default:
886 // just set new value
887 pSet->setPropertyValue( nHandle, rValue );
888 break;
892 static sal_Int32 calcMaxParaDepth( const Reference< XShape >& xTargetShape )
894 sal_Int32 nMaxParaDepth = -1;
896 if( xTargetShape.is() )
898 Reference< XEnumerationAccess > xText( xTargetShape, UNO_QUERY );
899 if( xText.is() )
901 Reference< XPropertySet > xParaSet;
903 Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW );
904 while( xEnumeration->hasMoreElements() )
906 xEnumeration->nextElement() >>= xParaSet;
907 if( xParaSet.is() )
909 sal_Int32 nParaDepth = 0;
910 xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth;
912 if( nParaDepth > nMaxParaDepth )
913 nMaxParaDepth = nParaDepth;
919 return nMaxParaDepth + 1;
922 Any CustomAnimationPane::getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect )
924 switch( nType )
926 case nPropertyTypeDirection:
927 case nPropertyTypeSpokes:
928 case nPropertyTypeZoom:
929 return Any( pEffect->getPresetSubType() );
931 case nPropertyTypeColor:
932 case nPropertyTypeFillColor:
933 case nPropertyTypeFirstColor:
934 case nPropertyTypeSecondColor:
935 case nPropertyTypeCharColor:
936 case nPropertyTypeLineColor:
938 const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1;
939 return pEffect->getColor( nIndex );
942 case nPropertyTypeFont:
943 return pEffect->getProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To );
945 case nPropertyTypeCharHeight:
947 static const OUStringLiteral aAttributeName( u"CharHeight" );
948 Any aValue( pEffect->getProperty( AnimationNodeType::SET, aAttributeName, EValue::To ) );
949 if( !aValue.hasValue() )
950 aValue = pEffect->getProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To );
951 return aValue;
954 case nPropertyTypeRotate:
955 return pEffect->getTransformationProperty( AnimationTransformType::ROTATE, EValue::By);
957 case nPropertyTypeTransparency:
958 return pEffect->getProperty( AnimationNodeType::SET, u"Opacity" , EValue::To );
960 case nPropertyTypeScale:
961 return pEffect->getTransformationProperty( AnimationTransformType::SCALE, EValue::By );
963 case nPropertyTypeCharDecoration:
965 Sequence< Any > aValues{
966 pEffect->getProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To ),
967 pEffect->getProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To ),
968 pEffect->getProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To )
970 return Any( aValues );
974 Any aAny;
975 return aAny;
978 bool CustomAnimationPane::setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const Any& rValue )
980 bool bEffectChanged = false;
981 switch( nType )
983 case nPropertyTypeDirection:
984 case nPropertyTypeSpokes:
985 case nPropertyTypeZoom:
987 OUString aPresetSubType;
988 rValue >>= aPresetSubType;
989 if( aPresetSubType != pEffect->getPresetSubType() )
991 CustomAnimationPresets::getCustomAnimationPresets().changePresetSubType( pEffect, aPresetSubType );
992 bEffectChanged = true;
995 break;
997 case nPropertyTypeFillColor:
998 case nPropertyTypeColor:
999 case nPropertyTypeFirstColor:
1000 case nPropertyTypeSecondColor:
1001 case nPropertyTypeCharColor:
1002 case nPropertyTypeLineColor:
1004 const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1;
1005 Any aOldColor( pEffect->getColor( nIndex ) );
1006 if( aOldColor != rValue )
1008 pEffect->setColor( nIndex, rValue );
1009 bEffectChanged = true;
1012 break;
1014 case nPropertyTypeFont:
1015 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To, rValue );
1016 break;
1018 case nPropertyTypeCharHeight:
1020 static const OUStringLiteral aAttributeName( u"CharHeight" );
1021 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, aAttributeName, EValue::To, rValue );
1022 if( !bEffectChanged )
1023 bEffectChanged = pEffect->setProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To, rValue );
1025 break;
1026 case nPropertyTypeRotate:
1027 bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::ROTATE, EValue::By , rValue );
1028 break;
1030 case nPropertyTypeTransparency:
1031 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"Opacity" , EValue::To, rValue );
1032 break;
1034 case nPropertyTypeScale:
1035 bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::SCALE, EValue::By, rValue );
1036 break;
1038 case nPropertyTypeCharDecoration:
1040 Sequence< Any > aValues(3);
1041 rValue >>= aValues;
1042 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To, aValues[0] );
1043 bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To, aValues[1] );
1044 bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To, aValues[2] );
1046 break;
1050 return bEffectChanged;
1053 static bool hasVisibleShape( const Reference< XShape >& xShape )
1057 const OUString sShapeType( xShape->getShapeType() );
1059 if( sShapeType == "com.sun.star.presentation.TitleTextShape" || sShapeType == "com.sun.star.presentation.OutlinerShape" ||
1060 sShapeType == "com.sun.star.presentation.SubtitleShape" || sShapeType == "com.sun.star.drawing.TextShape" )
1062 Reference< XPropertySet > xSet( xShape, UNO_QUERY_THROW );
1064 FillStyle eFillStyle;
1065 xSet->getPropertyValue( "FillStyle" ) >>= eFillStyle;
1067 css::drawing::LineStyle eLineStyle;
1068 xSet->getPropertyValue( "LineStyle" ) >>= eLineStyle;
1070 return eFillStyle != FillStyle_NONE || eLineStyle != css::drawing::LineStyle_NONE;
1073 catch( Exception& )
1076 return true;
1079 std::unique_ptr<STLPropertySet> CustomAnimationPane::createSelectionSet()
1081 std::unique_ptr<STLPropertySet> pSet = CustomAnimationDialog::createDefaultSet();
1083 pSet->setPropertyValue( nHandleCurrentPage, Any( mxCurrentPage ) );
1085 sal_Int32 nMaxParaDepth = 0;
1087 // get options from selected effects
1088 const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets());
1089 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1091 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
1092 if( !pEffectSequence )
1093 pEffectSequence = mpMainSequence.get();
1095 if( pEffect->hasText() )
1097 sal_Int32 n = calcMaxParaDepth(pEffect->getTargetShape());
1098 if( n > nMaxParaDepth )
1099 nMaxParaDepth = n;
1102 addValue( pSet, nHandleHasAfterEffect, Any( pEffect->hasAfterEffect() ) );
1103 addValue( pSet, nHandleAfterEffectOnNextEffect, Any( pEffect->IsAfterEffectOnNext() ) );
1104 addValue( pSet, nHandleDimColor, pEffect->getDimColor() );
1105 addValue( pSet, nHandleIterateType, Any( pEffect->getIterateType() ) );
1107 // convert absolute time to percentage value
1108 // This calculation is done in float to avoid some rounding artifacts.
1109 float fIterateInterval = static_cast<float>(pEffect->getIterateInterval());
1110 if( pEffect->getDuration() )
1111 fIterateInterval = static_cast<float>(fIterateInterval / pEffect->getDuration() );
1112 fIterateInterval *= 100.0;
1113 addValue( pSet, nHandleIterateInterval, Any( static_cast<double>(fIterateInterval) ) );
1115 addValue( pSet, nHandleBegin, Any( pEffect->getBegin() ) );
1116 addValue( pSet, nHandleDuration, Any( pEffect->getDuration() ) );
1117 addValue( pSet, nHandleStart, Any( pEffect->getNodeType() ) );
1118 addValue( pSet, nHandleRepeat, pEffect->getRepeatCount() );
1119 addValue( pSet, nHandleEnd, pEffect->getEnd() );
1120 addValue( pSet, nHandleRewind, Any( pEffect->getFill() ) );
1122 addValue( pSet, nHandlePresetId, Any( pEffect->getPresetId() ) );
1124 addValue( pSet, nHandleHasText, Any( pEffect->hasText() ) );
1126 addValue( pSet, nHandleHasVisibleShape, Any( hasVisibleShape( pEffect->getTargetShape() ) ) );
1128 Any aSoundSource;
1129 if( pEffect->getAudio().is() )
1131 aSoundSource = pEffect->getAudio()->getSource();
1132 addValue( pSet, nHandleSoundVolume, Any( pEffect->getAudio()->getVolume() ) );
1133 // todo addValue( pSet, nHandleSoundEndAfterSlide, makeAny( pEffect->getAudio()->getEndAfterSlide() ) );
1134 // this is now stored at the XCommand parameter sequence
1136 else if( pEffect->getCommand() == EffectCommands::STOPAUDIO )
1138 aSoundSource <<= true;
1140 addValue( pSet, nHandleSoundURL, aSoundSource );
1142 sal_Int32 nGroupId = pEffect->getGroupId();
1143 CustomAnimationTextGroupPtr pTextGroup;
1144 if( nGroupId != -1 )
1145 pTextGroup = pEffectSequence->findGroup( nGroupId );
1147 addValue( pSet, nHandleTextGrouping, Any( pTextGroup ? pTextGroup->getTextGrouping() : sal_Int32(-1) ) );
1148 addValue( pSet, nHandleAnimateForm, Any( !pTextGroup || pTextGroup->getAnimateForm() ) );
1149 addValue( pSet, nHandleTextGroupingAuto, Any( pTextGroup ? pTextGroup->getTextGroupingAuto() : -1.0 ) );
1150 addValue( pSet, nHandleTextReverse, Any( pTextGroup && pTextGroup->getTextReverse() ) );
1152 if( pEffectSequence->getSequenceType() == EffectNodeType::INTERACTIVE_SEQUENCE )
1154 InteractiveSequence* pIS = static_cast< InteractiveSequence* >( pEffectSequence );
1155 addValue( pSet, nHandleTrigger, Any( pIS->getTriggerShape() ) );
1158 CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( pEffect->getPresetId() );
1159 if( pDescriptor )
1161 sal_Int32 nType = nPropertyTypeNone;
1163 std::vector<OUString> aProperties( pDescriptor->getProperties() );
1164 if( !aProperties.empty() )
1165 nType = getPropertyType( aProperties.front() );
1167 if( nType != nPropertyTypeNone )
1169 addValue( pSet, nHandleProperty1Type, Any( nType ) );
1170 addValue( pSet, nHandleProperty1Value, getProperty1Value( nType, pEffect ) );
1173 if( pDescriptor->hasProperty( u"Accelerate" ) )
1175 addValue( pSet, nHandleAccelerate, Any( pEffect->getAcceleration() ) );
1178 if( pDescriptor->hasProperty( u"Decelerate" ) )
1180 addValue( pSet, nHandleDecelerate, Any( pEffect->getDecelerate() ) );
1183 if( pDescriptor->hasProperty( u"AutoReverse" ) )
1185 addValue( pSet, nHandleAutoReverse, Any( pEffect->getAutoReverse() ) );
1190 addValue( pSet, nHandleMaxParaDepth, Any( nMaxParaDepth ) );
1192 return pSet;
1195 void CustomAnimationPane::changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet )
1197 // change selected effect
1198 bool bChanged = false;
1200 MainSequenceRebuildGuard aGuard( mpMainSequence );
1202 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1204 DBG_ASSERT( pEffect->getEffectSequence(), "sd::CustomAnimationPane::changeSelection(), dead effect in selection!" );
1205 if( !pEffect->getEffectSequence() )
1206 continue;
1208 double fDuration = 0.0; // we might need this for iterate-interval
1209 if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct )
1211 pResultSet->getPropertyValue( nHandleDuration ) >>= fDuration;
1213 else
1215 fDuration = pEffect->getDuration();
1218 if( pResultSet->getPropertyState( nHandleIterateType ) == STLPropertyState::Direct )
1220 sal_Int16 nIterateType = 0;
1221 pResultSet->getPropertyValue( nHandleIterateType ) >>= nIterateType;
1222 if( pEffect->getIterateType() != nIterateType )
1224 pEffect->setIterateType( nIterateType );
1225 bChanged = true;
1229 if( pEffect->getIterateType() )
1231 if( pResultSet->getPropertyState( nHandleIterateInterval ) == STLPropertyState::Direct )
1233 double fIterateInterval = 0.0;
1234 pResultSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval;
1235 if( pEffect->getIterateInterval() != fIterateInterval )
1237 const double f = fIterateInterval * pEffect->getDuration() / 100;
1238 pEffect->setIterateInterval( f );
1239 bChanged = true;
1244 double fBegin = 0.0;
1246 if( pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct )
1247 pResultSet->getPropertyValue( nHandleBegin ) >>= fBegin;
1248 else
1249 fBegin = pEffect->getBegin();
1251 if( pEffect->getBegin() != fBegin && pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct)
1253 pEffect->setBegin( fBegin );
1254 bChanged = true;
1257 if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct )
1259 if( pEffect->getDuration() != fDuration )
1261 pEffect->setDuration( fDuration );
1262 bChanged = true;
1266 if( pResultSet->getPropertyState( nHandleStart ) == STLPropertyState::Direct )
1268 sal_Int16 nNodeType = 0;
1269 pResultSet->getPropertyValue( nHandleStart ) >>= nNodeType;
1270 if( pEffect->getNodeType() != nNodeType )
1272 pEffect->setNodeType( nNodeType );
1273 bChanged = true;
1277 if( pResultSet->getPropertyState( nHandleRepeat ) == STLPropertyState::Direct )
1279 Any aRepeatCount( pResultSet->getPropertyValue( nHandleRepeat ) );
1280 if( aRepeatCount != pEffect->getRepeatCount() )
1282 pEffect->setRepeatCount( aRepeatCount );
1283 bChanged = true;
1287 if( pResultSet->getPropertyState( nHandleEnd ) == STLPropertyState::Direct )
1289 Any aEndValue( pResultSet->getPropertyValue( nHandleEnd ) );
1290 if( pEffect->getEnd() != aEndValue )
1292 pEffect->setEnd( aEndValue );
1293 bChanged = true;
1297 if( pResultSet->getPropertyState( nHandleRewind ) == STLPropertyState::Direct )
1299 sal_Int16 nFill = 0;
1300 pResultSet->getPropertyValue( nHandleRewind ) >>= nFill;
1301 if( pEffect->getFill() != nFill )
1303 pEffect->setFill( nFill );
1304 bChanged = true;
1308 if( pResultSet->getPropertyState( nHandleHasAfterEffect ) == STLPropertyState::Direct )
1310 bool bHasAfterEffect = false;
1311 if( pResultSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect )
1313 if( pEffect->hasAfterEffect() != bHasAfterEffect )
1315 pEffect->setHasAfterEffect( bHasAfterEffect );
1316 bChanged = true;
1321 if( pResultSet->getPropertyState( nHandleAfterEffectOnNextEffect ) == STLPropertyState::Direct )
1323 bool bAfterEffectOnNextEffect = false;
1324 if( (pResultSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextEffect)
1325 && (pEffect->IsAfterEffectOnNext() != bAfterEffectOnNextEffect) )
1327 pEffect->setAfterEffectOnNext( bAfterEffectOnNextEffect );
1328 bChanged = true;
1332 if( pResultSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Direct )
1334 Any aDimColor( pResultSet->getPropertyValue( nHandleDimColor ) );
1335 if( pEffect->getDimColor() != aDimColor )
1337 pEffect->setDimColor( aDimColor );
1338 bChanged = true;
1342 if( pResultSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct )
1344 double fAccelerate = 0.0;
1345 pResultSet->getPropertyValue( nHandleAccelerate ) >>= fAccelerate;
1346 if( pEffect->getAcceleration() != fAccelerate )
1348 pEffect->setAcceleration( fAccelerate );
1349 bChanged = true;
1353 if( pResultSet->getPropertyState( nHandleDecelerate ) == STLPropertyState::Direct )
1355 double fDecelerate = 0.0;
1356 pResultSet->getPropertyValue( nHandleDecelerate ) >>= fDecelerate;
1357 if( pEffect->getDecelerate() != fDecelerate )
1359 pEffect->setDecelerate( fDecelerate );
1360 bChanged = true;
1364 if( pResultSet->getPropertyState( nHandleAutoReverse ) == STLPropertyState::Direct )
1366 bool bAutoReverse = false;
1367 pResultSet->getPropertyValue( nHandleAutoReverse ) >>= bAutoReverse;
1368 if( pEffect->getAutoReverse() != bAutoReverse )
1370 pEffect->setAutoReverse( bAutoReverse );
1371 bChanged = true;
1375 if( pResultSet->getPropertyState( nHandleProperty1Value ) == STLPropertyState::Direct )
1377 sal_Int32 nType = 0;
1378 pOldSet->getPropertyValue( nHandleProperty1Type ) >>= nType;
1380 bChanged |= setProperty1Value( nType, pEffect, pResultSet->getPropertyValue( nHandleProperty1Value ) );
1383 if( pResultSet->getPropertyState( nHandleSoundURL ) == STLPropertyState::Direct )
1385 const Any aSoundSource( pResultSet->getPropertyValue( nHandleSoundURL ) );
1387 if( aSoundSource.getValueType() == ::cppu::UnoType<sal_Bool>::get() )
1389 pEffect->setStopAudio();
1390 bChanged = true;
1392 else
1394 OUString aSoundURL;
1395 aSoundSource >>= aSoundURL;
1397 if( !aSoundURL.isEmpty() )
1399 if( !pEffect->getAudio().is() )
1401 pEffect->createAudio( aSoundSource );
1402 bChanged = true;
1404 else
1406 if( pEffect->getAudio()->getSource() != aSoundSource )
1408 pEffect->getAudio()->setSource( aSoundSource );
1409 bChanged = true;
1413 else
1415 if( pEffect->getAudio().is() || pEffect->getStopAudio() )
1417 pEffect->removeAudio();
1418 bChanged = true;
1424 if( pResultSet->getPropertyState( nHandleTrigger ) == STLPropertyState::Direct )
1426 Reference< XShape > xTriggerShape;
1427 pResultSet->getPropertyValue( nHandleTrigger ) >>= xTriggerShape;
1428 bChanged |= mpMainSequence->setTrigger( pEffect, xTriggerShape );
1432 const bool bHasTextGrouping = pResultSet->getPropertyState( nHandleTextGrouping ) == STLPropertyState::Direct;
1433 const bool bHasAnimateForm = pResultSet->getPropertyState( nHandleAnimateForm ) == STLPropertyState::Direct;
1434 const bool bHasTextGroupingAuto = pResultSet->getPropertyState( nHandleTextGroupingAuto ) == STLPropertyState::Direct;
1435 const bool bHasTextReverse = pResultSet->getPropertyState( nHandleTextReverse ) == STLPropertyState::Direct;
1437 if( bHasTextGrouping || bHasAnimateForm || bHasTextGroupingAuto || bHasTextReverse )
1439 // we need to do a second pass for text grouping options
1440 // since changing them can cause effects to be removed
1441 // or replaced, we do this after we applied all other options
1442 // above
1444 sal_Int32 nTextGrouping = 0;
1445 bool bAnimateForm = true, bTextReverse = false;
1446 double fTextGroupingAuto = -1.0;
1448 if( bHasTextGrouping )
1449 pResultSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping;
1450 else
1451 pOldSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping;
1453 if( bHasAnimateForm )
1454 pResultSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm;
1455 else
1456 pOldSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm;
1458 if( bHasTextGroupingAuto )
1459 pResultSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto;
1460 else
1461 pOldSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto;
1463 if( bHasTextReverse )
1464 pResultSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse;
1465 else
1466 pOldSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse;
1468 EffectSequence const aSelectedEffects( maListSelection );
1469 for( CustomAnimationEffectPtr const& pEffect : aSelectedEffects )
1471 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
1472 if( !pEffectSequence )
1473 pEffectSequence = mpMainSequence.get();
1475 sal_Int32 nGroupId = pEffect->getGroupId();
1476 CustomAnimationTextGroupPtr pTextGroup;
1477 if( nGroupId != -1 )
1479 // use existing group
1480 pTextGroup = pEffectSequence->findGroup( nGroupId );
1482 else
1484 // somethings changed so we need a group now
1485 pTextGroup = pEffectSequence->createTextGroup( pEffect, nTextGrouping, fTextGroupingAuto, bAnimateForm, bTextReverse );
1486 bChanged = true;
1489 //#i119988#
1490 /************************************************************************/
1492 Note, the setAnimateForm means set the animation from TextGroup to Object's Shape
1493 And on the UI in means "Animate attached shape" in "Effect Option" dialog
1494 The setTextGrouping means set animation to Object's Text,
1495 the nTextGrouping is Text Animation Type
1496 nTextGrouping = -1 is "As one Object", means no text animation.
1498 The previous call order first do the setTextGrouping and then do the setAnimateForm,
1499 that will cause such defect: in the setTextGrouping, the effect has been removed,
1500 but in setAnimateForm still need this effect, then a NULL pointer of that effect will
1501 be gotten, and cause crash.
1503 []bHasAnimateForm means the UI has changed, bAnimateForm is it value
1505 So if create a new textgroup animation, the following animation will never be run!
1506 Since the \A1\B0Animate attached shape\A1\B1 is default checked.
1507 And the bHasAnimateForm default is false, and if user uncheck it the value bAnimateForm will be false,
1508 it same as the TextGroup\A1\AFs default value, also could not be run setAnimateForm.
1509 if( bHasAnimateForm )
1511 if( pTextGroup->getAnimateForm() != bAnimateForm )
1513 pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
1514 bChanged = true;
1518 In setTextGrouping, there are three case:
1519 1. Create new text effects for empty TextGroup
1520 2. Remove all text effects of TextGroup (nTextGrouping == -1)
1521 3. Change all the text effects\A1\AF start type
1523 So here is the right logic:
1524 If set the animation from text to shape and remove text animation,
1525 should do setAnimateForm first, then do setTextGrouping.
1526 Other case,do setTextGrouping first, then do setAnimateForm.
1529 /************************************************************************/
1531 bool bDoSetAnimateFormFirst = false;
1532 bool bNeedDoSetAnimateForm = false;
1534 if( bHasAnimateForm )
1536 if( pTextGroup && pTextGroup->getAnimateForm() != bAnimateForm )
1538 if( (pTextGroup->getTextGrouping() >= 0) && (nTextGrouping == -1 ) )
1540 bDoSetAnimateFormFirst = true;
1542 bNeedDoSetAnimateForm = true;
1546 if (bDoSetAnimateFormFirst)
1548 pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
1549 bChanged = true;
1552 if( bHasTextGrouping )
1554 if( pTextGroup && pTextGroup->getTextGrouping() != nTextGrouping )
1556 pEffectSequence->setTextGrouping( pTextGroup, nTextGrouping );
1558 // All the effects of the outline object is removed so we need to
1559 // put it back. OTOH, the shape object that still has effects
1560 // in the text group is fine.
1561 if (nTextGrouping == -1 && pTextGroup->getEffects().empty())
1563 pEffect->setTarget(Any(pEffect->getTargetShape()));
1564 pEffect->setGroupId(-1);
1565 mpMainSequence->append(pEffect);
1568 bChanged = true;
1572 if (!bDoSetAnimateFormFirst && bNeedDoSetAnimateForm)
1574 if( pTextGroup )
1576 pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
1577 bChanged = true;
1581 if( bHasTextGroupingAuto )
1583 if( pTextGroup && pTextGroup->getTextGroupingAuto() != fTextGroupingAuto )
1585 pEffectSequence->setTextGroupingAuto( pTextGroup, fTextGroupingAuto );
1586 bChanged = true;
1590 if( bHasTextReverse )
1592 if( pTextGroup && pTextGroup->getTextReverse() != bTextReverse )
1594 pEffectSequence->setTextReverse( pTextGroup, bTextReverse );
1595 bChanged = true;
1601 if( bChanged )
1603 mpMainSequence->rebuild();
1604 updateControls();
1605 mrBase.GetDocShell()->SetModified();
1609 void CustomAnimationPane::showOptions(const OUString& rPage)
1611 std::unique_ptr<STLPropertySet> xSet = createSelectionSet();
1613 auto xDlg = std::make_shared<CustomAnimationDialog>(GetFrameWeld(), std::move(xSet), rPage);
1615 weld::DialogController::runAsync(xDlg, [xDlg, this](sal_Int32 nResult){
1616 if (nResult )
1618 addUndo();
1619 changeSelection(xDlg->getResultSet(), xDlg->getPropertySet());
1620 updateControls();
1625 void CustomAnimationPane::onChangeCurrentPage()
1627 if( !mxView.is() )
1628 return;
1632 Reference< XDrawPage > xNewPage( mxView->getCurrentPage() );
1633 if( xNewPage != mxCurrentPage )
1635 mxCurrentPage = xNewPage;
1636 SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
1637 if( pPage )
1639 mpMainSequence = pPage->getMainSequence();
1640 mxCustomAnimationList->update( mpMainSequence );
1642 updateControls();
1645 catch( Exception& )
1647 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onChangeCurrentPage()" );
1651 static bool getTextSelection( const Any& rSelection, Reference< XShape >& xShape, std::vector< sal_Int16 >& rParaList )
1653 Reference< XTextRange > xSelectedText;
1654 rSelection >>= xSelectedText;
1655 if( xSelectedText.is() ) try
1657 xShape.set( xSelectedText->getText(), UNO_QUERY_THROW );
1659 css::uno::Reference<css::document::XActionLockable> xLockable(xShape, css::uno::UNO_QUERY);
1660 if (xLockable.is())
1661 xLockable->addActionLock();
1662 comphelper::ScopeGuard aGuard([&xLockable]()
1664 if (xLockable.is())
1665 xLockable->removeActionLock();
1668 Reference< XTextRangeCompare > xTextRangeCompare( xShape, UNO_QUERY_THROW );
1669 Reference< XEnumerationAccess > xParaEnumAccess( xShape, UNO_QUERY_THROW );
1670 Reference< XEnumeration > xParaEnum( xParaEnumAccess->createEnumeration(), UNO_SET_THROW );
1671 Reference< XTextRange > xRange;
1672 Reference< XTextRange > xStart( xSelectedText->getStart() );
1673 Reference< XTextRange > xEnd( xSelectedText->getEnd() );
1675 if( xTextRangeCompare->compareRegionEnds( xStart, xEnd ) < 0 )
1677 Reference< XTextRange > xTemp( xStart );
1678 xStart = xEnd;
1679 xEnd = xTemp;
1682 sal_Int16 nPara = 0;
1683 while( xParaEnum->hasMoreElements() )
1685 xParaEnum->nextElement() >>= xRange;
1687 // break if start of selection is prior to end of current paragraph
1688 if( xRange.is() && (xTextRangeCompare->compareRegionEnds( xStart, xRange ) >= 0 ) )
1689 break;
1691 nPara++;
1694 while( xRange.is() )
1696 if( xRange.is() && !xRange->getString().isEmpty() )
1697 rParaList.push_back( nPara );
1699 // break if end of selection is before or at end of current paragraph
1700 if( xRange.is() && xTextRangeCompare->compareRegionEnds( xEnd, xRange ) >= 0 )
1701 break;
1703 nPara++;
1705 if( xParaEnum->hasMoreElements() )
1706 xParaEnum->nextElement() >>= xRange;
1707 else
1708 xRange.clear();
1711 return true;
1713 catch( Exception& )
1715 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::getTextSelection()" );
1718 return false;
1721 namespace
1723 Reference<XShape> getTargetShape(const Any& rTarget)
1725 Reference<XShape> xShape;
1726 rTarget >>= xShape;
1727 if( !xShape.is() )
1729 ParagraphTarget aParaTarget;
1730 if (rTarget >>= aParaTarget)
1731 xShape = aParaTarget.Shape;
1733 return xShape;
1737 void CustomAnimationPane::onAdd()
1739 bool bHasText = true;
1741 // first create vector of targets for dialog preview
1742 std::vector< Any > aTargets;
1744 // gather shapes from the selection
1745 Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW );
1746 maViewSelection = xSel->getSelection();
1748 if( maViewSelection.getValueType() == cppu::UnoType<XShapes>::get())
1750 Reference< XIndexAccess > xShapes;
1751 maViewSelection >>= xShapes;
1753 sal_Int32 nCount = xShapes->getCount();
1754 aTargets.reserve( nCount );
1755 for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ )
1757 Any aTarget( xShapes->getByIndex( nIndex ) );
1758 aTargets.push_back( aTarget );
1759 if( bHasText )
1761 Reference< XText > xText;
1762 aTarget >>= xText;
1763 if( !xText.is() || xText->getString().isEmpty() )
1764 bHasText = false;
1768 else if ( maViewSelection.getValueType() == cppu::UnoType<XShape>::get())
1770 aTargets.push_back( maViewSelection );
1771 Reference< XText > xText;
1772 maViewSelection >>= xText;
1773 if( !xText.is() || xText->getString().isEmpty() )
1774 bHasText = false;
1776 else if ( maViewSelection.getValueType() == cppu::UnoType<XTextCursor>::get())
1778 Reference< XShape > xShape;
1779 std::vector< sal_Int16 > aParaList;
1780 if( getTextSelection( maViewSelection, xShape, aParaList ) )
1782 ParagraphTarget aParaTarget;
1783 aParaTarget.Shape = xShape;
1785 for( const auto& rPara : aParaList )
1787 aParaTarget.Paragraph = rPara;
1788 aTargets.push_back( Any( aParaTarget ) );
1792 else
1794 OSL_FAIL("sd::CustomAnimationPane::onAdd(), unknown view selection!" );
1795 return;
1798 CustomAnimationPresetPtr pDescriptor;
1799 mxFTCategory->set_sensitive(true);
1800 mxFTAnimation->set_sensitive(true);
1802 bool bCategoryReset = false;
1804 if (!mxLBCategory->get_sensitive() || mxLBCategory->get_active() == -1)
1806 mxLBCategory->set_sensitive(true);
1807 mxLBCategory->set_active(0);
1808 bCategoryReset = true;
1811 if (bCategoryReset || !mxLBAnimation->get_sensitive() ||
1812 mxLBAnimation->get_selected_index() == -1)
1814 mxLBAnimation->set_sensitive(true);
1816 sal_Int32 nFirstEffect = fillAnimationLB(bHasText);
1817 if (nFirstEffect == -1)
1818 return;
1820 mxLBAnimation->select(nFirstEffect);
1821 mnLastSelectedAnimation = nFirstEffect;
1824 auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_selected_id());
1825 if (pEntryData)
1826 pDescriptor = *pEntryData;
1828 if( pDescriptor )
1830 const double fDuration = pDescriptor->getDuration();
1831 mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE);
1832 bool bHasSpeed = pDescriptor->getDuration() > 0.001;
1833 mxCBXDuration->set_sensitive( bHasSpeed );
1834 mxFTDuration->set_sensitive( bHasSpeed );
1836 mxCustomAnimationList->unselect_all();
1838 // gather shapes from the selection
1839 bool bFirst = true;
1840 for( const auto& rTarget : aTargets )
1842 css::uno::Reference<css::document::XActionLockable> xLockable(getTargetShape(rTarget), css::uno::UNO_QUERY);
1843 if (xLockable.is())
1844 xLockable->addActionLock();
1845 comphelper::ScopeGuard aGuard([&xLockable]()
1847 if (xLockable.is())
1848 xLockable->removeActionLock();
1851 CustomAnimationEffectPtr pCreated = mpMainSequence->append( pDescriptor, rTarget, fDuration );
1853 // if only one shape with text and no fill or outline is selected, animate only by first level paragraphs
1854 if( bHasText && (aTargets.size() == 1) )
1856 Reference< XShape > xShape( rTarget, UNO_QUERY );
1857 if( xShape.is() && !hasVisibleShape( xShape ) )
1859 mpMainSequence->createTextGroup( pCreated, 1, -1.0, false, false );
1863 if( bFirst )
1864 bFirst = false;
1865 else
1866 pCreated->setNodeType( EffectNodeType::WITH_PREVIOUS );
1868 if( pCreated )
1869 mxCustomAnimationList->select( pCreated );
1873 PathKind ePathKind = getCreatePathKind();
1875 if (ePathKind != PathKind::NONE)
1877 createPath( ePathKind, aTargets, 0.0 );
1878 updateMotionPathTags();
1881 addUndo();
1882 mrBase.GetDocShell()->SetModified();
1884 updateControls();
1886 SlideShow::Stop( mrBase );
1889 void CustomAnimationPane::onRemove()
1891 if( maListSelection.empty() )
1892 return;
1894 addUndo();
1896 MainSequenceRebuildGuard aGuard( mpMainSequence );
1898 EffectSequence aList( maListSelection );
1900 for( CustomAnimationEffectPtr& pEffect : aList )
1902 if( pEffect->getEffectSequence() )
1903 pEffect->getEffectSequence()->remove( pEffect );
1906 maListSelection.clear();
1907 mrBase.GetDocShell()->SetModified();
1910 void CustomAnimationPane::remove( CustomAnimationEffectPtr const & pEffect )
1912 if( pEffect->getEffectSequence() )
1914 addUndo();
1915 pEffect->getEffectSequence()->remove( pEffect );
1916 mrBase.GetDocShell()->SetModified();
1920 void CustomAnimationPane::onChangeStart()
1922 sal_Int16 nNodeType;
1923 switch( mxLBStart->get_active() )
1925 case 0: nNodeType = EffectNodeType::ON_CLICK; break;
1926 case 1: nNodeType = EffectNodeType::WITH_PREVIOUS; break;
1927 case 2: nNodeType = EffectNodeType::AFTER_PREVIOUS; break;
1928 default:
1929 return;
1932 onChangeStart( nNodeType );
1935 void CustomAnimationPane::onChangeStart( sal_Int16 nNodeType )
1937 addUndo();
1939 MainSequenceRebuildGuard aGuard( mpMainSequence );
1941 bool bNeedRebuild = false;
1943 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1945 if( pEffect->getNodeType() != nNodeType )
1947 pEffect->setNodeType( nNodeType );
1948 bNeedRebuild = true;
1952 if( bNeedRebuild )
1954 mpMainSequence->rebuild();
1955 updateControls();
1956 mrBase.GetDocShell()->SetModified();
1960 void CustomAnimationPane::onChangeSpeed()
1962 double fDuration = getDuration();
1964 if(fDuration < 0)
1965 return;
1966 else
1968 addUndo();
1970 MainSequenceRebuildGuard aGuard( mpMainSequence );
1972 // change selected effect
1973 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1975 pEffect->setDuration( fDuration );
1978 mpMainSequence->rebuild();
1979 updateControls();
1980 mrBase.GetDocShell()->SetModified();
1984 double CustomAnimationPane::getDuration() const
1986 double fDuration = 0;
1988 if (!mxCBXDuration->get_text().isEmpty())
1989 fDuration = mxCBXDuration->get_value(FieldUnit::NONE) / 100.0;
1991 return fDuration;
1994 PathKind CustomAnimationPane::getCreatePathKind() const
1996 PathKind eKind = PathKind::NONE;
1998 if (mxLBAnimation->count_selected_rows() == 1 &&
1999 mxLBCategory->get_active() == gnMotionPathPos)
2001 const sal_Int32 nPos = mxLBAnimation->get_selected_index();
2002 if( nPos == mnCurvePathPos )
2004 eKind = PathKind::CURVE;
2006 else if( nPos == mnPolygonPathPos )
2008 eKind = PathKind::POLYGON;
2010 else if( nPos == mnFreeformPathPos )
2012 eKind = PathKind::FREEFORM;
2016 return eKind;
2019 void CustomAnimationPane::createPath( PathKind eKind, std::vector< Any >& rTargets, double fDuration)
2021 sal_uInt16 nSID = 0;
2023 switch( eKind )
2025 case PathKind::CURVE: nSID = SID_DRAW_BEZIER_NOFILL; break;
2026 case PathKind::POLYGON: nSID = SID_DRAW_POLYGON_NOFILL; break;
2027 case PathKind::FREEFORM: nSID = SID_DRAW_FREELINE_NOFILL; break;
2028 default: break;
2031 if( !nSID )
2032 return;
2034 DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >(
2035 FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get());
2037 if( pViewShell )
2039 DrawView* pView = pViewShell->GetDrawView();
2040 if( pView )
2041 pView->UnmarkAllObj();
2043 std::vector< Any > aTargets( 1, Any( fDuration ) );
2044 aTargets.insert( aTargets.end(), rTargets.begin(), rTargets.end() );
2045 Sequence< Any > aTargetSequence( comphelper::containerToSequence( aTargets ) );
2046 const SfxUnoAnyItem aItem( SID_ADD_MOTION_PATH, Any( aTargetSequence ) );
2047 pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( nSID, SfxCallMode::ASYNCHRON, {&aItem} );
2052 /// this link is called when the property box is modified by the user
2053 IMPL_LINK_NOARG(CustomAnimationPane, implPropertyHdl, LinkParamNone*, void)
2055 if (!mxLBSubControl)
2056 return;
2058 addUndo();
2060 MainSequenceRebuildGuard aGuard( mpMainSequence );
2062 const Any aValue(mxLBSubControl->getValue());
2064 bool bNeedUpdate = false;
2066 // change selected effect
2067 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2069 if( setProperty1Value( mnPropertyType, pEffect, aValue ) )
2070 bNeedUpdate = true;
2073 if( bNeedUpdate )
2075 mpMainSequence->rebuild();
2076 updateControls();
2077 mrBase.GetDocShell()->SetModified();
2080 onPreview( false );
2083 IMPL_LINK_NOARG(CustomAnimationPane, DelayModifiedHdl, weld::MetricSpinButton&, void)
2085 addUndo();
2088 IMPL_LINK_NOARG(CustomAnimationPane, DelayLoseFocusHdl, weld::Widget&, void)
2090 double fBegin = mxMFStartDelay->get_value(FieldUnit::NONE);
2092 //sequence rebuild only when the control loses focus
2093 MainSequenceRebuildGuard aGuard( mpMainSequence );
2095 // change selected effect
2096 for( CustomAnimationEffectPtr& pEffect : maListSelection )
2098 pEffect->setBegin( fBegin/10.0 );
2101 mpMainSequence->rebuild();
2102 updateControls();
2103 mrBase.GetDocShell()->SetModified();
2106 IMPL_LINK_NOARG(CustomAnimationPane, AnimationSelectHdl, weld::TreeView&, void)
2108 maIdle.Start();
2111 IMPL_LINK_NOARG(CustomAnimationPane, SelectionHandler, Timer*, void)
2113 if (mxLBAnimation->has_grab()) // tdf#136474 try again later
2115 maIdle.Start();
2116 return;
2119 int nSelected = mxLBAnimation->get_selected_index();
2120 if (nSelected == -1)
2121 return;
2123 // tdf#99137, the selected entry may also be a subcategory title, so not an effect
2124 // just skip it and move to the next one in this case
2125 if (mxLBAnimation->get_text_emphasis(nSelected, 0))
2127 if (nSelected == 0 || nSelected > mnLastSelectedAnimation)
2128 mxLBAnimation->select(++nSelected);
2129 else
2130 mxLBAnimation->select(--nSelected);
2133 mnLastSelectedAnimation = nSelected;
2135 CustomAnimationPresetPtr* pPreset = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nSelected));
2136 PathKind ePathKind = getCreatePathKind();
2138 if ( ePathKind != PathKind::NONE )
2140 std::vector< Any > aTargets;
2141 MainSequenceRebuildGuard aGuard( mpMainSequence );
2143 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2145 aTargets.push_back( pEffect->getTarget() );
2147 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
2148 if( !pEffectSequence )
2149 pEffectSequence = mpMainSequence.get();
2151 // delete the old animation, new one will be appended
2152 // by createPath and SID_ADD_MOTION_PATH therein
2153 pEffectSequence->remove( pEffect );
2156 createPath( ePathKind, aTargets, 0.0 );
2157 updateMotionPathTags();
2158 return;
2161 CustomAnimationPresetPtr pDescriptor(*pPreset);
2162 const double fDuration = (*pPreset)->getDuration();
2163 MainSequenceRebuildGuard aGuard( mpMainSequence );
2165 // get selected effect
2166 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2168 // Dispose the deprecated motion path tag. It will be rebuilt later.
2169 if (pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH)
2171 for (auto const& xTag: maMotionPathTags)
2173 if(xTag->getEffect() == pEffect && !xTag->isDisposed())
2174 xTag->Dispose();
2178 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
2179 if( !pEffectSequence )
2180 pEffectSequence = mpMainSequence.get();
2182 pEffectSequence->replace( pEffect, pDescriptor, fDuration );
2185 addUndo();
2186 onPreview(false);
2189 IMPL_LINK_NOARG(CustomAnimationPane, UpdateAnimationLB, weld::ComboBox&, void)
2191 //FIXME: first effect only? what if there is more?
2192 CustomAnimationEffectPtr pEffect = maListSelection.front();
2193 fillAnimationLB( pEffect->hasText() );
2196 IMPL_LINK_NOARG(CustomAnimationPane, DurationModifiedHdl, weld::MetricSpinButton&, void)
2198 if (!mxCBXDuration->get_text().isEmpty())
2200 double duration_value = static_cast<double>(mxCBXDuration->get_value(FieldUnit::NONE));
2201 if(duration_value <= 0.0)
2203 mxCBXDuration->set_value(1, FieldUnit::NONE);
2205 onChangeSpeed();
2209 namespace
2211 void InsertCategory(weld::TreeView& rLBAnimation, const OUString& rMotionPathLabel)
2213 int nRow = rLBAnimation.n_children();
2214 rLBAnimation.append_text(rMotionPathLabel);
2215 rLBAnimation.set_text_emphasis(nRow, true, 0);
2216 rLBAnimation.set_text_align(nRow, 0.5, 0);
2220 sal_Int32 CustomAnimationPane::fillAnimationLB( bool bHasText )
2222 PresetCategoryList rCategoryList;
2223 sal_uInt16 nPosition = mxLBCategory->get_active();
2224 const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets());
2225 switch(nPosition)
2227 case 0:rCategoryList = rPresets.getEntrancePresets();break;
2228 case 1:rCategoryList = rPresets.getEmphasisPresets();break;
2229 case 2:rCategoryList = rPresets.getExitPresets();break;
2230 case 3:rCategoryList = rPresets.getMotionPathsPresets();break;
2231 case 4:rCategoryList = rPresets.getMiscPresets();break;
2234 sal_Int32 nFirstEffect = -1;
2236 int nOldEntryCount = mxLBAnimation->n_children();
2237 int nOldScrollPos = mxLBAnimation->vadjustment_get_value();
2239 mxLBAnimation->freeze();
2240 mxLBAnimation->clear();
2241 mnLastSelectedAnimation = -1;
2243 if (nPosition == gnMotionPathPos)
2245 OUString sMotionPathLabel( SdResId( STR_CUSTOMANIMATION_USERPATH ) );
2246 InsertCategory(*mxLBAnimation, sMotionPathLabel);
2247 mnCurvePathPos = mxLBAnimation->n_children();
2248 mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulCOMBLINE) );
2249 mxLBAnimation->set_text_emphasis(mnCurvePathPos, false, 0);
2250 mnPolygonPathPos = mnCurvePathPos + 1;
2251 mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulPOLY) );
2252 mxLBAnimation->set_text_emphasis(mnPolygonPathPos, false, 0);
2253 mnFreeformPathPos = mnPolygonPathPos + 1;
2254 mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulFREELINE) );
2255 mxLBAnimation->set_text_emphasis(mnFreeformPathPos, false, 0);
2258 for (const PresetCategoryPtr& pCategory : rCategoryList)
2260 if( pCategory )
2262 InsertCategory(*mxLBAnimation, pCategory->maLabel);
2264 int nPos = mxLBAnimation->n_children();
2266 std::vector< CustomAnimationPresetPtr > aSortedVector =
2267 pCategory->maEffects;
2269 for( const CustomAnimationPresetPtr& pDescriptor : aSortedVector )
2271 // ( !isTextOnly || ( isTextOnly && bHasText ) ) <=> !isTextOnly || bHasText
2272 if( pDescriptor && ( !pDescriptor->isTextOnly() || bHasText ) )
2274 auto pCustomPtr = new CustomAnimationPresetPtr(pDescriptor);
2275 OUString sId = weld::toId(pCustomPtr);
2276 mxLBAnimation->append(sId, pDescriptor->getLabel());
2277 mxLBAnimation->set_text_emphasis(nPos, false, 0);
2279 if (nFirstEffect == -1)
2280 nFirstEffect = nPos;
2282 ++nPos;
2288 mxLBAnimation->thaw();
2290 if (mxLBAnimation->n_children() == nOldEntryCount)
2291 mxLBAnimation->vadjustment_set_value(nOldScrollPos);
2293 return nFirstEffect;
2296 IMPL_LINK(CustomAnimationPane, implToggleHdl, weld::Toggleable&, rBtn, void)
2298 implControlHdl(&rBtn);
2301 IMPL_LINK(CustomAnimationPane, implClickHdl, weld::Button&, rBtn, void)
2303 implControlHdl(&rBtn);
2306 IMPL_LINK( CustomAnimationPane, implControlListBoxHdl, weld::ComboBox&, rListBox, void )
2308 implControlHdl(&rListBox);
2311 /// this link is called when one of the controls is modified
2312 void CustomAnimationPane::implControlHdl(const weld::Widget* pControl)
2314 if (pControl == mxPBAddEffect.get())
2315 onAdd();
2316 else if (pControl == mxPBRemoveEffect.get())
2317 onRemove();
2318 else if (pControl == mxLBStart.get())
2319 onChangeStart();
2320 else if (pControl == mxPBPropertyMore.get())
2321 showOptions();
2322 else if (pControl == mxPBMoveUp.get())
2323 moveSelection( true );
2324 else if (pControl == mxPBMoveDown.get())
2325 moveSelection( false );
2326 else if (pControl == mxPBPlay.get())
2327 onPreview( true );
2328 else if (pControl == mxCBAutoPreview.get())
2330 SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress);
2331 pOptions->SetPreviewChangedEffects(mxCBAutoPreview->get_active());
2335 IMPL_LINK_NOARG(CustomAnimationPane, lateInitCallback, Timer *, void)
2337 // Call getPresets() to initiate the (expensive) construction of the
2338 // presets list.
2339 CustomAnimationPresets::getCustomAnimationPresets();
2341 // update selection and control states
2342 onSelectionChanged();
2345 void CustomAnimationPane::moveSelection( bool bUp )
2347 if( maListSelection.empty() )
2348 return;
2350 EffectSequenceHelper* pSequence = maListSelection.front()->getEffectSequence();
2351 if( pSequence == nullptr )
2352 return;
2354 addUndo();
2356 bool bChanged = false;
2358 MainSequenceRebuildGuard aGuard( mpMainSequence );
2359 EffectSequence& rEffectSequence = pSequence->getSequence();
2361 if( bUp )
2363 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2365 EffectSequence::iterator aUpEffectPos( pSequence->find( pEffect ) );
2366 // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists
2367 if( aUpEffectPos != rEffectSequence.end() )
2369 EffectSequence::iterator aInsertPos( rEffectSequence.erase( aUpEffectPos ) );
2371 if( aInsertPos != rEffectSequence.begin() )
2373 --aInsertPos;
2374 while( (aInsertPos != rEffectSequence.begin()) && !mxCustomAnimationList->isExpanded(*aInsertPos))
2375 --aInsertPos;
2376 rEffectSequence.insert( aInsertPos, pEffect );
2378 else
2380 rEffectSequence.push_front( pEffect );
2382 bChanged = true;
2386 else
2388 EffectSequence::reverse_iterator aIter( maListSelection.rbegin() );
2389 const EffectSequence::reverse_iterator aEnd( maListSelection.rend() );
2391 while( aIter != aEnd )
2393 CustomAnimationEffectPtr pEffect = *aIter++;
2395 EffectSequence::iterator aDownEffectPos( pSequence->find( pEffect ) );
2396 // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists
2397 if( aDownEffectPos != rEffectSequence.end() )
2399 EffectSequence::iterator aInsertPos( rEffectSequence.erase( aDownEffectPos ) );
2401 if( aInsertPos != rEffectSequence.end() )
2403 ++aInsertPos;
2404 // Advance over rolled-up (un-expanded) items, unless we just moved it there.
2405 while( (aInsertPos != rEffectSequence.end())
2406 && !mxCustomAnimationList->isExpanded(*aInsertPos)
2407 && (std::find(maListSelection.begin(), maListSelection.end(), *aInsertPos)
2408 == maListSelection.end())
2410 ++aInsertPos;
2411 rEffectSequence.insert( aInsertPos, pEffect );
2413 else
2415 rEffectSequence.push_back( pEffect );
2417 bChanged = true;
2422 if( bChanged )
2424 mpMainSequence->rebuild();
2425 updateControls();
2426 mrBase.GetDocShell()->SetModified();
2430 void CustomAnimationPane::onPreview( bool bForcePreview )
2432 if (!bForcePreview && !mxCBAutoPreview->get_active())
2433 return;
2435 // No preview in LOK.
2436 if (comphelper::LibreOfficeKit::isActive())
2437 return;
2439 if( maListSelection.empty() )
2441 rtl::Reference< MotionPathTag > xMotionPathTag;
2442 auto aIter = std::find_if(maMotionPathTags.begin(), maMotionPathTags.end(),
2443 [](const MotionPathTagVector::value_type& rxMotionPathTag) { return rxMotionPathTag->isSelected(); });
2444 if (aIter != maMotionPathTags.end())
2445 xMotionPathTag = *aIter;
2447 if( xMotionPathTag.is() )
2449 MainSequencePtr pSequence = std::make_shared<MainSequence>();
2450 pSequence->append( xMotionPathTag->getEffect()->clone() );
2451 preview( pSequence->getRootNode() );
2453 else
2455 Reference< XAnimationNodeSupplier > xNodeSupplier( mxCurrentPage, UNO_QUERY );
2456 if( !xNodeSupplier.is() )
2457 return;
2459 preview( xNodeSupplier->getAnimationNode() );
2462 else
2464 MainSequencePtr pSequence = std::make_shared<MainSequence>();
2466 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2468 pSequence->append( pEffect->clone() );
2471 preview( pSequence->getRootNode() );
2475 void CustomAnimationPane::preview( const Reference< XAnimationNode >& xAnimationNode )
2477 Reference< XParallelTimeContainer > xRoot = ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() );
2478 Sequence< css::beans::NamedValue > aUserData
2479 { { "node-type", css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } };
2480 xRoot->setUserData( aUserData );
2481 xRoot->appendChild( xAnimationNode );
2483 SlideShow::StartPreview( mrBase, mxCurrentPage, xRoot );
2486 // ICustomAnimationListController
2487 void CustomAnimationPane::onSelect()
2489 maListSelection = mxCustomAnimationList->getSelection();
2490 updateControls();
2492 // mark shapes from selected effects
2493 if( maSelectionLock.isLocked() )
2494 return;
2496 // tdf#145030 if nothing is selected in the effects list, leave the selection of
2497 // objects in the slide untouched
2498 if (maListSelection.empty())
2499 return;
2501 ScopeLockGuard aGuard( maSelectionLock );
2502 DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >(
2503 FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get());
2504 DrawView* pView = pViewShell ? pViewShell->GetDrawView() : nullptr;
2506 if( pView )
2508 pView->UnmarkAllObj();
2509 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2511 Reference< XShape > xShape( pEffect->getTargetShape() );
2512 SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
2513 if( pObj )
2514 pView->MarkObj(pObj, pView->GetSdrPageView());
2519 // ICustomAnimationListController
2520 // pEffectInsertBefore may be null if moving to end of list.
2521 void CustomAnimationPane::onDragNDropComplete(std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore)
2523 if ( !mpMainSequence )
2524 return;
2526 addUndo();
2528 MainSequenceRebuildGuard aGuard( mpMainSequence );
2530 // Move all selected effects
2531 for( auto const& pEffectDragged : pEffectsDragged )
2533 // Move this dragged effect and any hidden sub-effects
2534 EffectSequence::iterator aIter = mpMainSequence->find( pEffectDragged );
2535 const EffectSequence::iterator aEnd( mpMainSequence->getEnd() );
2537 while( aIter != aEnd )
2539 CustomAnimationEffectPtr pEffect = *aIter++;
2541 // Update model with new location (function triggers a rebuild)
2542 // target may be null, which will insert at the end.
2543 mpMainSequence->moveToBeforeEffect( pEffect, pEffectInsertBefore );
2544 // Done moving effect and its hidden sub-effects when *next* effect is visible.
2545 if (aIter != aEnd && mxCustomAnimationList->isVisible(*aIter))
2546 break;
2550 updateControls();
2551 mrBase.GetDocShell()->SetModified();
2554 void CustomAnimationPane::updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag )
2556 MainSequenceRebuildGuard aGuard( mpMainSequence );
2557 if( !xTag.is() )
2558 return;
2560 SdrPathObj* pPathObj = xTag->getPathObj();
2561 CustomAnimationEffectPtr pEffect = xTag->getEffect();
2562 if( (pPathObj != nullptr) && pEffect )
2564 SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager();
2565 if( pManager )
2567 SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
2568 if( pPage )
2569 pManager->AddUndoAction( std::make_unique<UndoAnimationPath>( mrBase.GetDocShell()->GetDoc(), pPage, pEffect->getNode() ) );
2572 pEffect->updatePathFromSdrPathObj( *pPathObj );
2578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */