/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sdresid.hxx" #include #include #include "CustomAnimationCreateDialog.hxx" #include "CustomAnimation.hrc" #include "CustomAnimationPane.hxx" #include "optsitem.hxx" #include "sddll.hxx" #include "sdmod.hxx" #include "helpids.h" using namespace ::com::sun::star; using ::com::sun::star::uno::UNO_QUERY; using ::com::sun::star::uno::UNO_QUERY_THROW; using ::com::sun::star::uno::Any; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::Exception; using namespace ::com::sun::star::presentation; namespace sd { const int ENTRANCE = 0; const int EMPHASIS = 1; const int EXIT = 2; const int MOTIONPATH = 3; const int MISCEFFECTS = 4; class CategoryListBox : public ListBox { public: explicit CategoryListBox( vcl::Window* pParent ); virtual ~CategoryListBox(); virtual void MouseButtonUp( const MouseEvent& rMEvt ) override; sal_Int32 InsertCategory( const OUString& rStr, sal_Int32 nPos = LISTBOX_APPEND ); void SetDoubleClickLink( const Link& rDoubleClickHdl ) { maDoubleClickHdl = rDoubleClickHdl; } DECL_LINK_TYPED(implDoubleClickHdl, ListBox&, void); private: virtual void UserDraw( const UserDrawEvent& rUDEvt ) override; Link maDoubleClickHdl; }; CategoryListBox::CategoryListBox( vcl::Window* pParent ) : ListBox( pParent, WB_TABSTOP | WB_BORDER ) { EnableUserDraw( true ); SetDoubleClickHdl( LINK( this, CategoryListBox, implDoubleClickHdl ) ); } VCL_BUILDER_FACTORY(CategoryListBox) CategoryListBox::~CategoryListBox() { } sal_Int32 CategoryListBox::InsertCategory( const OUString& rStr, sal_Int32 nPos /* = LISTBOX_APPEND */ ) { sal_Int32 n = ListBox::InsertEntry( rStr, nPos ); if( n != LISTBOX_ENTRY_NOTFOUND ) ListBox::SetEntryFlags( n, ListBox::GetEntryFlags(n) | ListBoxEntryFlags::DisableSelection ); return n; } void CategoryListBox::UserDraw( const UserDrawEvent& rUDEvt ) { const sal_uInt16 nItem = rUDEvt.GetItemId(); if( ListBox::GetEntryFlags(nItem) & ListBoxEntryFlags::DisableSelection ) { Rectangle aOutRect( rUDEvt.GetRect() ); vcl::RenderContext* pDev = rUDEvt.GetRenderContext(); // fill the background Color aColor (GetSettings().GetStyleSettings().GetDialogColor()); pDev->SetFillColor (aColor); pDev->SetLineColor (); pDev->DrawRect(aOutRect); // Erase the four corner pixels to make the rectangle appear rounded. pDev->SetLineColor( GetSettings().GetStyleSettings().GetWindowColor()); pDev->DrawPixel( aOutRect.TopLeft()); pDev->DrawPixel( Point(aOutRect.Right(), aOutRect.Top())); pDev->DrawPixel( Point(aOutRect.Left(), aOutRect.Bottom())); pDev->DrawPixel( Point(aOutRect.Right(), aOutRect.Bottom())); // draw the category title pDev->DrawText (aOutRect, GetEntry(nItem), DrawTextFlags::Center ); } else { DrawEntry( rUDEvt, true, true ); } } IMPL_LINK_NOARG_TYPED(CategoryListBox, implDoubleClickHdl, ListBox&, void) { CaptureMouse(); } void CategoryListBox::MouseButtonUp( const MouseEvent& rMEvt ) { ReleaseMouse(); if( rMEvt.IsLeft() && (rMEvt.GetClicks() == 2) ) { maDoubleClickHdl.Call( *this ); } else { ListBox::MouseButtonUp( rMEvt ); } } class CustomAnimationCreateTabPage : public TabPage { public: CustomAnimationCreateTabPage( vcl::Window* pParent, CustomAnimationCreateDialog* pDialogParent, sal_uInt16 nTabId, const PresetCategoryList& rCategoryList, bool bHasText, bool bIsMotionPath = false ); virtual ~CustomAnimationCreateTabPage(); virtual void dispose() override; PathKind getCreatePathKind() const; CustomAnimationPresetPtr getSelectedPreset() const; double getDuration() const; void setDuration( double fDuration ); bool getIsPreview() const; void setIsPreview( bool bIsPreview ); sal_uInt16 getId() const { return mnId;} bool select( const OUString& rsPresetId ); private: DECL_LINK_TYPED( implSelectHdl, ListBox&, void ); DECL_LINK_TYPED( implDoubleClickHdl, CategoryListBox&, void ); void onSelectEffect(); void clearEffects(); private: VclPtr mpLBEffects; VclPtr mpFTSpeed; VclPtr mpCBSpeed; VclPtr mpCBXPReview; VclPtr mpParent; sal_uInt16 mnId; sal_Int32 mnCurvePathPos; sal_Int32 mnPolygonPathPos; sal_Int32 mnFreeformPathPos; }; struct ImplStlEffectCategorySortHelper { ImplStlEffectCategorySortHelper(); bool operator()( const CustomAnimationPresetPtr& p1, const CustomAnimationPresetPtr& p2 ); private: uno::Reference< i18n::XCollator > mxCollator; }; ImplStlEffectCategorySortHelper::ImplStlEffectCategorySortHelper() { mxCollator = i18n::Collator::create( ::comphelper::getProcessComponentContext() ); const lang::Locale& rLocale = Application::GetSettings().GetLanguageTag().getLocale(); mxCollator->loadDefaultCollator(rLocale, 0); } bool ImplStlEffectCategorySortHelper::operator()( const CustomAnimationPresetPtr& p1, const CustomAnimationPresetPtr& p2 ) { return mxCollator->compareString(p1->getLabel(), p2->getLabel()) == -1; } CustomAnimationCreateTabPage::CustomAnimationCreateTabPage( vcl::Window* pParent, CustomAnimationCreateDialog* pDialogParent, sal_uInt16 nTabId, const PresetCategoryList& rCategoryList, bool bHasText, bool bIsMotionPath ) : TabPage( pParent, "CustomAnimationCreateTab", "modules/simpress/ui/customanimationcreatetab.ui" ) , mpParent( pDialogParent ) , mnId( nTabId ) , mnCurvePathPos( LISTBOX_ENTRY_NOTFOUND ) , mnPolygonPathPos( LISTBOX_ENTRY_NOTFOUND ) , mnFreeformPathPos( LISTBOX_ENTRY_NOTFOUND ) { get( mpLBEffects, "effect_list" ); mpLBEffects->set_height_request( mpLBEffects->GetTextHeight() * 16 ); get( mpFTSpeed, "effect_speed_label" ); get( mpCBSpeed, "effect_speed_list" ); get( mpCBXPReview, "auto_preview" ); fillDurationComboBox(mpCBSpeed); OUString sMotionPathLabel( SD_RESSTR( STR_CUSTOMANIMATION_USERPATH ) ); sal_Int32 nFirstEffect = LISTBOX_ENTRY_NOTFOUND; if( bIsMotionPath ) { mpLBEffects->InsertCategory( sMotionPathLabel ); mnCurvePathPos = nFirstEffect = mpLBEffects->InsertEntry( sdr::GetResourceString(STR_ObjNameSingulCOMBLINE) ); mnPolygonPathPos = mpLBEffects->InsertEntry( sdr::GetResourceString(STR_ObjNameSingulPOLY) ); mnFreeformPathPos = mpLBEffects->InsertEntry( sdr::GetResourceString(STR_ObjNameSingulFREELINE) ); }; PresetCategoryList::const_iterator aCategoryIter( rCategoryList.begin() ); const PresetCategoryList::const_iterator aCategoryEnd( rCategoryList.end() ); while( aCategoryIter != aCategoryEnd ) { PresetCategoryPtr pCategory( *aCategoryIter++ ); if( pCategory.get() ) { mpLBEffects->InsertCategory( pCategory->maLabel ); std::vector< CustomAnimationPresetPtr > aSortedVector(pCategory->maEffects.size()); std::copy( pCategory->maEffects.begin(), pCategory->maEffects.end(), aSortedVector.begin() ); ImplStlEffectCategorySortHelper aSortHelper; std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper ); std::vector< CustomAnimationPresetPtr >::const_iterator aIter( aSortedVector.begin() ); const std::vector< CustomAnimationPresetPtr >::const_iterator aEnd( aSortedVector.end() ); while( aIter != aEnd ) { CustomAnimationPresetPtr pDescriptor = (*aIter++); if( pDescriptor.get() && (bHasText || !pDescriptor->isTextOnly() ) ) { sal_Int32 nPos = mpLBEffects->InsertEntry( pDescriptor->getLabel() ); mpLBEffects->SetEntryData( nPos, static_cast( new CustomAnimationPresetPtr( pDescriptor ) ) ); if( nFirstEffect == LISTBOX_ENTRY_NOTFOUND ) nFirstEffect = nPos; } } } } mpLBEffects->SelectEntryPos( nFirstEffect ); if( nFirstEffect != LISTBOX_ENTRY_NOTFOUND ) onSelectEffect(); mpLBEffects->SetSelectHdl( LINK( this, CustomAnimationCreateTabPage, implSelectHdl ) ); mpLBEffects->SetDoubleClickLink( LINK( this, CustomAnimationCreateTabPage, implDoubleClickHdl ) ); } CustomAnimationCreateTabPage::~CustomAnimationCreateTabPage() { disposeOnce(); } void CustomAnimationCreateTabPage::dispose() { clearEffects(); mpLBEffects.clear(); mpFTSpeed.clear(); mpCBSpeed.clear(); mpCBXPReview.clear(); mpParent.clear(); TabPage::dispose(); } IMPL_LINK_TYPED( CustomAnimationCreateTabPage, implSelectHdl, ListBox&, rControl, void ) { if( &rControl == mpLBEffects ) onSelectEffect(); } IMPL_LINK_TYPED( CustomAnimationCreateTabPage, implDoubleClickHdl, CategoryListBox&, rControl, void ) { if( &rControl == mpLBEffects ) { if( mpLBEffects->GetSelectEntryCount() ) mpParent->EndDialog( RET_OK ); } } void CustomAnimationCreateTabPage::onSelectEffect() { CustomAnimationPresetPtr*p = static_cast< CustomAnimationPresetPtr* >( mpLBEffects->GetSelectEntryData() ); if( !p ) return; CustomAnimationPresetPtr pPreset( *p ); const double fDuration = pPreset->getDuration(); sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND; if( fDuration == 5.0 ) nPos = 0; else if( fDuration == 3.0 ) nPos = 1; else if( fDuration == 2.0 ) nPos = 2; else if( fDuration == 1.0 ) nPos = 3; else if( fDuration == 0.5 ) nPos = 4; mpCBSpeed->SelectEntryPos( nPos ); bool bHasSpeed = pPreset->getDuration() > 0.001; mpCBSpeed->Enable( bHasSpeed ); mpFTSpeed->Enable( bHasSpeed ); if( mpCBXPReview->IsChecked() ) { mpParent->preview( pPreset ); } } void CustomAnimationCreateTabPage::clearEffects() { sal_Int32 nPos = mpLBEffects->GetEntryCount(); while( nPos-- ) delete static_cast< CustomAnimationPresetPtr* >( mpLBEffects->GetEntryData( nPos ) ); mpLBEffects->Clear(); } CustomAnimationPresetPtr CustomAnimationCreateTabPage::getSelectedPreset() const { CustomAnimationPresetPtr pPreset; if( mpLBEffects->GetSelectEntryCount() == 1 ) { void* pEntryData = mpLBEffects->GetSelectEntryData(); if( pEntryData ) pPreset = *static_cast< CustomAnimationPresetPtr* >( pEntryData ); } return pPreset; } PathKind CustomAnimationCreateTabPage::getCreatePathKind() const { PathKind eKind = PathKind::NONE; if( mpLBEffects->GetSelectEntryCount() == 1 ) { const sal_Int32 nPos = mpLBEffects->GetSelectEntryPos(); if( nPos == mnCurvePathPos ) { eKind = PathKind::CURVE; } else if( nPos == mnPolygonPathPos ) { eKind = PathKind::POLYGON; } else if( nPos == mnFreeformPathPos ) { eKind = PathKind::FREEFORM; } } return eKind; } double CustomAnimationCreateTabPage::getDuration() const { sal_Int32 nPos = mpCBSpeed->GetSelectEntryPos(); if( (nPos == LISTBOX_ENTRY_NOTFOUND) || !mpCBSpeed->IsEnabled() ) { CustomAnimationPresetPtr pPreset = getSelectedPreset(); if( pPreset.get() ) return pPreset->getDuration(); } switch( nPos ) { case 0: return 5.0f; case 1: return 3.0f; case 2: return 2.0f; case 3: return 1.0f; case 4: return 0.5f; } return 0.0f; } void CustomAnimationCreateTabPage::setDuration( double fDuration ) { sal_Int32 nPos = 0; if( fDuration < 2.0f ) { if( fDuration < 1.0f ) { nPos = 4; } else { nPos = 3; } } else if( fDuration < 5.0f ) { if( fDuration < 3.0f ) { nPos = 2; } else { nPos = 1; } } mpCBSpeed->SelectEntryPos( nPos ); } bool CustomAnimationCreateTabPage::getIsPreview() const { return mpCBXPReview->IsChecked(); } void CustomAnimationCreateTabPage::setIsPreview( bool bIsPreview ) { mpCBXPReview->Check( bIsPreview ); } bool CustomAnimationCreateTabPage::select( const OUString& rsPresetId ) { sal_Int32 nPos = mpLBEffects->GetEntryCount(); while( nPos-- ) { void* pEntryData = mpLBEffects->GetEntryData( nPos ); if( pEntryData ) { CustomAnimationPresetPtr& pPtr = *static_cast< CustomAnimationPresetPtr* >(pEntryData); if( pPtr.get() && pPtr->getPresetId() == rsPresetId ) { mpLBEffects->SelectEntryPos( nPos ); return true; } } } return false; } CustomAnimationCreateDialog::CustomAnimationCreateDialog( vcl::Window* pParent, CustomAnimationPane* pPane, const std::vector< css::uno::Any >& rTargets, bool bHasText, const OUString& rsPresetId, double fDuration ) : TabDialog( pParent, "CustomAnimationCreate", "modules/simpress/ui/customanimationcreatedialog.ui" ) , mpPane( pPane ) , mrTargets( rTargets ) , mfDuration( fDuration ) { get( mpTabControl, "tabs" ); SdOptions* pOptions = SD_MOD()->GetSdOptions(DOCUMENT_TYPE_IMPRESS); mbIsPreview = pOptions->IsPreviewNewEffects(); mnEntranceId = mpTabControl->GetPageId("entrance"); mnEmphasisId = mpTabControl->GetPageId("emphasis"); mnExitId = mpTabControl->GetPageId("exit"); mnMPathId = mpTabControl->GetPageId("motion_paths"); mnMiscId = mpTabControl->GetPageId("misc_effects"); const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); mpTabPages[ENTRANCE] = VclPtr::Create( mpTabControl, this, mnEntranceId, rPresets.getEntrancePresets(), bHasText ); mpTabControl->SetTabPage( mnEntranceId, mpTabPages[ENTRANCE] ); mpTabPages[EMPHASIS] = VclPtr::Create( mpTabControl, this, mnEmphasisId, rPresets.getEmphasisPresets(), bHasText ); mpTabControl->SetTabPage( mnEmphasisId, mpTabPages[EMPHASIS] ); mpTabPages[EXIT] = VclPtr::Create( mpTabControl, this, mnExitId, rPresets.getExitPresets(), bHasText ); mpTabControl->SetTabPage( mnExitId, mpTabPages[EXIT] ); mpTabPages[MOTIONPATH] = VclPtr::Create( mpTabControl, this, mnMPathId, rPresets.getMotionPathsPresets(), bHasText, true ); mpTabControl->SetTabPage( mnMPathId, mpTabPages[MOTIONPATH] ); mpTabPages[MISCEFFECTS] = VclPtr::Create( mpTabControl, this, mnMiscId, rPresets.getMiscPresets(), bHasText ); mpTabControl->SetTabPage( mnMiscId, mpTabPages[MISCEFFECTS] ); getCurrentPage()->setDuration( mfDuration ); getCurrentPage()->setIsPreview( mbIsPreview ); mpTabControl->SetActivatePageHdl( LINK( this, CustomAnimationCreateDialog, implActivatePagekHdl ) ); mpTabControl->SetDeactivatePageHdl( LINK( this, CustomAnimationCreateDialog, implDeactivatePagekHdl ) ); setPosition(); // select current preset if available if( !rsPresetId.isEmpty() ) { for( sal_uInt16 i = ENTRANCE; i <= MISCEFFECTS; i++ ) { if( mpTabPages[i]->select( rsPresetId ) ) { mpTabControl->SetCurPageId( mpTabPages[i]->getId() ); break; } } } } CustomAnimationCreateDialog::~CustomAnimationCreateDialog() { disposeOnce(); } void CustomAnimationCreateDialog::dispose() { storePosition(); SdOptions* pOptions = SD_MOD()->GetSdOptions(DOCUMENT_TYPE_IMPRESS); pOptions->SetPreviewNewEffects( getCurrentPage()->getIsPreview() ); mpTabPages[ENTRANCE].disposeAndClear(); mpTabPages[EMPHASIS].disposeAndClear(); mpTabPages[EXIT].disposeAndClear(); mpTabPages[MOTIONPATH].disposeAndClear(); mpTabPages[MISCEFFECTS].disposeAndClear(); mpTabControl.clear(); mpPane.clear(); TabDialog::dispose(); } CustomAnimationCreateTabPage* CustomAnimationCreateDialog::getCurrentPage() const { sal_Int16 curPageId = mpTabControl->GetCurPageId(); for( sal_uInt16 i = ENTRANCE; i <= MISCEFFECTS; i++ ) { if( mpTabPages[i]->getId() == curPageId ) return mpTabPages[i]; } return mpTabPages[MOTIONPATH]; } PathKind CustomAnimationCreateDialog::getCreatePathKind() const { return getCurrentPage()->getCreatePathKind(); } CustomAnimationPresetPtr CustomAnimationCreateDialog::getSelectedPreset() const { return getCurrentPage()->getSelectedPreset(); } double CustomAnimationCreateDialog::getSelectedDuration() const { return getCurrentPage()->getDuration(); } IMPL_LINK_NOARG_TYPED(CustomAnimationCreateDialog, implActivatePagekHdl, TabControl*, void) { getCurrentPage()->setDuration( mfDuration ); getCurrentPage()->setIsPreview( mbIsPreview ); } IMPL_LINK_NOARG_TYPED(CustomAnimationCreateDialog, implDeactivatePagekHdl, TabControl *, bool) { mfDuration = getCurrentPage()->getDuration(); mbIsPreview = getCurrentPage()->getIsPreview(); return true; } void CustomAnimationCreateDialog::preview( const CustomAnimationPresetPtr& pPreset ) const { MainSequencePtr pSequence( new MainSequence() ); std::vector< Any >::const_iterator aIter( mrTargets.begin() ); const std::vector< Any >::const_iterator aEnd( mrTargets.end() ); const double fDuration = getSelectedDuration(); bool bFirst = true; while( aIter != aEnd ) { CustomAnimationEffectPtr pNew( pSequence->append( pPreset, (*aIter++), fDuration ) ); if( bFirst ) bFirst = false; else pNew->setNodeType( EffectNodeType::WITH_PREVIOUS ); } mpPane->preview( pSequence->getRootNode() ); } namespace { vcl::Window * lcl_GetTopmostParent( vcl::Window * pWindow ) { vcl::Window * pResult = nullptr; vcl::Window * pCurrent = pWindow ? pWindow->GetParent() : nullptr; while( pCurrent ) { pResult = pCurrent; pCurrent = pCurrent->GetParent(); } return pResult; } } void CustomAnimationCreateDialog::setPosition() { SvtViewOptions aDlgOpt(E_TABDIALOG, OStringToOUString(GetHelpId(), RTL_TEXTENCODING_UTF8)); if ( aDlgOpt.Exists() ) { SetWindowState( OUStringToOString(aDlgOpt.GetWindowState(), RTL_TEXTENCODING_ASCII_US) ); } else { // default position: aligned with right edge of parent vcl::Window * pParent = lcl_GetTopmostParent( this ); if( pParent ) { Point aPos( GetPosPixel()); Size aSize( GetSizePixel()); Size aParentSize( pParent->GetSizePixel()); // right center aPos.setX( aParentSize.getWidth() - aSize.getWidth() ); aPos.setY( (aParentSize.getHeight() - aSize.getHeight()) / 2 ); SetPosPixel( aPos ); } } } void CustomAnimationCreateDialog::storePosition() { // save settings (screen position and current page) SvtViewOptions aDlgOpt(E_TABDIALOG, OStringToOUString(GetHelpId(), RTL_TEXTENCODING_UTF8)); aDlgOpt.SetWindowState(OStringToOUString( GetWindowState(WINDOWSTATE_MASK_POS), RTL_TEXTENCODING_ASCII_US)); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */