android: Update app-specific/MIME type icons
[LibreOffice.git] / sd / source / ui / animations / SlideTransitionPane.cxx
blobac3f01762d50193c4ecb495c44ed9da119e6f8c6
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/drawing/XDrawView.hpp>
21 #include <SlideTransitionPane.hxx>
23 #include <TransitionPreset.hxx>
24 #include <sdresid.hxx>
25 #include <ViewShellBase.hxx>
26 #include <DrawDocShell.hxx>
27 #include <SlideSorterViewShell.hxx>
28 #include <drawdoc.hxx>
29 #include <sdmod.hxx>
30 #include <sdpage.hxx>
31 #include <filedlg.hxx>
32 #include <strings.hrc>
33 #include <EventMultiplexer.hxx>
35 #include <comphelper/lok.hxx>
36 #include <sal/log.hxx>
37 #include <tools/debug.hxx>
38 #include <svx/gallery.hxx>
39 #include <utility>
40 #include <vcl/stdtext.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/weld.hxx>
43 #include <tools/urlobj.hxx>
44 #include <slideshow.hxx>
45 #include <sdundogr.hxx>
46 #include <undoanim.hxx>
47 #include <optsitem.hxx>
49 #include <o3tl/safeint.hxx>
51 #include <algorithm>
53 using namespace ::com::sun::star;
55 using ::com::sun::star::uno::Reference;
58 namespace sd::impl
60 struct TransitionEffect
62 TransitionEffect() :
63 mnType( 0 ),
64 mnSubType( 0 ),
65 mbDirection( true ),
66 mnFadeColor( 0 )
68 init();
70 explicit TransitionEffect( const ::sd::TransitionPreset & rPreset ) :
71 mnType( rPreset.getTransition()),
72 mnSubType( rPreset.getSubtype()),
73 mbDirection( rPreset.getDirection()),
74 mnFadeColor( rPreset.getFadeColor())
76 init();
78 explicit TransitionEffect( const SdPage & rPage ) :
79 mnType( rPage.getTransitionType() ),
80 mnSubType( rPage.getTransitionSubtype() ),
81 mbDirection( rPage.getTransitionDirection() ),
82 mnFadeColor( rPage.getTransitionFadeColor() )
84 init();
86 mfDuration = rPage.getTransitionDuration();
87 mfTime = rPage.GetTime();
88 mePresChange = rPage.GetPresChange();
89 mbSoundOn = rPage.IsSoundOn();
90 maSound = rPage.GetSoundFile();
91 mbLoopSound = rPage.IsLoopSound();
92 mbStopSound = rPage.IsStopSound();
95 void init()
97 mfDuration = 2.0;
98 mfTime = 0.0;
99 mePresChange = PresChange::Manual;
100 mbSoundOn = false;
101 mbLoopSound = false;
102 mbStopSound = false;
104 mbEffectAmbiguous = false;
105 mbDurationAmbiguous = false;
106 mbTimeAmbiguous = false;
107 mbPresChangeAmbiguous = false;
108 mbSoundAmbiguous = false;
109 mbLoopSoundAmbiguous = false;
112 void setAllAmbiguous()
114 mbEffectAmbiguous = true;
115 mbDurationAmbiguous = true;
116 mbTimeAmbiguous = true;
117 mbPresChangeAmbiguous = true;
118 mbSoundAmbiguous = true;
119 mbLoopSoundAmbiguous = true;
122 bool operator == ( const ::sd::TransitionPreset & rPreset ) const
124 return
125 (mnType == rPreset.getTransition()) &&
126 (mnSubType == rPreset.getSubtype()) &&
127 (mbDirection == rPreset.getDirection()) &&
128 (mnFadeColor == rPreset.getFadeColor());
131 void applyTo( SdPage & rOutPage ) const
133 if( ! mbEffectAmbiguous )
135 rOutPage.setTransitionType( mnType );
136 rOutPage.setTransitionSubtype( mnSubType );
137 rOutPage.setTransitionDirection( mbDirection );
138 rOutPage.setTransitionFadeColor( mnFadeColor );
141 if( ! mbDurationAmbiguous )
142 rOutPage.setTransitionDuration( mfDuration );
143 if( ! mbTimeAmbiguous )
144 rOutPage.SetTime( mfTime );
145 if( ! mbPresChangeAmbiguous )
146 rOutPage.SetPresChange( mePresChange );
147 if( ! mbSoundAmbiguous )
149 if( mbStopSound )
151 rOutPage.SetStopSound( true );
152 rOutPage.SetSound( false );
154 else
156 rOutPage.SetStopSound( false );
157 rOutPage.SetSound( mbSoundOn );
158 rOutPage.SetSoundFile( maSound );
161 if( ! mbLoopSoundAmbiguous )
162 rOutPage.SetLoopSound( mbLoopSound );
165 void compareWith( const SdPage & rPage )
167 TransitionEffect aOtherEffect( rPage );
168 mbEffectAmbiguous = mbEffectAmbiguous || aOtherEffect.mbEffectAmbiguous
169 || (mnType != aOtherEffect.mnType)
170 || (mnSubType != aOtherEffect.mnSubType)
171 || (mbDirection != aOtherEffect.mbDirection)
172 || (mnFadeColor != aOtherEffect.mnFadeColor);
174 mbDurationAmbiguous = mbDurationAmbiguous || aOtherEffect.mbDurationAmbiguous || mfDuration != aOtherEffect.mfDuration;
175 mbTimeAmbiguous = mbTimeAmbiguous || aOtherEffect.mbTimeAmbiguous || mfTime != aOtherEffect.mfTime;
176 mbPresChangeAmbiguous = mbPresChangeAmbiguous || aOtherEffect.mbPresChangeAmbiguous || mePresChange != aOtherEffect.mePresChange;
177 mbSoundAmbiguous = mbSoundAmbiguous || aOtherEffect.mbSoundAmbiguous || mbSoundOn != aOtherEffect.mbSoundOn;
178 #if 0
179 // Weird leftover isolated expression with no effect, introduced in 2007 in
180 // CWS impress122. Ifdeffed out to avoid compiler warning, kept here in case
181 // somebody who understands this code notices and understands what the
182 // "right" thing to do might be.
183 (!mbStopSound && !aOtherEffect.mbStopSound && maSound != aOtherEffect.maSound) || (mbStopSound != aOtherEffect.mbStopSound);
184 #endif
185 mbLoopSoundAmbiguous = mbLoopSoundAmbiguous || aOtherEffect.mbLoopSoundAmbiguous || mbLoopSound != aOtherEffect.mbLoopSound;
188 // effect
189 sal_Int16 mnType;
190 sal_Int16 mnSubType;
191 bool mbDirection;
192 sal_Int32 mnFadeColor;
194 // other settings
195 double mfDuration;
196 double mfTime;
197 PresChange mePresChange;
198 bool mbSoundOn;
199 OUString maSound;
200 bool mbLoopSound;
201 bool mbStopSound;
203 bool mbEffectAmbiguous;
204 bool mbDurationAmbiguous;
205 bool mbTimeAmbiguous;
206 bool mbPresChangeAmbiguous;
207 bool mbSoundAmbiguous;
208 bool mbLoopSoundAmbiguous;
211 } // namespace sd::impl
213 // Local Helper Functions
214 namespace
217 void lcl_ApplyToPages(
218 const ::sd::slidesorter::SharedPageSelection& rpPages,
219 const ::sd::impl::TransitionEffect & rEffect )
221 for( const auto& rpPage : *rpPages )
223 rEffect.applyTo( *rpPage );
227 void lcl_CreateUndoForPages(
228 const ::sd::slidesorter::SharedPageSelection& rpPages,
229 ::sd::ViewShellBase const & rBase )
231 ::sd::DrawDocShell* pDocSh = rBase.GetDocShell();
232 if (!pDocSh)
233 return;
234 SfxUndoManager* pManager = pDocSh->GetUndoManager();
235 if (!pManager)
236 return;
237 SdDrawDocument* pDoc = pDocSh->GetDoc();
238 if (!pDoc)
239 return;
241 OUString aComment( SdResId(STR_UNDO_SLIDE_PARAMS) );
242 pManager->EnterListAction(aComment, aComment, 0, rBase.GetViewShellId());
243 std::unique_ptr<SdUndoGroup> pUndoGroup(new SdUndoGroup( pDoc ));
244 pUndoGroup->SetComment( aComment );
246 for( const auto& rpPage : *rpPages )
248 pUndoGroup->AddAction( new sd::UndoTransition( pDoc, rpPage ) );
251 pManager->AddUndoAction( std::move(pUndoGroup) );
252 pManager->LeaveListAction();
255 struct lcl_EqualsSoundFileName
257 explicit lcl_EqualsSoundFileName( OUString aStr ) :
258 maStr(std::move( aStr ))
261 bool operator() ( const OUString & rStr ) const
263 // note: formerly this was a case insensitive search for all
264 // platforms. It seems more sensible to do this platform-dependent
265 INetURLObject aURL(rStr);
266 #if defined(_WIN32)
267 return maStr.equalsIgnoreAsciiCase( aURL.GetBase() );
268 #else
269 return maStr == aURL.GetBase();
270 #endif
273 private:
274 OUString maStr;
277 // returns -1 if no object was found
278 bool lcl_findSoundInList( const ::std::vector< OUString > & rSoundList,
279 std::u16string_view rFileName,
280 ::std::vector< OUString >::size_type & rOutPosition )
282 INetURLObject aURL(rFileName);
283 ::std::vector< OUString >::const_iterator aIt =
284 ::std::find_if( rSoundList.begin(), rSoundList.end(),
285 lcl_EqualsSoundFileName( aURL.GetBase()));
286 if( aIt != rSoundList.end())
288 rOutPosition = ::std::distance( rSoundList.begin(), aIt );
289 return true;
292 return false;
295 OUString lcl_getSoundFileURL(
296 const ::std::vector< OUString > & rSoundList,
297 const weld::ComboBox& rListBox )
299 sal_Int32 nPos = rListBox.get_active();
300 // the first three entries are no actual sounds
301 if( nPos >= 3 )
303 DBG_ASSERT( static_cast<sal_uInt32>(rListBox.get_count() - 3) == rSoundList.size(),
304 "Sound list-box is not synchronized to sound list" );
305 nPos -= 3;
306 if( rSoundList.size() > o3tl::make_unsigned(nPos) )
307 return rSoundList[ nPos ];
310 return OUString();
313 struct lcl_AppendSoundToListBox
315 explicit lcl_AppendSoundToListBox(weld::ComboBox& rListBox)
316 : mrListBox( rListBox )
319 void operator() ( std::u16string_view rString ) const
321 INetURLObject aURL( rString );
322 mrListBox.append_text( aURL.GetBase() );
325 private:
326 weld::ComboBox& mrListBox;
329 void lcl_FillSoundListBox(
330 const ::std::vector< OUString > & rSoundList,
331 weld::ComboBox& rOutListBox )
333 sal_Int32 nCount = rOutListBox.get_count();
335 // keep first three entries
336 for( sal_Int32 i=nCount - 1; i>=3; --i )
337 rOutListBox.remove( i );
339 ::std::for_each( rSoundList.begin(), rSoundList.end(),
340 lcl_AppendSoundToListBox( rOutListBox ));
343 /// Returns an offset into the list of transition presets
344 size_t getPresetOffset( const sd::impl::TransitionEffect &rEffect )
346 const sd::TransitionPresetList& rPresetList =
347 sd::TransitionPreset::getTransitionPresetList();
349 size_t nIdx = 0;
350 for( const auto& aIt: rPresetList )
352 if( rEffect.operator==( *aIt ))
353 break;
354 nIdx++;
356 return nIdx;
359 } // anonymous namespace
361 namespace sd
364 class TransitionPane : public ValueSet
366 public:
367 explicit TransitionPane(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
368 : ValueSet(std::move(pScrolledWindow))
372 void Recalculate()
374 GetScrollBar()->set_vpolicy(VclPolicyType::AUTOMATIC);
375 RecalculateItemSizes();
378 virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
380 Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(70, 88), MapMode(MapUnit::MapAppFont));
381 pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
382 ValueSet::SetDrawingArea(pDrawingArea);
383 SetOutputSizePixel(aSize);
385 SetStyle(GetStyle() | WB_ITEMBORDER | WB_FLATVALUESET | WB_VSCROLL);
386 EnableFullItemMode( false );
387 SetColCount(3);
391 // SlideTransitionPane
392 SlideTransitionPane::SlideTransitionPane(
393 weld::Widget* pParent,
394 ViewShellBase & rBase) :
395 PanelLayout( pParent, "SlideTransitionsPanel", "modules/simpress/ui/slidetransitionspanel.ui" ),
396 mrBase( rBase ),
397 mpDrawDoc( rBase.GetDocShell() ? rBase.GetDocShell()->GetDoc() : nullptr ),
398 mbHasSelection( false ),
399 mbUpdatingControls( false ),
400 mbIsMainViewChangePending( false ),
401 maLateInitTimer("sd SlideTransitionPane maLateInitTimer")
403 Initialize(mpDrawDoc);
406 css::ui::LayoutSize SlideTransitionPane::GetHeightForWidth(const sal_Int32 /*nWidth*/)
408 sal_Int32 nMinimumHeight = get_preferred_size().Height();
409 return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight);
412 constexpr sal_uInt16 nNoneId = std::numeric_limits<sal_uInt16>::max();
414 void SlideTransitionPane::Initialize(SdDrawDocument* pDoc)
416 mxLB_VARIANT = m_xBuilder->weld_combo_box("variant_list");
417 mxCBX_duration = m_xBuilder->weld_metric_spin_button("transition_duration", FieldUnit::SECOND);
418 mxFT_SOUND = m_xBuilder->weld_label("sound_label");
419 mxLB_SOUND = m_xBuilder->weld_combo_box("sound_list");
420 mxCB_LOOP_SOUND = m_xBuilder->weld_check_button("loop_sound");
421 mxRB_ADVANCE_ON_MOUSE = m_xBuilder->weld_radio_button("rb_mouse_click");
422 mxRB_ADVANCE_AUTO = m_xBuilder->weld_radio_button("rb_auto_after");
423 mxMF_ADVANCE_AUTO_AFTER = m_xBuilder->weld_metric_spin_button("auto_after_value", FieldUnit::SECOND);
424 mxPB_APPLY_TO_ALL = m_xBuilder->weld_button("apply_to_all");
425 mxPB_PLAY = m_xBuilder->weld_button("play");
426 mxCB_AUTO_PREVIEW = m_xBuilder->weld_check_button("auto_preview");
428 auto nMax = mxMF_ADVANCE_AUTO_AFTER->get_max(FieldUnit::SECOND);
429 mxMF_ADVANCE_AUTO_AFTER->set_max(99, FieldUnit::SECOND);
430 int nWidthChars = mxMF_ADVANCE_AUTO_AFTER->get_width_chars();
431 mxMF_ADVANCE_AUTO_AFTER->set_max(nMax, FieldUnit::SECOND);
432 mxMF_ADVANCE_AUTO_AFTER->set_width_chars(nWidthChars);
433 mxCBX_duration->set_width_chars(nWidthChars);
435 mxVS_TRANSITION_ICONS.reset(new TransitionPane(m_xBuilder->weld_scrolled_window("transitions_iconswin", true)));
436 mxVS_TRANSITION_ICONSWin.reset(new weld::CustomWeld(*m_xBuilder, "transitions_icons", *mxVS_TRANSITION_ICONS));
438 if( pDoc )
439 mxModel.set( pDoc->getUnoModel(), uno::UNO_QUERY );
440 // TODO: get correct view
441 if( mxModel.is())
442 mxView.set( mxModel->getCurrentController(), uno::UNO_QUERY );
444 // dummy list box of slide transitions for startup.
445 mxVS_TRANSITION_ICONS->InsertItem(
446 nNoneId, Image( StockImage::Yes, "sd/cmd/transition-none.png" ),
447 SdResId( STR_SLIDETRANSITION_NONE ),
448 VALUESET_APPEND, /* show legend */ true );
449 mxVS_TRANSITION_ICONS->Recalculate();
451 // set defaults
452 mxCB_AUTO_PREVIEW->set_active(true); // automatic preview on
454 // update control states before adding handlers
455 updateControls();
457 // set handlers
458 mxPB_APPLY_TO_ALL->connect_clicked( LINK( this, SlideTransitionPane, ApplyToAllButtonClicked ));
459 mxPB_PLAY->connect_clicked( LINK( this, SlideTransitionPane, PlayButtonClicked ));
461 mxVS_TRANSITION_ICONS->SetSelectHdl( LINK( this, SlideTransitionPane, TransitionSelected ));
463 mxLB_VARIANT->connect_changed( LINK( this, SlideTransitionPane, VariantListBoxSelected ));
464 mxCBX_duration->connect_value_changed(LINK( this, SlideTransitionPane, DurationModifiedHdl));
465 mxCBX_duration->connect_focus_out(LINK( this, SlideTransitionPane, DurationLoseFocusHdl));
466 mxLB_SOUND->connect_changed( LINK( this, SlideTransitionPane, SoundListBoxSelected ));
467 mxCB_LOOP_SOUND->connect_toggled( LINK( this, SlideTransitionPane, LoopSoundBoxChecked ));
469 mxRB_ADVANCE_ON_MOUSE->connect_toggled( LINK( this, SlideTransitionPane, AdvanceSlideRadioButtonToggled ));
470 mxRB_ADVANCE_AUTO->connect_toggled( LINK( this, SlideTransitionPane, AdvanceSlideRadioButtonToggled ));
471 mxMF_ADVANCE_AUTO_AFTER->connect_value_changed( LINK( this, SlideTransitionPane, AdvanceTimeModified ));
472 mxCB_AUTO_PREVIEW->connect_toggled( LINK( this, SlideTransitionPane, AutoPreviewClicked ));
473 addListener();
475 maLateInitTimer.SetTimeout(200);
476 maLateInitTimer.SetInvokeHandler(LINK(this, SlideTransitionPane, LateInitCallback));
477 maLateInitTimer.Start();
480 SlideTransitionPane::~SlideTransitionPane()
482 maLateInitTimer.Stop();
483 removeListener();
484 mxVS_TRANSITION_ICONSWin.reset();
485 mxVS_TRANSITION_ICONS.reset();
486 mxLB_VARIANT.reset();
487 mxCBX_duration.reset();
488 mxFT_SOUND.reset();
489 mxLB_SOUND.reset();
490 mxCB_LOOP_SOUND.reset();
491 mxRB_ADVANCE_ON_MOUSE.reset();
492 mxRB_ADVANCE_AUTO.reset();
493 mxMF_ADVANCE_AUTO_AFTER.reset();
494 mxPB_APPLY_TO_ALL.reset();
495 mxPB_PLAY.reset();
496 mxCB_AUTO_PREVIEW.reset();
499 void SlideTransitionPane::onSelectionChanged()
501 updateControls();
504 void SlideTransitionPane::onChangeCurrentPage()
506 updateControls();
509 ::sd::slidesorter::SharedPageSelection SlideTransitionPane::getSelectedPages() const
511 ::sd::slidesorter::SlideSorterViewShell * pSlideSorterViewShell
512 = ::sd::slidesorter::SlideSorterViewShell::GetSlideSorter(mrBase);
513 std::shared_ptr<sd::slidesorter::SlideSorterViewShell::PageSelection> pSelection;
515 if( pSlideSorterViewShell )
517 pSelection = pSlideSorterViewShell->GetPageSelection();
519 else
521 pSelection = std::make_shared<sd::slidesorter::SlideSorterViewShell::PageSelection>();
522 if( mxView.is() )
524 SdPage* pPage = SdPage::getImplementation( mxView->getCurrentPage() );
525 if( pPage )
526 pSelection->push_back(pPage);
530 return pSelection;
533 void SlideTransitionPane::updateControls()
535 ::sd::slidesorter::SharedPageSelection pSelectedPages(getSelectedPages());
536 if( pSelectedPages->empty())
538 mbHasSelection = false;
539 return;
541 mbHasSelection = true;
543 DBG_ASSERT( ! mbUpdatingControls, "Multiple Control Updates" );
544 mbUpdatingControls = true;
546 // get model data for first page
547 SdPage * pFirstPage = pSelectedPages->front();
548 DBG_ASSERT( pFirstPage, "Invalid Page" );
550 impl::TransitionEffect aEffect( *pFirstPage );
552 // merge with other pages
554 // start with second page (note aIt != aEndIt, because ! aSelectedPages.empty())
555 for( const auto& rpPage : *pSelectedPages )
557 if( rpPage )
558 aEffect.compareWith( *rpPage );
561 // detect current slide effect
562 if( aEffect.mbEffectAmbiguous )
564 SAL_WARN( "sd.transitions", "Unusual, ambiguous transition effect" );
565 mxVS_TRANSITION_ICONS->SelectItem(nNoneId);
567 else
569 // ToDo: That 0 is "no transition" is documented nowhere except in the
570 // CTOR of sdpage
571 if( aEffect.mnType == 0 )
572 mxVS_TRANSITION_ICONS->SelectItem(nNoneId);
573 else
574 updateVariants( getPresetOffset( aEffect ) );
577 if( aEffect.mbDurationAmbiguous )
579 mxCBX_duration->set_text("");
580 //TODO mxCBX_duration->SetNoSelection();
582 else
584 mxCBX_duration->set_value( (aEffect.mfDuration)*100.0, FieldUnit::SECOND );
587 if( aEffect.mbSoundAmbiguous )
589 mxLB_SOUND->set_active(-1);
590 maCurrentSoundFile.clear();
592 else
594 maCurrentSoundFile.clear();
595 if( aEffect.mbStopSound )
597 mxLB_SOUND->set_active( 1 );
599 else if( aEffect.mbSoundOn && !aEffect.maSound.isEmpty() )
601 std::vector<OUString>::size_type nPos = 0;
602 if( lcl_findSoundInList( maSoundList, aEffect.maSound, nPos ))
604 mxLB_SOUND->set_active( nPos + 3 );
605 maCurrentSoundFile = aEffect.maSound;
608 else
610 mxLB_SOUND->set_active( 0 );
614 if( aEffect.mbLoopSoundAmbiguous )
616 mxCB_LOOP_SOUND->set_state(TRISTATE_INDET);
618 else
620 mxCB_LOOP_SOUND->set_active(aEffect.mbLoopSound);
623 if( aEffect.mbPresChangeAmbiguous )
625 mxRB_ADVANCE_ON_MOUSE->set_active( false );
626 mxRB_ADVANCE_AUTO->set_active( false );
628 else
630 mxRB_ADVANCE_ON_MOUSE->set_active( aEffect.mePresChange == PresChange::Manual );
631 mxRB_ADVANCE_AUTO->set_active( aEffect.mePresChange == PresChange::Auto );
632 mxMF_ADVANCE_AUTO_AFTER->set_value(aEffect.mfTime * 100.0, FieldUnit::SECOND);
635 if (comphelper::LibreOfficeKit::isActive())
637 mxPB_PLAY->hide();
638 mxCB_AUTO_PREVIEW->set_active(false);
639 mxCB_AUTO_PREVIEW->hide();
640 mxFT_SOUND->hide();
641 mxLB_SOUND->hide();
642 mxCB_LOOP_SOUND->hide();
644 else
646 SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress);
647 mxCB_AUTO_PREVIEW->set_active( pOptions->IsPreviewTransitions() );
650 mbUpdatingControls = false;
652 updateControlState();
655 void SlideTransitionPane::updateControlState()
657 mxVS_TRANSITION_ICONSWin->set_sensitive( mbHasSelection );
658 mxLB_VARIANT->set_sensitive( mbHasSelection && mxLB_VARIANT->get_count() > 0 );
659 mxCBX_duration->set_sensitive( mbHasSelection );
660 mxLB_SOUND->set_sensitive( mbHasSelection );
661 mxCB_LOOP_SOUND->set_sensitive( mbHasSelection && (mxLB_SOUND->get_active() > 2));
662 mxRB_ADVANCE_ON_MOUSE->set_sensitive( mbHasSelection );
663 mxRB_ADVANCE_AUTO->set_sensitive( mbHasSelection );
664 mxMF_ADVANCE_AUTO_AFTER->set_sensitive( mbHasSelection && mxRB_ADVANCE_AUTO->get_active());
666 mxPB_APPLY_TO_ALL->set_sensitive( mbHasSelection );
667 mxPB_PLAY->set_sensitive( mbHasSelection );
668 mxCB_AUTO_PREVIEW->set_sensitive( mbHasSelection );
671 void SlideTransitionPane::updateSoundList()
673 maSoundList.clear();
675 GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, maSoundList );
676 GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, maSoundList );
678 lcl_FillSoundListBox( maSoundList, *mxLB_SOUND );
681 void SlideTransitionPane::openSoundFileDialog()
683 if( ! mxLB_SOUND->get_sensitive())
684 return;
686 weld::Window* pDialogParent(GetFrameWeld());
687 SdOpenSoundFileDialog aFileDialog(pDialogParent);
689 DBG_ASSERT( mxLB_SOUND->get_active() == 2,
690 "Dialog should only open when \"Other sound\" is selected" );
692 bool bValidSoundFile( false );
693 bool bQuitLoop( false );
695 while( ! bQuitLoop &&
696 aFileDialog.Execute() == ERRCODE_NONE )
698 OUString aFile = aFileDialog.GetPath();
699 std::vector<OUString>::size_type nPos = 0;
700 bValidSoundFile = lcl_findSoundInList( maSoundList, aFile, nPos );
702 if( bValidSoundFile )
704 bQuitLoop = true;
706 else // not in sound list
708 // try to insert into gallery
709 if( GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aFile ) )
711 updateSoundList();
712 bValidSoundFile = lcl_findSoundInList( maSoundList, aFile, nPos );
713 DBG_ASSERT( bValidSoundFile, "Adding sound to gallery failed" );
715 bQuitLoop = true;
717 else
719 OUString aStrWarning(SdResId(STR_WARNING_NOSOUNDFILE));
720 aStrWarning = aStrWarning.replaceFirst("%", aFile);
721 std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(pDialogParent,
722 VclMessageType::Warning, VclButtonsType::NONE,
723 aStrWarning));
724 xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY);
725 xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
726 bQuitLoop = (xWarn->run() != RET_RETRY);
728 bValidSoundFile = false;
732 if( bValidSoundFile )
733 // skip first three entries in list
734 mxLB_SOUND->set_active( nPos + 3 );
737 if( bValidSoundFile )
738 return;
740 if( !maCurrentSoundFile.isEmpty() )
742 std::vector<OUString>::size_type nPos = 0;
743 if( lcl_findSoundInList( maSoundList, maCurrentSoundFile, nPos ))
744 mxLB_SOUND->set_active( nPos + 3 );
745 else
746 mxLB_SOUND->set_active( 0 ); // NONE
748 else
749 mxLB_SOUND->set_active( 0 ); // NONE
752 impl::TransitionEffect SlideTransitionPane::getTransitionEffectFromControls() const
754 impl::TransitionEffect aResult;
755 aResult.setAllAmbiguous();
757 bool bNoneSelected = mxVS_TRANSITION_ICONS->IsNoSelection() || mxVS_TRANSITION_ICONS->GetSelectedItemId() == nNoneId;
759 // check first (aResult might be overwritten)
760 if( mxVS_TRANSITION_ICONSWin->get_sensitive() &&
761 !bNoneSelected &&
762 mxVS_TRANSITION_ICONS->GetSelectedItemId() > 0 )
764 const sd::TransitionPresetList& rPresetList = sd::TransitionPreset::getTransitionPresetList();
765 auto aSelected = rPresetList.begin();
766 std::advance( aSelected, mxVS_TRANSITION_ICONS->GetSelectedItemId() - 1);
768 if (mxLB_VARIANT->get_active() == -1)
770 // Transition with just one effect.
771 aResult = impl::TransitionEffect( **aSelected );
772 aResult.setAllAmbiguous();
774 else
776 int nVariant = 0;
777 bool bFound = false;
778 for( const auto& aIter: rPresetList )
780 if( aIter->getSetId() == (*aSelected)->getSetId() )
782 if( mxLB_VARIANT->get_active() == nVariant)
784 aResult = impl::TransitionEffect( *aIter );
785 aResult.setAllAmbiguous();
786 bFound = true;
787 break;
789 else
791 nVariant++;
795 if( !bFound )
797 aResult.mnType = 0;
800 aResult.mbEffectAmbiguous = false;
802 else if (bNoneSelected)
804 aResult.mbEffectAmbiguous = false;
807 //duration
809 if( mxCBX_duration->get_sensitive() && (!(mxCBX_duration->get_text()).isEmpty()) )
811 aResult.mfDuration = static_cast<double>(mxCBX_duration->get_value(FieldUnit::SECOND))/100.0;
812 aResult.mbDurationAmbiguous = false;
815 // slide-advance mode
816 if( mxRB_ADVANCE_ON_MOUSE->get_sensitive() && mxRB_ADVANCE_AUTO->get_sensitive() &&
817 (mxRB_ADVANCE_ON_MOUSE->get_active() || mxRB_ADVANCE_AUTO->get_active()))
819 if( mxRB_ADVANCE_ON_MOUSE->get_active())
820 aResult.mePresChange = PresChange::Manual;
821 else
823 aResult.mePresChange = PresChange::Auto;
824 if( mxMF_ADVANCE_AUTO_AFTER->get_sensitive())
826 aResult.mfTime = static_cast<double>(mxMF_ADVANCE_AUTO_AFTER->get_value(FieldUnit::SECOND) ) / 100.0 ;
827 aResult.mbTimeAmbiguous = false;
831 aResult.mbPresChangeAmbiguous = false;
834 // sound
835 if( mxLB_SOUND->get_sensitive())
837 maCurrentSoundFile.clear();
838 sal_Int32 nPos = mxLB_SOUND->get_active();
839 if (nPos != -1)
841 aResult.mbStopSound = nPos == 1;
842 aResult.mbSoundOn = nPos > 1;
843 if( aResult.mbStopSound )
845 aResult.maSound.clear();
846 aResult.mbSoundAmbiguous = false;
848 else
850 aResult.maSound = lcl_getSoundFileURL(maSoundList, *mxLB_SOUND);
851 aResult.mbSoundAmbiguous = false;
852 maCurrentSoundFile = aResult.maSound;
857 // sound loop
858 if( mxCB_LOOP_SOUND->get_sensitive() )
860 aResult.mbLoopSound = mxCB_LOOP_SOUND->get_active();
861 aResult.mbLoopSoundAmbiguous = false;
864 return aResult;
867 void SlideTransitionPane::applyToSelectedPages(bool bPreview = true)
869 if( mbUpdatingControls )
870 return;
872 vcl::Window *pFocusWindow = Application::GetFocusWindow();
874 ::sd::slidesorter::SharedPageSelection pSelectedPages( getSelectedPages());
875 impl::TransitionEffect aEffect = getTransitionEffectFromControls();
876 if( ! pSelectedPages->empty())
878 lcl_CreateUndoForPages( pSelectedPages, mrBase );
879 lcl_ApplyToPages( pSelectedPages, aEffect );
880 mrBase.GetDocShell()->SetModified();
882 if( mxCB_AUTO_PREVIEW->get_sensitive() &&
883 mxCB_AUTO_PREVIEW->get_active() && bPreview)
885 if (aEffect.mnType) // mnType = 0 denotes no transition
886 playCurrentEffect();
887 else if( mxView.is() )
888 SlideShow::Stop( mrBase );
891 if (pFocusWindow)
892 pFocusWindow->GrabFocus();
895 void SlideTransitionPane::playCurrentEffect()
897 if( mxView.is() )
900 Reference< css::animations::XAnimationNode > xNode;
901 SlideShow::StartPreview( mrBase, mxView->getCurrentPage(), xNode );
905 void SlideTransitionPane::addListener()
907 Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,SlideTransitionPane,EventMultiplexerListener) );
908 mrBase.GetEventMultiplexer()->AddEventListener( aLink );
911 void SlideTransitionPane::removeListener()
913 Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,SlideTransitionPane,EventMultiplexerListener) );
914 mrBase.GetEventMultiplexer()->RemoveEventListener( aLink );
917 IMPL_LINK(SlideTransitionPane,EventMultiplexerListener,
918 tools::EventMultiplexerEvent&, rEvent, void)
920 switch (rEvent.meEventId)
922 case EventMultiplexerEventId::EditViewSelection:
923 onSelectionChanged();
924 break;
926 case EventMultiplexerEventId::CurrentPageChanged:
927 case EventMultiplexerEventId::SlideSortedSelection:
928 onChangeCurrentPage();
929 break;
931 case EventMultiplexerEventId::MainViewRemoved:
932 mxView.clear();
933 onSelectionChanged();
934 onChangeCurrentPage();
935 break;
937 case EventMultiplexerEventId::MainViewAdded:
938 mbIsMainViewChangePending = true;
939 break;
941 case EventMultiplexerEventId::ConfigurationUpdated:
942 if (mbIsMainViewChangePending)
944 mbIsMainViewChangePending = false;
946 // At this moment the controller may not yet been set at
947 // model or ViewShellBase. Take it from the view shell
948 // passed with the event.
949 if (mrBase.GetMainViewShell() != nullptr)
951 mxView.set(mrBase.GetController(), css::uno::UNO_QUERY);
952 onSelectionChanged();
953 onChangeCurrentPage();
956 break;
958 default:
959 if (rEvent.meEventId != EventMultiplexerEventId::Disposing)
961 onSelectionChanged();
962 onChangeCurrentPage();
964 break;
968 IMPL_LINK_NOARG(SlideTransitionPane, ApplyToAllButtonClicked, weld::Button&, void)
970 DBG_ASSERT( mpDrawDoc, "Invalid Draw Document!" );
971 if( !mpDrawDoc )
972 return;
974 ::sd::slidesorter::SharedPageSelection pPages =
975 std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>();
977 sal_uInt16 nPageCount = mpDrawDoc->GetSdPageCount( PageKind::Standard );
978 pPages->reserve( nPageCount );
979 for( sal_uInt16 i=0; i<nPageCount; ++i )
981 SdPage * pPage = mpDrawDoc->GetSdPage( i, PageKind::Standard );
982 if( pPage )
983 pPages->push_back( pPage );
986 if( ! pPages->empty())
988 lcl_CreateUndoForPages( pPages, mrBase );
989 lcl_ApplyToPages( pPages, getTransitionEffectFromControls() );
993 IMPL_LINK_NOARG(SlideTransitionPane, PlayButtonClicked, weld::Button&, void)
995 playCurrentEffect();
998 IMPL_LINK_NOARG(SlideTransitionPane, TransitionSelected, ValueSet*, void)
1000 updateVariants( mxVS_TRANSITION_ICONS->GetSelectedItemId() - 1 );
1001 applyToSelectedPages();
1004 /// we use an integer offset into the list of transition presets
1005 void SlideTransitionPane::updateVariants( size_t nPresetOffset )
1007 const sd::TransitionPresetList& rPresetList = sd::TransitionPreset::getTransitionPresetList();
1008 mxLB_VARIANT->clear();
1009 mxVS_TRANSITION_ICONS->SelectItem(nNoneId);
1011 if( nPresetOffset >= rPresetList.size() )
1013 mxLB_VARIANT->set_sensitive( false );
1015 else
1017 auto pFound = rPresetList.begin();
1018 std::advance( pFound, nPresetOffset );
1020 // Fill in the variant listbox
1021 size_t nFirstItem = 0, nItem = 1;
1022 for( const auto& aIt: rPresetList )
1024 if( aIt->getSetId() == (*pFound)->getSetId() )
1026 if (!nFirstItem)
1027 nFirstItem = nItem;
1028 if( !aIt->getVariantLabel().isEmpty() )
1030 mxLB_VARIANT->append_text( aIt->getVariantLabel() );
1031 if( *pFound == aIt )
1032 mxLB_VARIANT->set_active( mxLB_VARIANT->get_count()-1 );
1035 nItem++;
1038 if( mxLB_VARIANT->get_count() == 0 )
1039 mxLB_VARIANT->set_sensitive( false );
1040 else
1041 mxLB_VARIANT->set_sensitive(true);
1043 // item has the id of the first transition from this set.
1044 mxVS_TRANSITION_ICONS->SelectItem( nFirstItem );
1048 IMPL_LINK_NOARG(SlideTransitionPane, AdvanceSlideRadioButtonToggled, weld::Toggleable&, void)
1050 updateControlState();
1051 applyToSelectedPages(false);
1054 IMPL_LINK_NOARG(SlideTransitionPane, AdvanceTimeModified, weld::MetricSpinButton&, void)
1056 applyToSelectedPages(false);
1059 IMPL_LINK_NOARG(SlideTransitionPane, VariantListBoxSelected, weld::ComboBox&, void)
1061 applyToSelectedPages();
1064 IMPL_LINK_NOARG(SlideTransitionPane, DurationModifiedHdl, weld::MetricSpinButton&, void)
1066 double duration_value = static_cast<double>(mxCBX_duration->get_value(FieldUnit::SECOND));
1067 if (duration_value <= 0.0)
1068 mxCBX_duration->set_value(0, FieldUnit::SECOND);
1069 else
1070 mxCBX_duration->set_value(duration_value, FieldUnit::SECOND);
1072 applyToSelectedPages();
1075 IMPL_LINK_NOARG(SlideTransitionPane, DurationLoseFocusHdl, weld::Widget&, void)
1077 applyToSelectedPages();
1080 IMPL_LINK_NOARG(SlideTransitionPane, SoundListBoxSelected, weld::ComboBox&, void)
1082 sal_Int32 nPos = mxLB_SOUND->get_active();
1083 if( nPos == 2 )
1085 // other sound...
1086 openSoundFileDialog();
1088 updateControlState();
1089 applyToSelectedPages();
1092 IMPL_LINK_NOARG(SlideTransitionPane, LoopSoundBoxChecked, weld::Toggleable&, void)
1094 applyToSelectedPages();
1097 IMPL_LINK_NOARG(SlideTransitionPane, AutoPreviewClicked, weld::Toggleable&, void)
1099 SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress);
1100 pOptions->SetPreviewTransitions( mxCB_AUTO_PREVIEW->get_active() );
1103 IMPL_LINK_NOARG(SlideTransitionPane, LateInitCallback, Timer *, void)
1105 const TransitionPresetList& rPresetList = TransitionPreset::getTransitionPresetList();
1107 size_t nPresetOffset = 0;
1108 for( const TransitionPresetPtr& pPreset: rPresetList )
1110 const OUString sLabel( pPreset->getSetLabel() );
1111 if( !sLabel.isEmpty() )
1113 if( m_aNumVariants.find( pPreset->getSetId() ) == m_aNumVariants.end() )
1115 OUString sImageName("sd/cmd/transition-" + pPreset->getSetId() + ".png");
1116 BitmapEx aIcon( sImageName );
1117 if ( aIcon.IsEmpty() ) // need a fallback
1118 sImageName = "sd/cmd/transition-none.png";
1120 mxVS_TRANSITION_ICONS->InsertItem(
1121 nPresetOffset + 1, Image(StockImage::Yes, sImageName), sLabel,
1122 VALUESET_APPEND, /* show legend */ true );
1124 m_aNumVariants[ pPreset->getSetId() ] = 1;
1126 else
1128 m_aNumVariants[ pPreset->getSetId() ]++;
1131 nPresetOffset++;
1133 mxVS_TRANSITION_ICONS->Recalculate();
1135 SAL_INFO( "sd.transitions", "Item transition offsets in ValueSet:");
1136 for( size_t i = 0; i < mxVS_TRANSITION_ICONS->GetItemCount(); ++i )
1137 SAL_INFO( "sd.transitions", i << ":" << mxVS_TRANSITION_ICONS->GetItemId( i ) );
1139 nPresetOffset = 0;
1140 SAL_INFO( "sd.transitions", "Transition presets by offsets:");
1141 for( const auto& aIter: rPresetList )
1143 SAL_INFO( "sd.transitions", nPresetOffset++ << " " <<
1144 aIter->getPresetId() << ": " << aIter->getSetId() );
1147 updateSoundList();
1148 updateControls();
1151 } // namespace sd
1153 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */