cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / animations / CustomAnimationPane.cxx
blobaa4161697f8edf464a5a08678b8a2271c9063eb6
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, u"CustomAnimationsPanel"_ustr, u"modules/simpress/ui/customanimationspanel.ui"_ustr)
122 , mrBase(rBase)
123 // load resources
124 , mxFTAnimation(m_xBuilder->weld_label(u"effectlabel"_ustr))
125 , mxCustomAnimationList(new CustomAnimationList(m_xBuilder->weld_tree_view(u"custom_animation_list"_ustr),
126 m_xBuilder->weld_label(u"custom_animation_label"_ustr),
127 m_xBuilder->weld_widget(u"custom_animation_label_parent"_ustr)))
128 , mxPBAddEffect(m_xBuilder->weld_button(u"add_effect"_ustr))
129 , mxPBRemoveEffect(m_xBuilder->weld_button(u"remove_effect"_ustr))
130 , mxPBMoveUp(m_xBuilder->weld_button(u"move_up"_ustr))
131 , mxPBMoveDown(m_xBuilder->weld_button(u"move_down"_ustr))
132 , mxFTCategory(m_xBuilder->weld_label(u"categorylabel"_ustr))
133 , mxLBCategory(m_xBuilder->weld_combo_box(u"categorylb"_ustr))
134 , mxFTEffect(m_xBuilder->weld_label(u"effect_label"_ustr))
135 , mxLBAnimation(m_xBuilder->weld_tree_view(u"effect_list"_ustr))
136 , mxFTStart(m_xBuilder->weld_label(u"start_effect"_ustr))
137 , mxLBStart(m_xBuilder->weld_combo_box(u"start_effect_list"_ustr))
138 , mxFTProperty(m_xBuilder->weld_label(u"effect_property"_ustr))
139 , mxPlaceholderBox(m_xBuilder->weld_container(u"placeholder"_ustr))
140 , mxPBPropertyMore(m_xBuilder->weld_button(u"more_properties"_ustr))
141 , mxFTDuration(m_xBuilder->weld_label(u"effect_duration"_ustr))
142 , mxCBXDuration(m_xBuilder->weld_metric_spin_button(u"anim_duration"_ustr, FieldUnit::SECOND))
143 , mxFTStartDelay(m_xBuilder->weld_label(u"delay_label"_ustr))
144 , mxMFStartDelay(m_xBuilder->weld_metric_spin_button(u"delay_value"_ustr, FieldUnit::SECOND))
145 , mxCBAutoPreview(m_xBuilder->weld_check_button(u"auto_preview"_ustr))
146 , mxPBPlay(m_xBuilder->weld_button(u"play"_ustr))
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_selection_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 = mrBase.GetDrawController();
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 (auto pMainViewShell = mrBase.GetMainViewShell().get())
294 if( pMainViewShell->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 = SdModule::get()->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 = std::move(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 maViewSelection = mxView->getSelection();
839 mxCustomAnimationList->onSelectionChanged( maViewSelection );
840 updateControls();
842 catch( Exception& )
844 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onSelectionChanged()" );
848 void CustomAnimationPane::onDoubleClick()
850 showOptions();
853 void CustomAnimationPane::onContextMenu(const OUString &rIdent)
855 if (rIdent == "onclick")
856 onChangeStart( EffectNodeType::ON_CLICK );
857 else if (rIdent == "withprev")
858 onChangeStart( EffectNodeType::WITH_PREVIOUS );
859 else if (rIdent == "afterprev")
860 onChangeStart( EffectNodeType::AFTER_PREVIOUS );
861 else if (rIdent == "options")
862 showOptions();
863 else if (rIdent == "timing")
864 showOptions(u"timing"_ustr);
865 else if (rIdent == "remove")
866 onRemove();
867 else if (rIdent == "create" && maViewSelection.hasValue())
868 onAdd();
869 updateControls();
872 static void addValue( const std::unique_ptr<STLPropertySet>& pSet, sal_Int32 nHandle, const Any& rValue )
874 switch( pSet->getPropertyState( nHandle ) )
876 case STLPropertyState::Ambiguous:
877 // value is already ambiguous, do nothing
878 break;
879 case STLPropertyState::Direct:
880 // set to ambiguous if existing value is different
881 if( rValue != pSet->getPropertyValue( nHandle ) )
882 pSet->setPropertyState( nHandle, STLPropertyState::Ambiguous );
883 break;
884 case STLPropertyState::Default:
885 // just set new value
886 pSet->setPropertyValue( nHandle, rValue );
887 break;
891 static sal_Int32 calcMaxParaDepth( const Reference< XShape >& xTargetShape )
893 sal_Int32 nMaxParaDepth = -1;
895 if( xTargetShape.is() )
897 Reference< XEnumerationAccess > xText( xTargetShape, UNO_QUERY );
898 if( xText.is() )
900 Reference< XPropertySet > xParaSet;
902 Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW );
903 while( xEnumeration->hasMoreElements() )
905 xEnumeration->nextElement() >>= xParaSet;
906 if( xParaSet.is() )
908 sal_Int32 nParaDepth = 0;
909 xParaSet->getPropertyValue( u"NumberingLevel"_ustr ) >>= nParaDepth;
911 if( nParaDepth > nMaxParaDepth )
912 nMaxParaDepth = nParaDepth;
918 return nMaxParaDepth + 1;
921 Any CustomAnimationPane::getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect )
923 switch( nType )
925 case nPropertyTypeDirection:
926 case nPropertyTypeSpokes:
927 case nPropertyTypeZoom:
928 return Any( pEffect->getPresetSubType() );
930 case nPropertyTypeColor:
931 case nPropertyTypeFillColor:
932 case nPropertyTypeFirstColor:
933 case nPropertyTypeSecondColor:
934 case nPropertyTypeCharColor:
935 case nPropertyTypeLineColor:
937 const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1;
938 return pEffect->getColor( nIndex );
941 case nPropertyTypeFont:
942 return pEffect->getProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To );
944 case nPropertyTypeCharHeight:
946 static constexpr OUString aAttributeName( u"CharHeight"_ustr );
947 Any aValue( pEffect->getProperty( AnimationNodeType::SET, aAttributeName, EValue::To ) );
948 if( !aValue.hasValue() )
949 aValue = pEffect->getProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To );
950 return aValue;
953 case nPropertyTypeRotate:
954 return pEffect->getTransformationProperty( AnimationTransformType::ROTATE, EValue::By);
956 case nPropertyTypeTransparency:
957 return pEffect->getProperty( AnimationNodeType::SET, u"Opacity" , EValue::To );
959 case nPropertyTypeScale:
960 return pEffect->getTransformationProperty( AnimationTransformType::SCALE, EValue::By );
962 case nPropertyTypeCharDecoration:
964 Sequence< Any > aValues{
965 pEffect->getProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To ),
966 pEffect->getProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To ),
967 pEffect->getProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To )
969 return Any( aValues );
973 Any aAny;
974 return aAny;
977 bool CustomAnimationPane::setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const Any& rValue )
979 bool bEffectChanged = false;
980 switch( nType )
982 case nPropertyTypeDirection:
983 case nPropertyTypeSpokes:
984 case nPropertyTypeZoom:
986 OUString aPresetSubType;
987 rValue >>= aPresetSubType;
988 if( aPresetSubType != pEffect->getPresetSubType() )
990 CustomAnimationPresets::getCustomAnimationPresets().changePresetSubType( pEffect, aPresetSubType );
991 bEffectChanged = true;
994 break;
996 case nPropertyTypeFillColor:
997 case nPropertyTypeColor:
998 case nPropertyTypeFirstColor:
999 case nPropertyTypeSecondColor:
1000 case nPropertyTypeCharColor:
1001 case nPropertyTypeLineColor:
1003 const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1;
1004 Any aOldColor( pEffect->getColor( nIndex ) );
1005 if( aOldColor != rValue )
1007 pEffect->setColor( nIndex, rValue );
1008 bEffectChanged = true;
1011 break;
1013 case nPropertyTypeFont:
1014 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To, rValue );
1015 break;
1017 case nPropertyTypeCharHeight:
1019 static constexpr OUString aAttributeName( u"CharHeight"_ustr );
1020 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, aAttributeName, EValue::To, rValue );
1021 if( !bEffectChanged )
1022 bEffectChanged = pEffect->setProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To, rValue );
1024 break;
1025 case nPropertyTypeRotate:
1026 bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::ROTATE, EValue::By , rValue );
1027 break;
1029 case nPropertyTypeTransparency:
1030 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"Opacity" , EValue::To, rValue );
1031 break;
1033 case nPropertyTypeScale:
1034 bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::SCALE, EValue::By, rValue );
1035 break;
1037 case nPropertyTypeCharDecoration:
1039 Sequence< Any > aValues(3);
1040 rValue >>= aValues;
1041 bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To, aValues[0] );
1042 bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To, aValues[1] );
1043 bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To, aValues[2] );
1045 break;
1049 return bEffectChanged;
1052 static bool hasVisibleShape( const Reference< XShape >& xShape )
1056 const OUString sShapeType( xShape->getShapeType() );
1058 if( sShapeType == "com.sun.star.presentation.TitleTextShape" || sShapeType == "com.sun.star.presentation.OutlinerShape" ||
1059 sShapeType == "com.sun.star.presentation.SubtitleShape" || sShapeType == "com.sun.star.drawing.TextShape" )
1061 Reference< XPropertySet > xSet( xShape, UNO_QUERY_THROW );
1063 FillStyle eFillStyle;
1064 xSet->getPropertyValue( u"FillStyle"_ustr ) >>= eFillStyle;
1066 css::drawing::LineStyle eLineStyle;
1067 xSet->getPropertyValue( u"LineStyle"_ustr ) >>= eLineStyle;
1069 return eFillStyle != FillStyle_NONE || eLineStyle != css::drawing::LineStyle_NONE;
1072 catch( Exception& )
1075 return true;
1078 std::unique_ptr<STLPropertySet> CustomAnimationPane::createSelectionSet()
1080 std::unique_ptr<STLPropertySet> pSet = CustomAnimationDialog::createDefaultSet();
1082 pSet->setPropertyValue( nHandleCurrentPage, Any( mxCurrentPage ) );
1084 sal_Int32 nMaxParaDepth = 0;
1086 // get options from selected effects
1087 const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets());
1088 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1090 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
1091 if( !pEffectSequence )
1092 pEffectSequence = mpMainSequence.get();
1094 if( pEffect->hasText() )
1096 sal_Int32 n = calcMaxParaDepth(pEffect->getTargetShape());
1097 if( n > nMaxParaDepth )
1098 nMaxParaDepth = n;
1101 addValue( pSet, nHandleHasAfterEffect, Any( pEffect->hasAfterEffect() ) );
1102 addValue( pSet, nHandleAfterEffectOnNextEffect, Any( pEffect->IsAfterEffectOnNext() ) );
1103 addValue( pSet, nHandleDimColor, pEffect->getDimColor() );
1104 addValue( pSet, nHandleIterateType, Any( pEffect->getIterateType() ) );
1106 // convert absolute time to percentage value
1107 // This calculation is done in float to avoid some rounding artifacts.
1108 float fIterateInterval = static_cast<float>(pEffect->getIterateInterval());
1109 if( pEffect->getDuration() )
1110 fIterateInterval = static_cast<float>(fIterateInterval / pEffect->getDuration() );
1111 fIterateInterval *= 100.0;
1112 addValue( pSet, nHandleIterateInterval, Any( static_cast<double>(fIterateInterval) ) );
1114 addValue( pSet, nHandleBegin, Any( pEffect->getBegin() ) );
1115 addValue( pSet, nHandleDuration, Any( pEffect->getDuration() ) );
1116 addValue( pSet, nHandleStart, Any( pEffect->getNodeType() ) );
1117 addValue( pSet, nHandleRepeat, pEffect->getRepeatCount() );
1118 addValue( pSet, nHandleEnd, pEffect->getEnd() );
1119 addValue( pSet, nHandleRewind, Any( pEffect->getFill() ) );
1121 addValue( pSet, nHandlePresetId, Any( pEffect->getPresetId() ) );
1123 addValue( pSet, nHandleHasText, Any( pEffect->hasText() ) );
1125 addValue( pSet, nHandleHasVisibleShape, Any( hasVisibleShape( pEffect->getTargetShape() ) ) );
1127 Any aSoundSource;
1128 if( pEffect->getAudio().is() )
1130 aSoundSource = pEffect->getAudio()->getSource();
1131 addValue( pSet, nHandleSoundVolume, Any( pEffect->getAudio()->getVolume() ) );
1132 // todo addValue( pSet, nHandleSoundEndAfterSlide, makeAny( pEffect->getAudio()->getEndAfterSlide() ) );
1133 // this is now stored at the XCommand parameter sequence
1135 else if( pEffect->getCommand() == EffectCommands::STOPAUDIO )
1137 aSoundSource <<= true;
1139 addValue( pSet, nHandleSoundURL, aSoundSource );
1141 sal_Int32 nGroupId = pEffect->getGroupId();
1142 CustomAnimationTextGroupPtr pTextGroup;
1143 if( nGroupId != -1 )
1144 pTextGroup = pEffectSequence->findGroup( nGroupId );
1146 addValue( pSet, nHandleTextGrouping, Any( pTextGroup ? pTextGroup->getTextGrouping() : sal_Int32(-1) ) );
1147 addValue( pSet, nHandleAnimateForm, Any( !pTextGroup || pTextGroup->getAnimateForm() ) );
1148 addValue( pSet, nHandleTextGroupingAuto, Any( pTextGroup ? pTextGroup->getTextGroupingAuto() : -1.0 ) );
1149 addValue( pSet, nHandleTextReverse, Any( pTextGroup && pTextGroup->getTextReverse() ) );
1151 if( pEffectSequence->getSequenceType() == EffectNodeType::INTERACTIVE_SEQUENCE )
1153 InteractiveSequence* pIS = static_cast< InteractiveSequence* >( pEffectSequence );
1154 addValue( pSet, nHandleTrigger, Any( pIS->getTriggerShape() ) );
1157 CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( pEffect->getPresetId() );
1158 if( pDescriptor )
1160 sal_Int32 nType = nPropertyTypeNone;
1162 std::vector<OUString> aProperties( pDescriptor->getProperties() );
1163 if( !aProperties.empty() )
1164 nType = getPropertyType( aProperties.front() );
1166 if( nType != nPropertyTypeNone )
1168 addValue( pSet, nHandleProperty1Type, Any( nType ) );
1169 addValue( pSet, nHandleProperty1Value, getProperty1Value( nType, pEffect ) );
1172 if( pDescriptor->hasProperty( u"Accelerate" ) )
1174 addValue( pSet, nHandleAccelerate, Any( pEffect->getAcceleration() ) );
1177 if( pDescriptor->hasProperty( u"Decelerate" ) )
1179 addValue( pSet, nHandleDecelerate, Any( pEffect->getDecelerate() ) );
1182 if( pDescriptor->hasProperty( u"AutoReverse" ) )
1184 addValue( pSet, nHandleAutoReverse, Any( pEffect->getAutoReverse() ) );
1189 addValue( pSet, nHandleMaxParaDepth, Any( nMaxParaDepth ) );
1191 return pSet;
1194 void CustomAnimationPane::changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet )
1196 // change selected effect
1197 bool bChanged = false;
1199 MainSequenceRebuildGuard aGuard( mpMainSequence );
1201 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1203 DBG_ASSERT( pEffect->getEffectSequence(), "sd::CustomAnimationPane::changeSelection(), dead effect in selection!" );
1204 if( !pEffect->getEffectSequence() )
1205 continue;
1207 double fDuration = 0.0; // we might need this for iterate-interval
1208 if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct )
1210 pResultSet->getPropertyValue( nHandleDuration ) >>= fDuration;
1212 else
1214 fDuration = pEffect->getDuration();
1217 if( pResultSet->getPropertyState( nHandleIterateType ) == STLPropertyState::Direct )
1219 sal_Int16 nIterateType = 0;
1220 pResultSet->getPropertyValue( nHandleIterateType ) >>= nIterateType;
1221 if( pEffect->getIterateType() != nIterateType )
1223 pEffect->setIterateType( nIterateType );
1224 bChanged = true;
1228 if( pEffect->getIterateType() )
1230 if( pResultSet->getPropertyState( nHandleIterateInterval ) == STLPropertyState::Direct )
1232 double fIterateInterval = 0.0;
1233 pResultSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval;
1234 if( pEffect->getIterateInterval() != fIterateInterval )
1236 const double f = fIterateInterval * pEffect->getDuration() / 100;
1237 pEffect->setIterateInterval( f );
1238 bChanged = true;
1243 double fBegin = 0.0;
1245 if( pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct )
1246 pResultSet->getPropertyValue( nHandleBegin ) >>= fBegin;
1247 else
1248 fBegin = pEffect->getBegin();
1250 if( pEffect->getBegin() != fBegin && pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct)
1252 pEffect->setBegin( fBegin );
1253 bChanged = true;
1256 if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct )
1258 if( pEffect->getDuration() != fDuration )
1260 pEffect->setDuration( fDuration );
1261 bChanged = true;
1265 if( pResultSet->getPropertyState( nHandleStart ) == STLPropertyState::Direct )
1267 sal_Int16 nNodeType = 0;
1268 pResultSet->getPropertyValue( nHandleStart ) >>= nNodeType;
1269 if( pEffect->getNodeType() != nNodeType )
1271 pEffect->setNodeType( nNodeType );
1272 bChanged = true;
1276 if( pResultSet->getPropertyState( nHandleRepeat ) == STLPropertyState::Direct )
1278 Any aRepeatCount( pResultSet->getPropertyValue( nHandleRepeat ) );
1279 if( aRepeatCount != pEffect->getRepeatCount() )
1281 pEffect->setRepeatCount( aRepeatCount );
1282 bChanged = true;
1286 if( pResultSet->getPropertyState( nHandleEnd ) == STLPropertyState::Direct )
1288 Any aEndValue( pResultSet->getPropertyValue( nHandleEnd ) );
1289 if( pEffect->getEnd() != aEndValue )
1291 pEffect->setEnd( aEndValue );
1292 bChanged = true;
1296 if( pResultSet->getPropertyState( nHandleRewind ) == STLPropertyState::Direct )
1298 sal_Int16 nFill = 0;
1299 pResultSet->getPropertyValue( nHandleRewind ) >>= nFill;
1300 if( pEffect->getFill() != nFill )
1302 pEffect->setFill( nFill );
1303 bChanged = true;
1307 if( pResultSet->getPropertyState( nHandleHasAfterEffect ) == STLPropertyState::Direct )
1309 bool bHasAfterEffect = false;
1310 if( pResultSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect )
1312 if( pEffect->hasAfterEffect() != bHasAfterEffect )
1314 pEffect->setHasAfterEffect( bHasAfterEffect );
1315 bChanged = true;
1320 if( pResultSet->getPropertyState( nHandleAfterEffectOnNextEffect ) == STLPropertyState::Direct )
1322 bool bAfterEffectOnNextEffect = false;
1323 if( (pResultSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextEffect)
1324 && (pEffect->IsAfterEffectOnNext() != bAfterEffectOnNextEffect) )
1326 pEffect->setAfterEffectOnNext( bAfterEffectOnNextEffect );
1327 bChanged = true;
1331 if( pResultSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Direct )
1333 Any aDimColor( pResultSet->getPropertyValue( nHandleDimColor ) );
1334 if( pEffect->getDimColor() != aDimColor )
1336 pEffect->setDimColor( aDimColor );
1337 bChanged = true;
1341 if( pResultSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct )
1343 double fAccelerate = 0.0;
1344 pResultSet->getPropertyValue( nHandleAccelerate ) >>= fAccelerate;
1345 if( pEffect->getAcceleration() != fAccelerate )
1347 pEffect->setAcceleration( fAccelerate );
1348 bChanged = true;
1352 if( pResultSet->getPropertyState( nHandleDecelerate ) == STLPropertyState::Direct )
1354 double fDecelerate = 0.0;
1355 pResultSet->getPropertyValue( nHandleDecelerate ) >>= fDecelerate;
1356 if( pEffect->getDecelerate() != fDecelerate )
1358 pEffect->setDecelerate( fDecelerate );
1359 bChanged = true;
1363 if( pResultSet->getPropertyState( nHandleAutoReverse ) == STLPropertyState::Direct )
1365 bool bAutoReverse = false;
1366 pResultSet->getPropertyValue( nHandleAutoReverse ) >>= bAutoReverse;
1367 if( pEffect->getAutoReverse() != bAutoReverse )
1369 pEffect->setAutoReverse( bAutoReverse );
1370 bChanged = true;
1374 if( pResultSet->getPropertyState( nHandleProperty1Value ) == STLPropertyState::Direct )
1376 sal_Int32 nType = 0;
1377 pOldSet->getPropertyValue( nHandleProperty1Type ) >>= nType;
1379 bChanged |= setProperty1Value( nType, pEffect, pResultSet->getPropertyValue( nHandleProperty1Value ) );
1382 if( pResultSet->getPropertyState( nHandleSoundURL ) == STLPropertyState::Direct )
1384 const Any aSoundSource( pResultSet->getPropertyValue( nHandleSoundURL ) );
1386 if( aSoundSource.getValueType() == ::cppu::UnoType<sal_Bool>::get() )
1388 pEffect->setStopAudio();
1389 bChanged = true;
1391 else
1393 OUString aSoundURL;
1394 aSoundSource >>= aSoundURL;
1396 if( !aSoundURL.isEmpty() )
1398 if( !pEffect->getAudio().is() )
1400 pEffect->createAudio( aSoundSource );
1401 bChanged = true;
1403 else
1405 if( pEffect->getAudio()->getSource() != aSoundSource )
1407 pEffect->getAudio()->setSource( aSoundSource );
1408 bChanged = true;
1412 else
1414 if( pEffect->getAudio().is() || pEffect->getStopAudio() )
1416 pEffect->removeAudio();
1417 bChanged = true;
1423 if( pResultSet->getPropertyState( nHandleTrigger ) == STLPropertyState::Direct )
1425 Reference< XShape > xTriggerShape;
1426 pResultSet->getPropertyValue( nHandleTrigger ) >>= xTriggerShape;
1427 bChanged |= mpMainSequence->setTrigger( pEffect, xTriggerShape );
1431 const bool bHasTextGrouping = pResultSet->getPropertyState( nHandleTextGrouping ) == STLPropertyState::Direct;
1432 const bool bHasAnimateForm = pResultSet->getPropertyState( nHandleAnimateForm ) == STLPropertyState::Direct;
1433 const bool bHasTextGroupingAuto = pResultSet->getPropertyState( nHandleTextGroupingAuto ) == STLPropertyState::Direct;
1434 const bool bHasTextReverse = pResultSet->getPropertyState( nHandleTextReverse ) == STLPropertyState::Direct;
1436 if( bHasTextGrouping || bHasAnimateForm || bHasTextGroupingAuto || bHasTextReverse )
1438 // we need to do a second pass for text grouping options
1439 // since changing them can cause effects to be removed
1440 // or replaced, we do this after we applied all other options
1441 // above
1443 sal_Int32 nTextGrouping = 0;
1444 bool bAnimateForm = true, bTextReverse = false;
1445 double fTextGroupingAuto = -1.0;
1447 if( bHasTextGrouping )
1448 pResultSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping;
1449 else
1450 pOldSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping;
1452 if( bHasAnimateForm )
1453 pResultSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm;
1454 else
1455 pOldSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm;
1457 if( bHasTextGroupingAuto )
1458 pResultSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto;
1459 else
1460 pOldSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto;
1462 if( bHasTextReverse )
1463 pResultSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse;
1464 else
1465 pOldSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse;
1467 EffectSequence const aSelectedEffects( maListSelection );
1468 for( CustomAnimationEffectPtr const& pEffect : aSelectedEffects )
1470 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
1471 if( !pEffectSequence )
1472 pEffectSequence = mpMainSequence.get();
1474 sal_Int32 nGroupId = pEffect->getGroupId();
1475 CustomAnimationTextGroupPtr pTextGroup;
1476 if( nGroupId != -1 )
1478 // use existing group
1479 pTextGroup = pEffectSequence->findGroup( nGroupId );
1481 else
1483 // somethings changed so we need a group now
1484 pTextGroup = pEffectSequence->createTextGroup( pEffect, nTextGrouping, fTextGroupingAuto, bAnimateForm, bTextReverse );
1485 bChanged = true;
1488 //#i119988#
1489 /************************************************************************/
1491 Note, the setAnimateForm means set the animation from TextGroup to Object's Shape
1492 And on the UI in means "Animate attached shape" in "Effect Option" dialog
1493 The setTextGrouping means set animation to Object's Text,
1494 the nTextGrouping is Text Animation Type
1495 nTextGrouping = -1 is "As one Object", means no text animation.
1497 The previous call order first do the setTextGrouping and then do the setAnimateForm,
1498 that will cause such defect: in the setTextGrouping, the effect has been removed,
1499 but in setAnimateForm still need this effect, then a NULL pointer of that effect will
1500 be gotten, and cause crash.
1502 []bHasAnimateForm means the UI has changed, bAnimateForm is it value
1504 So if create a new textgroup animation, the following animation will never be run!
1505 Since the \A1\B0Animate attached shape\A1\B1 is default checked.
1506 And the bHasAnimateForm default is false, and if user uncheck it the value bAnimateForm will be false,
1507 it same as the TextGroup\A1\AFs default value, also could not be run setAnimateForm.
1508 if( bHasAnimateForm )
1510 if( pTextGroup->getAnimateForm() != bAnimateForm )
1512 pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
1513 bChanged = true;
1517 In setTextGrouping, there are three case:
1518 1. Create new text effects for empty TextGroup
1519 2. Remove all text effects of TextGroup (nTextGrouping == -1)
1520 3. Change all the text effects\A1\AF start type
1522 So here is the right logic:
1523 If set the animation from text to shape and remove text animation,
1524 should do setAnimateForm first, then do setTextGrouping.
1525 Other case,do setTextGrouping first, then do setAnimateForm.
1528 /************************************************************************/
1530 bool bDoSetAnimateFormFirst = false;
1531 bool bNeedDoSetAnimateForm = false;
1533 if( bHasAnimateForm )
1535 if( pTextGroup && pTextGroup->getAnimateForm() != bAnimateForm )
1537 if( (pTextGroup->getTextGrouping() >= 0) && (nTextGrouping == -1 ) )
1539 bDoSetAnimateFormFirst = true;
1541 bNeedDoSetAnimateForm = true;
1545 if (bDoSetAnimateFormFirst)
1547 pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
1548 bChanged = true;
1551 if( bHasTextGrouping )
1553 if( pTextGroup && pTextGroup->getTextGrouping() != nTextGrouping )
1555 pEffectSequence->setTextGrouping( pTextGroup, nTextGrouping );
1557 // All the effects of the outline object is removed so we need to
1558 // put it back. OTOH, the shape object that still has effects
1559 // in the text group is fine.
1560 if (nTextGrouping == -1 && pTextGroup->getEffects().empty())
1562 pEffect->setTarget(Any(pEffect->getTargetShape()));
1563 pEffect->setGroupId(-1);
1564 mpMainSequence->append(pEffect);
1567 bChanged = true;
1571 if (!bDoSetAnimateFormFirst && bNeedDoSetAnimateForm)
1573 if( pTextGroup )
1575 pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm );
1576 bChanged = true;
1580 if( bHasTextGroupingAuto )
1582 if( pTextGroup && pTextGroup->getTextGroupingAuto() != fTextGroupingAuto )
1584 pEffectSequence->setTextGroupingAuto( pTextGroup, fTextGroupingAuto );
1585 bChanged = true;
1589 if( bHasTextReverse )
1591 if( pTextGroup && pTextGroup->getTextReverse() != bTextReverse )
1593 pEffectSequence->setTextReverse( pTextGroup, bTextReverse );
1594 bChanged = true;
1600 if( bChanged )
1602 mpMainSequence->rebuild();
1603 updateControls();
1604 mrBase.GetDocShell()->SetModified();
1608 void CustomAnimationPane::showOptions(const OUString& rPage)
1610 std::unique_ptr<STLPropertySet> xSet = createSelectionSet();
1612 auto xDlg = std::make_shared<CustomAnimationDialog>(GetFrameWeld(), std::move(xSet), rPage);
1614 weld::DialogController::runAsync(xDlg, [xDlg, this](sal_Int32 nResult){
1615 if (nResult )
1617 addUndo();
1618 changeSelection(xDlg->getResultSet(), xDlg->getPropertySet());
1619 updateControls();
1624 void CustomAnimationPane::onChangeCurrentPage()
1626 if( !mxView.is() )
1627 return;
1631 Reference< XDrawPage > xNewPage( mxView->getCurrentPage() );
1632 if( xNewPage != mxCurrentPage )
1634 mxCurrentPage = std::move(xNewPage);
1635 SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
1636 if( pPage )
1638 mpMainSequence = pPage->getMainSequence();
1639 mxCustomAnimationList->update( mpMainSequence );
1641 updateControls();
1644 catch( Exception& )
1646 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onChangeCurrentPage()" );
1650 static bool getTextSelection(const Reference< XTextRange >& xSelectedText, Reference< XShape >& xShape, std::vector< sal_Int16 >& rParaList )
1652 if( xSelectedText.is() ) try
1654 xShape.set( xSelectedText->getText(), UNO_QUERY_THROW );
1656 css::uno::Reference<css::document::XActionLockable> xLockable(xShape, css::uno::UNO_QUERY);
1657 if (xLockable.is())
1658 xLockable->addActionLock();
1659 comphelper::ScopeGuard aGuard([&xLockable]()
1661 if (xLockable.is())
1662 xLockable->removeActionLock();
1665 Reference< XTextRangeCompare > xTextRangeCompare( xShape, UNO_QUERY_THROW );
1666 Reference< XEnumerationAccess > xParaEnumAccess( xShape, UNO_QUERY_THROW );
1667 Reference< XEnumeration > xParaEnum( xParaEnumAccess->createEnumeration(), UNO_SET_THROW );
1668 Reference< XTextRange > xRange;
1669 Reference< XTextRange > xStart( xSelectedText->getStart() );
1670 Reference< XTextRange > xEnd( xSelectedText->getEnd() );
1672 if (xTextRangeCompare->compareRegionEnds(xStart, xEnd) < 0)
1673 std::swap(xStart, xEnd);
1675 sal_Int16 nPara = 0;
1676 while( xParaEnum->hasMoreElements() )
1678 xParaEnum->nextElement() >>= xRange;
1680 // break if start of selection is prior to end of current paragraph
1681 if( xRange.is() && (xTextRangeCompare->compareRegionEnds( xStart, xRange ) >= 0 ) )
1682 break;
1684 nPara++;
1687 while( xRange.is() )
1689 if( xRange.is() && !xRange->getString().isEmpty() )
1690 rParaList.push_back( nPara );
1692 // break if end of selection is before or at end of current paragraph
1693 if( xRange.is() && xTextRangeCompare->compareRegionEnds( xEnd, xRange ) >= 0 )
1694 break;
1696 nPara++;
1698 if( xParaEnum->hasMoreElements() )
1699 xParaEnum->nextElement() >>= xRange;
1700 else
1701 xRange.clear();
1704 return true;
1706 catch( Exception& )
1708 TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::getTextSelection()" );
1711 return false;
1714 namespace
1716 Reference<XShape> getTargetShape(const Any& rTarget)
1718 Reference<XShape> xShape;
1719 rTarget >>= xShape;
1720 if( !xShape.is() )
1722 ParagraphTarget aParaTarget;
1723 if (rTarget >>= aParaTarget)
1724 xShape = aParaTarget.Shape;
1726 return xShape;
1730 void CustomAnimationPane::onAdd()
1732 bool bHasText = true;
1734 // first create vector of targets for dialog preview
1735 std::vector< Any > aTargets;
1737 // gather shapes from the selection
1738 maViewSelection = mxView->getSelection();
1740 if (Reference<XIndexAccess> xShapes; maViewSelection >>= xShapes)
1742 sal_Int32 nCount = xShapes->getCount();
1743 aTargets.reserve( nCount );
1744 for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ )
1746 Any aTarget( xShapes->getByIndex( nIndex ) );
1747 aTargets.push_back( aTarget );
1748 if( bHasText )
1750 Reference< XText > xText;
1751 aTarget >>= xText;
1752 if( !xText.is() || xText->getString().isEmpty() )
1753 bHasText = false;
1757 else if (Reference<XText> xText; maViewSelection >>= xText)
1759 aTargets.push_back( maViewSelection );
1760 if( !xText.is() || xText->getString().isEmpty() )
1761 bHasText = false;
1763 else if (Reference<XTextCursor> xCursor; maViewSelection >>= xCursor)
1765 Reference< XShape > xShape;
1766 std::vector< sal_Int16 > aParaList;
1767 if (getTextSelection(xCursor, xShape, aParaList))
1769 ParagraphTarget aParaTarget;
1770 aParaTarget.Shape = std::move(xShape);
1772 for( const auto& rPara : aParaList )
1774 aParaTarget.Paragraph = rPara;
1775 aTargets.push_back( Any( aParaTarget ) );
1779 else
1781 OSL_FAIL("sd::CustomAnimationPane::onAdd(), unknown view selection!" );
1782 return;
1785 CustomAnimationPresetPtr pDescriptor;
1786 mxFTCategory->set_sensitive(true);
1787 mxFTAnimation->set_sensitive(true);
1789 bool bCategoryReset = false;
1791 if (!mxLBCategory->get_sensitive() || mxLBCategory->get_active() == -1)
1793 mxLBCategory->set_sensitive(true);
1794 mxLBCategory->set_active(0);
1795 bCategoryReset = true;
1798 if (bCategoryReset || !mxLBAnimation->get_sensitive() ||
1799 mxLBAnimation->get_selected_index() == -1)
1801 mxLBAnimation->set_sensitive(true);
1803 sal_Int32 nFirstEffect = fillAnimationLB(bHasText);
1804 if (nFirstEffect == -1)
1805 return;
1807 mxLBAnimation->select(nFirstEffect);
1808 mnLastSelectedAnimation = nFirstEffect;
1811 auto pEntryData = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_selected_id());
1812 if (pEntryData)
1813 pDescriptor = *pEntryData;
1815 if( pDescriptor )
1817 const double fDuration = pDescriptor->getDuration();
1818 mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE);
1819 bool bHasSpeed = pDescriptor->getDuration() > 0.001;
1820 mxCBXDuration->set_sensitive( bHasSpeed );
1821 mxFTDuration->set_sensitive( bHasSpeed );
1823 mxCustomAnimationList->unselect_all();
1825 // gather shapes from the selection
1826 bool bFirst = true;
1827 for( const auto& rTarget : aTargets )
1829 css::uno::Reference<css::document::XActionLockable> xLockable(getTargetShape(rTarget), css::uno::UNO_QUERY);
1830 if (xLockable.is())
1831 xLockable->addActionLock();
1832 comphelper::ScopeGuard aGuard([&xLockable]()
1834 if (xLockable.is())
1835 xLockable->removeActionLock();
1838 CustomAnimationEffectPtr pCreated = mpMainSequence->append( pDescriptor, rTarget, fDuration );
1840 // if only one shape with text and no fill or outline is selected, animate only by first level paragraphs
1841 if( bHasText && (aTargets.size() == 1) )
1843 Reference< XShape > xShape( rTarget, UNO_QUERY );
1844 if( xShape.is() && !hasVisibleShape( xShape ) )
1846 mpMainSequence->createTextGroup( pCreated, 1, -1.0, false, false );
1850 if( bFirst )
1851 bFirst = false;
1852 else
1853 pCreated->setNodeType( EffectNodeType::WITH_PREVIOUS );
1855 if( pCreated )
1856 mxCustomAnimationList->select( pCreated );
1860 PathKind ePathKind = getCreatePathKind();
1862 if (ePathKind != PathKind::NONE)
1864 createPath( ePathKind, aTargets, 0.0 );
1865 updateMotionPathTags();
1868 addUndo();
1869 mrBase.GetDocShell()->SetModified();
1871 updateControls();
1873 if (!SlideShow::IsInteractiveSlideshow(&mrBase)) // IASS
1874 SlideShow::Stop( mrBase );
1877 void CustomAnimationPane::onRemove()
1879 if( maListSelection.empty() )
1880 return;
1882 addUndo();
1884 MainSequenceRebuildGuard aGuard( mpMainSequence );
1886 EffectSequence aList( maListSelection );
1888 for( CustomAnimationEffectPtr& pEffect : aList )
1890 if( pEffect->getEffectSequence() )
1891 pEffect->getEffectSequence()->remove( pEffect );
1894 maListSelection.clear();
1895 mrBase.GetDocShell()->SetModified();
1898 void CustomAnimationPane::remove( CustomAnimationEffectPtr const & pEffect )
1900 if( pEffect->getEffectSequence() )
1902 addUndo();
1903 pEffect->getEffectSequence()->remove( pEffect );
1904 mrBase.GetDocShell()->SetModified();
1908 void CustomAnimationPane::onChangeStart()
1910 sal_Int16 nNodeType;
1911 switch( mxLBStart->get_active() )
1913 case 0: nNodeType = EffectNodeType::ON_CLICK; break;
1914 case 1: nNodeType = EffectNodeType::WITH_PREVIOUS; break;
1915 case 2: nNodeType = EffectNodeType::AFTER_PREVIOUS; break;
1916 default:
1917 return;
1920 onChangeStart( nNodeType );
1923 void CustomAnimationPane::onChangeStart( sal_Int16 nNodeType )
1925 addUndo();
1927 MainSequenceRebuildGuard aGuard( mpMainSequence );
1929 bool bNeedRebuild = false;
1931 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1933 if( pEffect->getNodeType() != nNodeType )
1935 pEffect->setNodeType( nNodeType );
1936 bNeedRebuild = true;
1940 if( bNeedRebuild )
1942 mpMainSequence->rebuild();
1943 updateControls();
1944 mrBase.GetDocShell()->SetModified();
1948 void CustomAnimationPane::onChangeSpeed()
1950 double fDuration = getDuration();
1952 if(fDuration < 0)
1953 return;
1954 else
1956 addUndo();
1958 MainSequenceRebuildGuard aGuard( mpMainSequence );
1960 // change selected effect
1961 for( CustomAnimationEffectPtr& pEffect : maListSelection )
1963 pEffect->setDuration( fDuration );
1966 mpMainSequence->rebuild();
1967 updateControls();
1968 mrBase.GetDocShell()->SetModified();
1972 double CustomAnimationPane::getDuration() const
1974 double fDuration = 0;
1976 if (!mxCBXDuration->get_text().isEmpty())
1977 fDuration = mxCBXDuration->get_value(FieldUnit::NONE) / 100.0;
1979 return fDuration;
1982 PathKind CustomAnimationPane::getCreatePathKind() const
1984 PathKind eKind = PathKind::NONE;
1986 if (mxLBAnimation->count_selected_rows() == 1 &&
1987 mxLBCategory->get_active() == gnMotionPathPos)
1989 const sal_Int32 nPos = mxLBAnimation->get_selected_index();
1990 if( nPos == mnCurvePathPos )
1992 eKind = PathKind::CURVE;
1994 else if( nPos == mnPolygonPathPos )
1996 eKind = PathKind::POLYGON;
1998 else if( nPos == mnFreeformPathPos )
2000 eKind = PathKind::FREEFORM;
2004 return eKind;
2007 void CustomAnimationPane::createPath( PathKind eKind, std::vector< Any >& rTargets, double fDuration)
2009 sal_uInt16 nSID = 0;
2011 switch( eKind )
2013 case PathKind::CURVE: nSID = SID_DRAW_BEZIER_NOFILL; break;
2014 case PathKind::POLYGON: nSID = SID_DRAW_POLYGON_NOFILL; break;
2015 case PathKind::FREEFORM: nSID = SID_DRAW_FREELINE_NOFILL; break;
2016 default: break;
2019 if( !nSID )
2020 return;
2022 DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >(
2023 FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get());
2025 if( pViewShell )
2027 DrawView* pView = pViewShell->GetDrawView();
2028 if( pView )
2029 pView->UnmarkAllObj();
2031 std::vector< Any > aTargets( 1, Any( fDuration ) );
2032 aTargets.insert( aTargets.end(), rTargets.begin(), rTargets.end() );
2033 Sequence< Any > aTargetSequence( comphelper::containerToSequence( aTargets ) );
2034 const SfxUnoAnyItem aItem( SID_ADD_MOTION_PATH, Any( aTargetSequence ) );
2035 pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( nSID, SfxCallMode::ASYNCHRON, {&aItem} );
2040 /// this link is called when the property box is modified by the user
2041 IMPL_LINK_NOARG(CustomAnimationPane, implPropertyHdl, LinkParamNone*, void)
2043 if (!mxLBSubControl)
2044 return;
2046 addUndo();
2048 MainSequenceRebuildGuard aGuard( mpMainSequence );
2050 const Any aValue(mxLBSubControl->getValue());
2052 bool bNeedUpdate = false;
2054 // change selected effect
2055 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2057 if( setProperty1Value( mnPropertyType, pEffect, aValue ) )
2058 bNeedUpdate = true;
2061 if( bNeedUpdate )
2063 mpMainSequence->rebuild();
2064 updateControls();
2065 mrBase.GetDocShell()->SetModified();
2068 onPreview( false );
2071 IMPL_LINK_NOARG(CustomAnimationPane, DelayModifiedHdl, weld::MetricSpinButton&, void)
2073 addUndo();
2076 IMPL_LINK_NOARG(CustomAnimationPane, DelayLoseFocusHdl, weld::Widget&, void)
2078 double fBegin = mxMFStartDelay->get_value(FieldUnit::NONE);
2080 //sequence rebuild only when the control loses focus
2081 MainSequenceRebuildGuard aGuard( mpMainSequence );
2083 // change selected effect
2084 for( CustomAnimationEffectPtr& pEffect : maListSelection )
2086 pEffect->setBegin( fBegin/10.0 );
2089 mpMainSequence->rebuild();
2090 updateControls();
2091 mrBase.GetDocShell()->SetModified();
2094 IMPL_LINK_NOARG(CustomAnimationPane, AnimationSelectHdl, weld::TreeView&, void)
2096 maIdle.Start();
2099 IMPL_LINK_NOARG(CustomAnimationPane, SelectionHandler, Timer*, void)
2101 if (mxLBAnimation->has_grab()) // tdf#136474 try again later
2103 maIdle.Start();
2104 return;
2107 int nSelected = mxLBAnimation->get_selected_index();
2108 if (nSelected == -1)
2109 return;
2111 // tdf#99137, the selected entry may also be a subcategory title, so not an effect
2112 // just skip it and move to the next one in this case
2113 if (mxLBAnimation->get_text_emphasis(nSelected, 0))
2115 if (nSelected == 0 || nSelected > mnLastSelectedAnimation)
2116 mxLBAnimation->select(++nSelected);
2117 else
2118 mxLBAnimation->select(--nSelected);
2121 mnLastSelectedAnimation = nSelected;
2123 CustomAnimationPresetPtr* pPreset = weld::fromId<CustomAnimationPresetPtr*>(mxLBAnimation->get_id(nSelected));
2124 PathKind ePathKind = getCreatePathKind();
2126 if ( ePathKind != PathKind::NONE )
2128 std::vector< Any > aTargets;
2129 MainSequenceRebuildGuard aGuard( mpMainSequence );
2131 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2133 aTargets.push_back( pEffect->getTarget() );
2135 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
2136 if( !pEffectSequence )
2137 pEffectSequence = mpMainSequence.get();
2139 // delete the old animation, new one will be appended
2140 // by createPath and SID_ADD_MOTION_PATH therein
2141 pEffectSequence->remove( pEffect );
2144 createPath( ePathKind, aTargets, 0.0 );
2145 updateMotionPathTags();
2146 return;
2149 CustomAnimationPresetPtr pDescriptor(*pPreset);
2150 const double fDuration = (*pPreset)->getDuration();
2151 MainSequenceRebuildGuard aGuard( mpMainSequence );
2153 // get selected effect
2154 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2156 // Dispose the deprecated motion path tag. It will be rebuilt later.
2157 if (pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH)
2159 for (auto const& xTag: maMotionPathTags)
2161 if(xTag->getEffect() == pEffect && !xTag->isDisposed())
2162 xTag->Dispose();
2166 EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence();
2167 if( !pEffectSequence )
2168 pEffectSequence = mpMainSequence.get();
2170 pEffectSequence->replace( pEffect, pDescriptor, fDuration );
2173 addUndo();
2174 onPreview(false);
2177 IMPL_LINK_NOARG(CustomAnimationPane, UpdateAnimationLB, weld::ComboBox&, void)
2179 //FIXME: first effect only? what if there is more?
2180 bool bHasText = false;
2181 if (!maListSelection.empty())
2183 CustomAnimationEffectPtr pEffect = maListSelection.front();
2184 bHasText = pEffect && pEffect->hasText();
2186 fillAnimationLB(bHasText);
2189 IMPL_LINK_NOARG(CustomAnimationPane, DurationModifiedHdl, weld::MetricSpinButton&, void)
2191 if (!mxCBXDuration->get_text().isEmpty())
2193 double duration_value = static_cast<double>(mxCBXDuration->get_value(FieldUnit::NONE));
2194 if(duration_value <= 0.0)
2196 mxCBXDuration->set_value(1, FieldUnit::NONE);
2198 onChangeSpeed();
2202 namespace
2204 void InsertCategory(weld::TreeView& rLBAnimation, const OUString& rMotionPathLabel)
2206 int nRow = rLBAnimation.n_children();
2207 rLBAnimation.append_text(rMotionPathLabel);
2208 rLBAnimation.set_text_emphasis(nRow, true, 0);
2209 rLBAnimation.set_text_align(nRow, 0.5, 0);
2213 sal_Int32 CustomAnimationPane::fillAnimationLB( bool bHasText )
2215 PresetCategoryList rCategoryList;
2216 sal_uInt16 nPosition = mxLBCategory->get_active();
2217 const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets());
2218 switch(nPosition)
2220 case 0:rCategoryList = rPresets.getEntrancePresets();break;
2221 case 1:rCategoryList = rPresets.getEmphasisPresets();break;
2222 case 2:rCategoryList = rPresets.getExitPresets();break;
2223 case 3:rCategoryList = rPresets.getMotionPathsPresets();break;
2224 case 4:rCategoryList = rPresets.getMiscPresets();break;
2227 sal_Int32 nFirstEffect = -1;
2229 int nOldEntryCount = mxLBAnimation->n_children();
2230 int nOldScrollPos = mxLBAnimation->vadjustment_get_value();
2232 mxLBAnimation->freeze();
2233 mxLBAnimation->clear();
2234 mnLastSelectedAnimation = -1;
2236 if (nPosition == gnMotionPathPos)
2238 OUString sMotionPathLabel( SdResId( STR_CUSTOMANIMATION_USERPATH ) );
2239 InsertCategory(*mxLBAnimation, sMotionPathLabel);
2240 mnCurvePathPos = mxLBAnimation->n_children();
2241 mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulCOMBLINE) );
2242 mxLBAnimation->set_text_emphasis(mnCurvePathPos, false, 0);
2243 mnPolygonPathPos = mnCurvePathPos + 1;
2244 mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulPOLY) );
2245 mxLBAnimation->set_text_emphasis(mnPolygonPathPos, false, 0);
2246 mnFreeformPathPos = mnPolygonPathPos + 1;
2247 mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulFREELINE) );
2248 mxLBAnimation->set_text_emphasis(mnFreeformPathPos, false, 0);
2251 for (const PresetCategoryPtr& pCategory : rCategoryList)
2253 if( pCategory )
2255 InsertCategory(*mxLBAnimation, pCategory->maLabel);
2257 int nPos = mxLBAnimation->n_children();
2259 std::vector< CustomAnimationPresetPtr > aSortedVector =
2260 pCategory->maEffects;
2262 for( const CustomAnimationPresetPtr& pDescriptor : aSortedVector )
2264 // ( !isTextOnly || ( isTextOnly && bHasText ) ) <=> !isTextOnly || bHasText
2265 if( pDescriptor && ( !pDescriptor->isTextOnly() || bHasText ) )
2267 auto pCustomPtr = new CustomAnimationPresetPtr(pDescriptor);
2268 OUString sId = weld::toId(pCustomPtr);
2269 mxLBAnimation->append(sId, pDescriptor->getLabel());
2270 mxLBAnimation->set_text_emphasis(nPos, false, 0);
2272 if (nFirstEffect == -1)
2273 nFirstEffect = nPos;
2275 ++nPos;
2281 mxLBAnimation->thaw();
2283 if (mxLBAnimation->n_children() == nOldEntryCount)
2284 mxLBAnimation->vadjustment_set_value(nOldScrollPos);
2286 return nFirstEffect;
2289 IMPL_LINK(CustomAnimationPane, implToggleHdl, weld::Toggleable&, rBtn, void)
2291 implControlHdl(&rBtn);
2294 IMPL_LINK(CustomAnimationPane, implClickHdl, weld::Button&, rBtn, void)
2296 implControlHdl(&rBtn);
2299 IMPL_LINK( CustomAnimationPane, implControlListBoxHdl, weld::ComboBox&, rListBox, void )
2301 implControlHdl(&rListBox);
2304 /// this link is called when one of the controls is modified
2305 void CustomAnimationPane::implControlHdl(const weld::Widget* pControl)
2307 if (pControl == mxPBAddEffect.get())
2308 onAdd();
2309 else if (pControl == mxPBRemoveEffect.get())
2310 onRemove();
2311 else if (pControl == mxLBStart.get())
2312 onChangeStart();
2313 else if (pControl == mxPBPropertyMore.get())
2314 showOptions();
2315 else if (pControl == mxPBMoveUp.get())
2316 moveSelection( true );
2317 else if (pControl == mxPBMoveDown.get())
2318 moveSelection( false );
2319 else if (pControl == mxPBPlay.get())
2320 onPreview( true );
2321 else if (pControl == mxCBAutoPreview.get())
2323 SdOptions* pOptions = SdModule::get()->GetSdOptions(DocumentType::Impress);
2324 pOptions->SetPreviewChangedEffects(mxCBAutoPreview->get_active());
2328 IMPL_LINK_NOARG(CustomAnimationPane, lateInitCallback, Timer *, void)
2330 // Call getPresets() to initiate the (expensive) construction of the
2331 // presets list.
2332 CustomAnimationPresets::getCustomAnimationPresets();
2334 // update selection and control states
2335 onSelectionChanged();
2338 void CustomAnimationPane::moveSelection( bool bUp )
2340 if( maListSelection.empty() )
2341 return;
2343 EffectSequenceHelper* pSequence = maListSelection.front()->getEffectSequence();
2344 if( pSequence == nullptr )
2345 return;
2347 addUndo();
2349 bool bChanged = false;
2351 MainSequenceRebuildGuard aGuard( mpMainSequence );
2352 EffectSequence& rEffectSequence = pSequence->getSequence();
2354 if( bUp )
2356 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2358 EffectSequence::iterator aUpEffectPos( pSequence->find( pEffect ) );
2359 // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists
2360 if( aUpEffectPos != rEffectSequence.end() )
2362 EffectSequence::iterator aInsertPos( rEffectSequence.erase( aUpEffectPos ) );
2364 if( aInsertPos != rEffectSequence.begin() )
2366 --aInsertPos;
2367 while( (aInsertPos != rEffectSequence.begin()) && !mxCustomAnimationList->isExpanded(*aInsertPos))
2368 --aInsertPos;
2369 rEffectSequence.insert( aInsertPos, pEffect );
2371 else
2373 rEffectSequence.push_front( pEffect );
2375 bChanged = true;
2379 else
2381 EffectSequence::reverse_iterator aIter( maListSelection.rbegin() );
2382 const EffectSequence::reverse_iterator aEnd( maListSelection.rend() );
2384 while( aIter != aEnd )
2386 CustomAnimationEffectPtr pEffect = *aIter++;
2388 EffectSequence::iterator aDownEffectPos( pSequence->find( pEffect ) );
2389 // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists
2390 if( aDownEffectPos != rEffectSequence.end() )
2392 EffectSequence::iterator aInsertPos( rEffectSequence.erase( aDownEffectPos ) );
2394 if( aInsertPos != rEffectSequence.end() )
2396 ++aInsertPos;
2397 // Advance over rolled-up (un-expanded) items, unless we just moved it there.
2398 while( (aInsertPos != rEffectSequence.end())
2399 && !mxCustomAnimationList->isExpanded(*aInsertPos)
2400 && (std::find(maListSelection.begin(), maListSelection.end(), *aInsertPos)
2401 == maListSelection.end())
2403 ++aInsertPos;
2404 rEffectSequence.insert( aInsertPos, pEffect );
2406 else
2408 rEffectSequence.push_back( pEffect );
2410 bChanged = true;
2415 if( bChanged )
2417 mpMainSequence->rebuild();
2418 updateControls();
2419 mrBase.GetDocShell()->SetModified();
2423 void CustomAnimationPane::onPreview( bool bForcePreview )
2425 if (!bForcePreview && !mxCBAutoPreview->get_active())
2426 return;
2428 // No preview in LOK.
2429 if (comphelper::LibreOfficeKit::isActive())
2430 return;
2432 if( maListSelection.empty() )
2434 rtl::Reference< MotionPathTag > xMotionPathTag;
2435 auto aIter = std::find_if(maMotionPathTags.begin(), maMotionPathTags.end(),
2436 [](const MotionPathTagVector::value_type& rxMotionPathTag) { return rxMotionPathTag->isSelected(); });
2437 if (aIter != maMotionPathTags.end())
2438 xMotionPathTag = *aIter;
2440 if( xMotionPathTag.is() )
2442 MainSequencePtr pSequence = std::make_shared<MainSequence>();
2443 pSequence->append( xMotionPathTag->getEffect()->clone() );
2444 preview( pSequence->getRootNode() );
2446 else
2448 Reference< XAnimationNodeSupplier > xNodeSupplier( mxCurrentPage, UNO_QUERY );
2449 if( !xNodeSupplier.is() )
2450 return;
2452 preview( xNodeSupplier->getAnimationNode() );
2455 else
2457 MainSequencePtr pSequence = std::make_shared<MainSequence>();
2459 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2461 pSequence->append( pEffect->clone() );
2464 preview( pSequence->getRootNode() );
2468 void CustomAnimationPane::preview( const Reference< XAnimationNode >& xAnimationNode )
2470 Reference< XParallelTimeContainer > xRoot = ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() );
2471 Sequence< css::beans::NamedValue > aUserData
2472 { { u"node-type"_ustr, css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } };
2473 xRoot->setUserData( aUserData );
2474 xRoot->appendChild( xAnimationNode );
2476 SlideShow::StartPreview( mrBase, mxCurrentPage, xRoot );
2479 // ICustomAnimationListController
2480 void CustomAnimationPane::onSelect()
2482 maListSelection = mxCustomAnimationList->getSelection();
2483 updateControls();
2485 // mark shapes from selected effects
2486 if( maSelectionLock.isLocked() )
2487 return;
2489 // tdf#145030 if nothing is selected in the effects list, leave the selection of
2490 // objects in the slide untouched
2491 if (maListSelection.empty())
2492 return;
2494 ScopeLockGuard aGuard( maSelectionLock );
2495 DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >(
2496 FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get());
2497 DrawView* pView = pViewShell ? pViewShell->GetDrawView() : nullptr;
2499 if( pView )
2501 pView->UnmarkAllObj();
2502 for( const CustomAnimationEffectPtr& pEffect : maListSelection )
2504 Reference< XShape > xShape( pEffect->getTargetShape() );
2505 SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
2506 if( pObj )
2507 pView->MarkObj(pObj, pView->GetSdrPageView());
2512 // ICustomAnimationListController
2513 // pEffectInsertBefore may be null if moving to end of list.
2514 void CustomAnimationPane::onDragNDropComplete(std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore)
2516 if ( !mpMainSequence )
2517 return;
2519 addUndo();
2521 MainSequenceRebuildGuard aGuard( mpMainSequence );
2523 // Move all selected effects
2524 for( auto const& pEffectDragged : pEffectsDragged )
2526 // Move this dragged effect and any hidden sub-effects
2527 EffectSequence::iterator aIter = mpMainSequence->find( pEffectDragged );
2528 const EffectSequence::iterator aEnd( mpMainSequence->getEnd() );
2530 while( aIter != aEnd )
2532 CustomAnimationEffectPtr pEffect = *aIter++;
2534 // Update model with new location (function triggers a rebuild)
2535 // target may be null, which will insert at the end.
2536 mpMainSequence->moveToBeforeEffect( pEffect, pEffectInsertBefore );
2537 // Done moving effect and its hidden sub-effects when *next* effect is visible.
2538 if (aIter != aEnd && mxCustomAnimationList->isVisible(*aIter))
2539 break;
2543 updateControls();
2544 mrBase.GetDocShell()->SetModified();
2547 void CustomAnimationPane::updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag )
2549 MainSequenceRebuildGuard aGuard( mpMainSequence );
2550 if( !xTag.is() )
2551 return;
2553 SdrPathObj* pPathObj = xTag->getPathObj();
2554 CustomAnimationEffectPtr pEffect = xTag->getEffect();
2555 if( (pPathObj != nullptr) && pEffect )
2557 SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager();
2558 if( pManager )
2560 SdPage* pPage = SdPage::getImplementation( mxCurrentPage );
2561 if( pPage )
2562 pManager->AddUndoAction( std::make_unique<UndoAnimationPath>( mrBase.GetDocShell()->GetDoc(), pPage, pEffect->getNode() ) );
2565 pEffect->updatePathFromSdrPathObj( *pPathObj );
2571 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */