/* -*- 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 #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace sd { /** * SdDisplay - Control */ SdDisplay::SdDisplay() : aScale(1, 1) { } SdDisplay::~SdDisplay() { } void SdDisplay::SetBitmapEx( BitmapEx const * pBmpEx ) { if( pBmpEx ) { aBitmapEx = *pBmpEx; } else { const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); const Color aFillColor = rStyles.GetFieldColor(); aBitmapEx.Erase(aFillColor); } } void SdDisplay::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) { rRenderContext.Push(vcl::PushFlags::MAPMODE); rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel)); const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); rRenderContext.SetBackground( Wallpaper( rStyles.GetFieldColor() ) ); rRenderContext.Erase(); Point aPt; Size aSize = GetOutputSizePixel(); Size aBmpSize = aBitmapEx.GetBitmap().GetSizePixel(); aBmpSize.setWidth( static_cast<::tools::Long>( static_cast(aBmpSize.Width()) * static_cast(aScale) ) ); aBmpSize.setHeight( static_cast<::tools::Long>( static_cast(aBmpSize.Height()) * static_cast(aScale) ) ); if( aBmpSize.Width() < aSize.Width() ) aPt.setX( ( aSize.Width() - aBmpSize.Width() ) / 2 ); if( aBmpSize.Height() < aSize.Height() ) aPt.setY( ( aSize.Height() - aBmpSize.Height() ) / 2 ); aBitmapEx.Draw(&rRenderContext, aPt, aBmpSize); rRenderContext.Pop(); } void SdDisplay::SetScale( const Fraction& rFrac ) { aScale = rFrac; } void SdDisplay::SetDrawingArea(weld::DrawingArea* pDrawingArea) { CustomWidgetController::SetDrawingArea(pDrawingArea); Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(147, 87), MapMode(MapUnit::MapAppFont))); pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); SetOutputSizePixel(aSize); } const size_t AnimationWindow::EMPTY_FRAMELIST = std::numeric_limits::max(); /** * AnimationWindow - FloatingWindow */ AnimationWindow::AnimationWindow(SfxBindings* pInBindings, SfxChildWindow *pCW, vcl::Window* pParent) : SfxDockingWindow(pInBindings, pCW, pParent, "DockingAnimation", "modules/simpress/ui/dockinganimation.ui") , m_xCtlDisplay(new SdDisplay) , m_xCtlDisplayWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xCtlDisplay)) , m_xBtnFirst(m_xBuilder->weld_button("first")) , m_xBtnReverse(m_xBuilder->weld_button("prev")) , m_xBtnStop(m_xBuilder->weld_button("stop")) , m_xBtnPlay(m_xBuilder->weld_button("next")) , m_xBtnLast(m_xBuilder->weld_button("last")) , m_xNumFldBitmap(m_xBuilder->weld_spin_button("numbitmap")) , m_xTimeField(m_xBuilder->weld_formatted_spin_button("duration")) , m_xFormatter(new weld::TimeFormatter(*m_xTimeField)) , m_xLbLoopCount(m_xBuilder->weld_combo_box("loopcount")) , m_xBtnGetOneObject(m_xBuilder->weld_button("getone")) , m_xBtnGetAllObjects(m_xBuilder->weld_button("getall")) , m_xBtnRemoveBitmap(m_xBuilder->weld_button("delone")) , m_xBtnRemoveAll(m_xBuilder->weld_button("delall")) , m_xFiCount(m_xBuilder->weld_label("count")) , m_xRbtGroup(m_xBuilder->weld_radio_button("group")) , m_xRbtBitmap(m_xBuilder->weld_radio_button("bitmap")) , m_xFtAdjustment(m_xBuilder->weld_label("alignmentft")) , m_xLbAdjustment(m_xBuilder->weld_combo_box("alignment")) , m_xBtnCreateGroup(m_xBuilder->weld_button("create")) , m_xBtnHelp(m_xBuilder->weld_button("help")) , m_nCurrentFrame(EMPTY_FRAMELIST) , bMovie(false) , bAllObjects(false) { SetText(SdResId(STR_ANIMATION_DIALOG_TITLE)); m_xFormatter->SetDuration(true); m_xFormatter->SetTimeFormat(TimeFieldFormat::F_SEC_CS); m_xFormatter->EnableEmptyField(false); // create new document with page pMyDoc.reset( new SdDrawDocument(DocumentType::Impress, nullptr) ); rtl::Reference pPage = pMyDoc->AllocSdPage(false); pMyDoc->InsertPage(pPage.get()); pControllerItem.reset( new AnimationControllerItem( SID_ANIMATOR_STATE, this, pInBindings ) ); m_xBtnFirst->connect_clicked( LINK( this, AnimationWindow, ClickFirstHdl ) ); m_xBtnReverse->connect_clicked( LINK( this, AnimationWindow, ClickPlayHdl ) ); m_xBtnStop->connect_clicked( LINK( this, AnimationWindow, ClickStopHdl ) ); m_xBtnPlay->connect_clicked( LINK( this, AnimationWindow, ClickPlayHdl ) ); m_xBtnLast->connect_clicked( LINK( this, AnimationWindow, ClickLastHdl ) ); m_xBtnGetOneObject->connect_clicked( LINK( this, AnimationWindow, ClickGetObjectHdl ) ); m_xBtnGetAllObjects->connect_clicked( LINK( this, AnimationWindow, ClickGetObjectHdl ) ); m_xBtnRemoveBitmap->connect_clicked( LINK( this, AnimationWindow, ClickRemoveBitmapHdl ) ); m_xBtnRemoveAll->connect_clicked( LINK( this, AnimationWindow, ClickRemoveBitmapHdl ) ); m_xRbtGroup->connect_toggled( LINK( this, AnimationWindow, ClickRbtHdl ) ); m_xRbtBitmap->connect_toggled( LINK( this, AnimationWindow, ClickRbtHdl ) ); m_xBtnCreateGroup->connect_clicked( LINK( this, AnimationWindow, ClickCreateGroupHdl ) ); m_xBtnHelp->connect_clicked( LINK( this, AnimationWindow, ClickHelpHdl ) ); m_xNumFldBitmap->connect_value_changed( LINK( this, AnimationWindow, ModifyBitmapHdl ) ); m_xTimeField->connect_value_changed( LINK( this, AnimationWindow, ModifyTimeHdl ) ); SetMinOutputSizePixel(GetOptimalSize()); ResetAttrs(); // the animator is empty; no animation group can be created m_xBtnCreateGroup->set_sensitive(false); } AnimationWindow::~AnimationWindow() { disposeOnce(); } void AnimationWindow::dispose() { pControllerItem.reset(); m_FrameList.clear(); m_nCurrentFrame = EMPTY_FRAMELIST; // delete the clones pMyDoc.reset(); m_xCtlDisplayWin.reset(); m_xCtlDisplay.reset(); m_xBtnFirst.reset(); m_xBtnReverse.reset(); m_xBtnStop.reset(); m_xBtnPlay.reset(); m_xBtnLast.reset(); m_xNumFldBitmap.reset(); m_xFormatter.reset(); m_xTimeField.reset(); m_xLbLoopCount.reset(); m_xBtnGetOneObject.reset(); m_xBtnGetAllObjects.reset(); m_xBtnRemoveBitmap.reset(); m_xBtnRemoveAll.reset(); m_xFiCount.reset(); m_xRbtGroup.reset(); m_xRbtBitmap.reset(); m_xFtAdjustment.reset(); m_xLbAdjustment.reset(); m_xBtnCreateGroup.reset(); m_xBtnHelp.reset(); SfxDockingWindow::dispose(); } IMPL_LINK_NOARG(AnimationWindow, ClickFirstHdl, weld::Button&, void) { m_nCurrentFrame = (m_FrameList.empty()) ? EMPTY_FRAMELIST : 0; UpdateControl(); } IMPL_LINK_NOARG(AnimationWindow, ClickStopHdl, weld::Button&, void) { bMovie = false; } IMPL_LINK( AnimationWindow, ClickPlayHdl, weld::Button&, rButton, void ) { ScopeLockGuard aGuard( maPlayLock ); bMovie = true; bool bDisableCtrls = false; size_t const nCount = m_FrameList.size(); bool bReverse = &rButton == m_xBtnReverse.get(); // it is difficult to find it later on bool bRbtGroupEnabled = m_xRbtGroup->get_sensitive(); bool bBtnGetAllObjectsEnabled = m_xBtnGetAllObjects->get_sensitive(); bool bBtnGetOneObjectEnabled = m_xBtnGetOneObject->get_sensitive(); // calculate overall time ::tools::Time aTime( 0 ); ::tools::Long nFullTime; if( m_xRbtBitmap->get_active() ) { for (size_t i = 0; i < nCount; ++i) { aTime += m_FrameList[i].second; } nFullTime = aTime.GetMSFromTime(); } else { nFullTime = nCount * 100; aTime.MakeTimeFromMS( nFullTime ); } // StatusBarManager from 1 second std::unique_ptr pProgress; if( nFullTime >= 1000 ) { bDisableCtrls = true; m_xBtnStop->set_sensitive(true); pProgress.reset(new SfxProgress( nullptr, "Animator:", nFullTime )); // "Animator:" here we should think about something smart } sal_uLong nTmpTime = 0; size_t i = 0; bool bCount = i < nCount; if( bReverse ) { i = nCount - 1; } while( bCount && bMovie ) { // make list and view consistent assert(i < m_FrameList.size()); m_nCurrentFrame = i; UpdateControl(bDisableCtrls); if( m_xRbtBitmap->get_active() ) { ::tools::Time const & rTime = m_FrameList[i].second; m_xFormatter->SetTime( rTime ); sal_uLong nTime = rTime.GetMSFromTime(); WaitInEffect( nTime, nTmpTime, pProgress.get() ); nTmpTime += nTime; } else { WaitInEffect( 100, nTmpTime, pProgress.get() ); nTmpTime += 100; } if( bReverse ) { if (i == 0) { // Terminate loop. bCount = false; } else { --i; } } else { i++; if (i >= nCount) { // Terminate loop. bCount = false; // Move i back into valid range. i = nCount - 1; } } } // to re-enable the controls bMovie = false; if (nCount > 0) { assert(i == m_nCurrentFrame); UpdateControl(); } if( pProgress ) { pProgress.reset(); m_xBtnStop->set_sensitive(false); } m_xRbtGroup->set_sensitive( bRbtGroupEnabled ); m_xBtnGetAllObjects->set_sensitive( bBtnGetAllObjectsEnabled ); m_xBtnGetOneObject->set_sensitive( bBtnGetOneObjectEnabled ); } IMPL_LINK_NOARG(AnimationWindow, ClickLastHdl, weld::Button&, void) { m_nCurrentFrame = (m_FrameList.empty()) ? EMPTY_FRAMELIST : m_FrameList.size() - 1 ; UpdateControl(); } IMPL_LINK_NOARG(AnimationWindow, ClickRbtHdl, weld::Toggleable&, void) { if (m_FrameList.empty() || m_xRbtGroup->get_active()) { m_xTimeField->set_text( OUString() ); m_xTimeField->set_sensitive( false ); m_xLbLoopCount->set_sensitive( false ); } else if (m_xRbtBitmap->get_active()) { sal_uLong n = m_xNumFldBitmap->get_value(); if( n > 0 ) { ::tools::Time const & rTime = m_FrameList[n - 1].second; m_xFormatter->SetTime( rTime ); m_xFormatter->ReFormat(); } m_xTimeField->set_sensitive(true); m_xLbLoopCount->set_sensitive(true); } } IMPL_LINK(AnimationWindow, ClickHelpHdl, weld::Button&, rButton, void) { if (Help* pHelp = Application::GetHelp()) pHelp->Start(OUString::fromUtf8(m_xContainer->get_help_id()), &rButton); } IMPL_LINK( AnimationWindow, ClickGetObjectHdl, weld::Button&, rBtn, void ) { bAllObjects = &rBtn == m_xBtnGetAllObjects.get(); // Code now in AddObj() SfxBoolItem aItem( SID_ANIMATOR_ADD, true ); GetBindings().GetDispatcher()->ExecuteList( SID_ANIMATOR_ADD, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); } IMPL_LINK( AnimationWindow, ClickRemoveBitmapHdl, weld::Button&, rBtn, void ) { SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); SdrObject* pObject; // tdf#95298 check m_nCurrentFrame for EMPTY_FRAMELIST to avoid out-of-bound array access if (&rBtn == m_xBtnRemoveBitmap.get() && EMPTY_FRAMELIST != m_nCurrentFrame) { m_FrameList.erase(m_FrameList.begin() + m_nCurrentFrame); pObject = pPage->GetObj(m_nCurrentFrame); // Through acquisition of the AnimatedGIFs, objects does not need to // exist. if( pObject ) { pObject = pPage->RemoveObject(m_nCurrentFrame); DBG_ASSERT(pObject, "Clone not found during deletion"); SdrObject::Free( pObject ); pPage->RecalcObjOrdNums(); } if (m_nCurrentFrame >= m_FrameList.size()) { // tdf#95298 last frame was deleted, try to use the one before it or go on empty state m_nCurrentFrame = m_FrameList.empty() ? EMPTY_FRAMELIST : m_FrameList.size() - 1; } } else // delete everything { std::unique_ptr xWarn(Application::CreateMessageDialog(GetFrameWeld(), VclMessageType::Warning, VclButtonsType::YesNo, SdResId(STR_ASK_DELETE_ALL_PICTURES))); short nReturn = xWarn->run(); if( nReturn == RET_YES ) { // clear frame list for (size_t i = m_FrameList.size(); i > 0; ) { --i; pObject = pPage->GetObj( i ); if( pObject ) { pObject = pPage->RemoveObject( i ); DBG_ASSERT(pObject, "Clone not found during deletion"); SdrObject::Free( pObject ); //pPage->RecalcObjOrdNums(); } } m_FrameList.clear(); m_nCurrentFrame = EMPTY_FRAMELIST; } } // can we create an animation group if (m_FrameList.empty()) { m_xBtnCreateGroup->set_sensitive(false); // if previous disabled by acquisition of AnimatedGIFs: //m_xRbtBitmap->set_sensitive(true); m_xRbtGroup->set_sensitive(true); } // calculate and set zoom for DisplayWin Fraction aFrac(GetScale()); m_xCtlDisplay->SetScale(aFrac); UpdateControl(); } IMPL_LINK_NOARG(AnimationWindow, ClickCreateGroupHdl, weld::Button&, void) { // Code now in CreatePresObj() SfxBoolItem aItem( SID_ANIMATOR_CREATE, true ); GetBindings().GetDispatcher()->ExecuteList(SID_ANIMATOR_CREATE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); } IMPL_LINK_NOARG(AnimationWindow, ModifyBitmapHdl, weld::SpinButton&, void) { sal_uLong nBmp = m_xNumFldBitmap->get_value(); if (nBmp > m_FrameList.size()) { nBmp = m_FrameList.size(); } m_nCurrentFrame = nBmp - 1; UpdateControl(); } IMPL_LINK_NOARG(AnimationWindow, ModifyTimeHdl, weld::FormattedSpinButton&, void) { sal_uLong nPos = m_xNumFldBitmap->get_value() - 1; ::tools::Time & rTime = m_FrameList[nPos].second; rTime = m_xFormatter->GetTime(); } void AnimationWindow::UpdateControl(bool const bDisableCtrls) { // tdf#95298 check m_nCurrentFrame for EMPTY_FRAMELIST to avoid out-of-bound array access if (!m_FrameList.empty() && EMPTY_FRAMELIST != m_nCurrentFrame) { BitmapEx & rBmp(m_FrameList[m_nCurrentFrame].first); SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); SdrObject *const pObject = pPage->GetObj(m_nCurrentFrame); if( pObject ) { ScopedVclPtrInstance< VirtualDevice > pVD; ::tools::Rectangle aObjRect( pObject->GetCurrentBoundRect() ); Size aObjSize( aObjRect.GetSize() ); Point aOrigin( Point( -aObjRect.Left(), -aObjRect.Top() ) ); MapMode aMap( pVD->GetMapMode() ); aMap.SetMapUnit( MapUnit::Map100thMM ); aMap.SetOrigin( aOrigin ); pVD->SetMapMode( aMap ); pVD->SetOutputSize( aObjSize ); const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); pVD->SetBackground( Wallpaper( rStyles.GetFieldColor() ) ); pVD->SetDrawMode( rStyles.GetHighContrastMode() ? sd::OUTPUT_DRAWMODE_CONTRAST : sd::OUTPUT_DRAWMODE_COLOR ); pVD->Erase(); pObject->SingleObjectPainter( *pVD ); rBmp = pVD->GetBitmapEx( aObjRect.TopLeft(), aObjSize ); } m_xCtlDisplay->SetBitmapEx(&rBmp); } else { m_xCtlDisplay->SetBitmapEx(nullptr); } m_xCtlDisplay->Invalidate(); m_xFiCount->set_label(OUString::number( m_FrameList.size())); if (!m_FrameList.empty() && !bMovie) { size_t nIndex = m_nCurrentFrame + 1; m_xNumFldBitmap->set_value(nIndex); // if there is at least 1 object in the list m_xBtnFirst->set_sensitive(true); m_xBtnReverse->set_sensitive(true); m_xBtnPlay->set_sensitive(true); m_xBtnLast->set_sensitive(true); m_xNumFldBitmap->set_sensitive(true); m_xTimeField->set_sensitive(true); m_xLbLoopCount->set_sensitive(true); m_xBtnRemoveBitmap->set_sensitive(true); m_xBtnRemoveAll->set_sensitive(true); } else { // if no object is in the list m_xBtnFirst->set_sensitive( false ); m_xBtnReverse->set_sensitive( false ); m_xBtnPlay->set_sensitive( false ); m_xBtnLast->set_sensitive( false ); m_xNumFldBitmap->set_sensitive( false ); m_xTimeField->set_sensitive( false ); m_xLbLoopCount->set_sensitive( false ); m_xBtnRemoveBitmap->set_sensitive( false ); m_xBtnRemoveAll->set_sensitive( false ); } if( bMovie && bDisableCtrls ) { m_xBtnGetOneObject->set_sensitive( false ); m_xBtnGetAllObjects->set_sensitive( false ); m_xRbtGroup->set_sensitive( false ); m_xRbtBitmap->set_sensitive( false ); m_xBtnCreateGroup->set_sensitive( false ); m_xFtAdjustment->set_sensitive( false ); m_xLbAdjustment->set_sensitive( false ); } else { // enable 'group object' only if it is not an Animated GIF if (m_FrameList.empty()) { m_xRbtGroup->set_sensitive(true); } m_xRbtBitmap->set_sensitive(true); m_xBtnCreateGroup->set_sensitive(!m_FrameList.empty()); m_xFtAdjustment->set_sensitive(true); m_xLbAdjustment->set_sensitive(true); } ClickRbtHdl(*m_xRbtGroup); } void AnimationWindow::ResetAttrs() { m_xRbtGroup->set_active(true); m_xLbAdjustment->set_active( BA_CENTER ); // LoopCount m_xLbLoopCount->set_active( m_xLbLoopCount->get_count() - 1); UpdateControl(); } void AnimationWindow::WaitInEffect( sal_uLong nMilliSeconds, sal_uLong nTime, SfxProgress* pProgress ) const { sal_uInt64 aEnd = ::tools::Time::GetSystemTicks() + nMilliSeconds; sal_uInt64 aCurrent = ::tools::Time::GetSystemTicks(); while (aCurrent < aEnd) { aCurrent = ::tools::Time::GetSystemTicks(); if( pProgress ) pProgress->SetState( nTime + nMilliSeconds + aCurrent - aEnd ); Application::Reschedule(); if( !bMovie ) return; } } Fraction AnimationWindow::GetScale() { Fraction aFrac; size_t const nCount = m_FrameList.size(); if (nCount > 0) { Size aBmpSize(0, 0); for (size_t i = 0; i < nCount; i++) { BitmapEx const & rBitmap = m_FrameList[i].first; Size aTempSize( rBitmap.GetBitmap().GetSizePixel() ); aBmpSize.setWidth( std::max( aBmpSize.Width(), aTempSize.Width() ) ); aBmpSize.setHeight( std::max( aBmpSize.Height(), aTempSize.Height() ) ); } aBmpSize.AdjustWidth(10 ); aBmpSize.AdjustHeight(10 ); Size aDisplaySize(m_xCtlDisplay->GetOutputSizePixel()); aFrac = Fraction( std::min( static_cast(aDisplaySize.Width()) / static_cast(aBmpSize.Width()), static_cast(aDisplaySize.Height()) / static_cast(aBmpSize.Height()) ) ); } return aFrac; } void AnimationWindow::Resize() { SfxDockingWindow::Resize(); Fraction aFrac(GetScale()); m_xCtlDisplay->SetScale(aFrac); } bool AnimationWindow::Close() { if( maPlayLock.isLocked() ) { return false; } else { SfxBoolItem aItem( SID_ANIMATION_OBJECTS, false ); GetBindings().GetDispatcher()->ExecuteList( SID_ANIMATION_OBJECTS, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); SfxDockingWindow::Close(); return true; } } void AnimationWindow::AddObj (::sd::View& rView ) { // finish text entry mode to ensure that bitmap is identical with object if( rView.IsTextEdit() ) rView.SdrEndTextEdit(); // clone object(s) and insert the clone(s) into the list const SdrMarkList& rMarkList = rView.GetMarkedObjectList(); const size_t nMarkCount = rMarkList.GetMarkCount(); SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); const size_t nCloneCount = pPage->GetObjCount(); if (nMarkCount <= 0) return; // If it is ONE animation object or one group object, which was // 'individually taken', we insert the objects separately bool bAnimObj = false; if( nMarkCount == 1 ) { SdrMark* pMark = rMarkList.GetMark(0); SdrObject* pObject = pMark->GetMarkedSdrObj(); SdAnimationInfo* pAnimInfo = SdDrawDocument::GetAnimationInfo( pObject ); SdrInventor nInv = pObject->GetObjInventor(); SdrObjKind nId = pObject->GetObjIdentifier(); // Animated Bitmap (GIF) if( nInv == SdrInventor::Default && nId == SdrObjKind::Graphic && static_cast( pObject )->IsAnimated() ) { const SdrGrafObj* pGrafObj = static_cast(pObject); Graphic aGraphic( pGrafObj->GetTransformedGraphic() ); sal_uInt16 nCount = 0; if( aGraphic.IsAnimated() ) nCount = aGraphic.GetAnimation().Count(); if( nCount > 0 ) { const Animation aAnimation( aGraphic.GetAnimation() ); for( sal_uInt16 i = 0; i < nCount; i++ ) { const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i ); // LoopCount if( i == 0 ) { sal_uInt32 nLoopCount = aAnimation.GetLoopCount(); if( !nLoopCount ) // endless m_xLbLoopCount->set_active( m_xLbLoopCount->get_count() - 1); else m_xLbLoopCount->set_active_text(OUString::number( nLoopCount ) ); } ::tools::Long nTime = rAnimationBitmap.mnWait; ::tools::Time aTime( 0, 0, nTime / 100, nTime % 100 ); size_t nIndex = m_nCurrentFrame + 1; m_FrameList.insert( m_FrameList.begin() + nIndex, ::std::make_pair(rAnimationBitmap.maBitmapEx, aTime)); // increment => next one inserted after this one ++m_nCurrentFrame; } // if an animated GIF is taken, only such one can be created m_xRbtBitmap->set_active(true); m_xRbtGroup->set_sensitive( false ); bAnimObj = true; } } else if( bAllObjects || ( pAnimInfo && pAnimInfo->mbIsMovie ) ) { // several objects SdrObjList* pObjList = static_cast(pObject)->GetSubList(); for( size_t nObject = 0; nObject < pObjList->GetObjCount(); ++nObject ) { SdrObject* pSnapShot(pObjList->GetObj(nObject)); BitmapEx aBitmapEx(SdrExchangeView::GetObjGraphic(*pSnapShot).GetBitmapEx()); size_t nIndex = m_nCurrentFrame + 1; m_FrameList.insert( m_FrameList.begin() + nIndex, ::std::make_pair(aBitmapEx, m_xFormatter->GetTime())); // increment => next one inserted after this one ++m_nCurrentFrame; // Clone pPage->InsertObject( pSnapShot->CloneSdrObject(pPage->getSdrModelFromSdrPage()), m_nCurrentFrame); } bAnimObj = true; } } // also one single animated object if( !bAnimObj && !( bAllObjects && nMarkCount > 1 ) ) { BitmapEx aBitmapEx(rView.GetAllMarkedGraphic().GetBitmapEx()); ::tools::Time aTime( m_xFormatter->GetTime() ); size_t nIndex = m_nCurrentFrame + 1; m_FrameList.insert( m_FrameList.begin() + nIndex, ::std::make_pair(aBitmapEx, aTime)); } // one single object if( nMarkCount == 1 && !bAnimObj ) { SdrMark* pMark = rMarkList.GetMark(0); SdrObject* pObject = pMark->GetMarkedSdrObj(); SdrObject* pClone(pObject->CloneSdrObject(pPage->getSdrModelFromSdrPage())); size_t nIndex = m_nCurrentFrame + 1; pPage->InsertObject(pClone, nIndex); } // several objects: group the clones else if (nMarkCount > 1) { // take objects separately if( bAllObjects ) { for( size_t nObject= 0; nObject < nMarkCount; ++nObject ) { // Clone SdrObject* pObject(rMarkList.GetMark(nObject)->GetMarkedSdrObj()); BitmapEx aBitmapEx(SdrExchangeView::GetObjGraphic(*pObject).GetBitmapEx()); size_t nIndex = m_nCurrentFrame + 1; m_FrameList.insert( m_FrameList.begin() + nIndex, ::std::make_pair(aBitmapEx, m_xFormatter->GetTime())); // increment => next one inserted after this one ++m_nCurrentFrame; pPage->InsertObject( pObject->CloneSdrObject(pPage->getSdrModelFromSdrPage()), m_nCurrentFrame); } bAnimObj = true; // that we don't change again } else { SdrObjGroup* pCloneGroup = new SdrObjGroup(rView.getSdrModelFromSdrView()); SdrObjList* pObjList = pCloneGroup->GetSubList(); for (size_t nObject= 0; nObject < nMarkCount; ++nObject) { pObjList->InsertObject( rMarkList.GetMark(nObject)->GetMarkedSdrObj()->CloneSdrObject( pPage->getSdrModelFromSdrPage())); } size_t nIndex = m_nCurrentFrame + 1; pPage->InsertObject(pCloneGroup, nIndex); } } if( !bAnimObj ) { ++m_nCurrentFrame; } // if there was nothing in the animator before but now is something // there, we can create an animation group if (nCloneCount == 0 && !m_FrameList.empty()) { m_xBtnCreateGroup->set_sensitive(true); } // calculate and set zoom for DisplayWin Fraction aFrac( GetScale() ); m_xCtlDisplay->SetScale(aFrac); UpdateControl(); } void AnimationWindow::CreateAnimObj (::sd::View& rView ) { vcl::Window* pOutWin = rView.GetFirstOutputDevice()->GetOwnerWindow(); // GetWin( 0 ); DBG_ASSERT( pOutWin, "Window does not exist!" ); // find window center const MapMode aMap100( MapUnit::Map100thMM ); Size aMaxSizeLog; Size aMaxSizePix; Size aTemp( pOutWin->GetOutputSizePixel() ); const Point aWindowCenter( pOutWin->PixelToLogic( Point( aTemp.Width() >> 1, aTemp.Height() >> 1 ) ) ); const OutputDevice* pDefDev = Application::GetDefaultDevice(); const size_t nCount = m_FrameList.size(); BitmapAdjustment eBA = static_cast(m_xLbAdjustment->get_active()); // find biggest bitmap for (size_t i = 0; i < nCount; ++i) { const BitmapEx& rBmpEx = m_FrameList[i].first; const Graphic aGraphic( rBmpEx ); Size aTmpSizeLog; const Size aTmpSizePix( rBmpEx.GetSizePixel() ); if ( aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) aTmpSizeLog = pDefDev->PixelToLogic( aGraphic.GetPrefSize(), aMap100 ); else aTmpSizeLog = OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), aMap100 ); aMaxSizeLog.setWidth( std::max( aMaxSizeLog.Width(), aTmpSizeLog.Width() ) ); aMaxSizeLog.setHeight( std::max( aMaxSizeLog.Height(), aTmpSizeLog.Height() ) ); aMaxSizePix.setWidth( std::max( aMaxSizePix.Width(), aTmpSizePix.Width() ) ); aMaxSizePix.setHeight( std::max( aMaxSizePix.Height(), aTmpSizePix.Height() ) ); } SdrPageView* pPV = rView.GetSdrPageView(); if( m_xRbtBitmap->get_active() ) { // create bitmap group (Animated GIF) Animation aAnimation; Point aPt; for (size_t i = 0; i < nCount; ++i) { ::tools::Time const & rTime = m_FrameList[i].second; ::tools::Long nTime = rTime.GetNanoSec(); nTime += rTime.GetSec() * 100; BitmapEx const & rBitmapEx = m_FrameList[i].first; // calculate offset for the specified direction const Size aBitmapSize( rBitmapEx.GetSizePixel() ); switch( eBA ) { case BA_LEFT_UP: break; case BA_LEFT: aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); break; case BA_LEFT_DOWN: aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); break; case BA_UP: aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); break; case BA_CENTER: aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); break; case BA_DOWN: aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); break; case BA_RIGHT_UP: aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); break; case BA_RIGHT: aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); break; case BA_RIGHT_DOWN: aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); break; } // find LoopCount (number of passes) AnimationBitmap aAnimationBitmap; sal_uInt32 nLoopCount = 0; sal_Int32 nPos = m_xLbLoopCount->get_active(); if( nPos != -1 && nPos != m_xLbLoopCount->get_count() - 1 ) // endless nLoopCount = m_xLbLoopCount->get_active_text().toUInt32(); aAnimationBitmap.maBitmapEx = rBitmapEx; aAnimationBitmap.maPositionPixel = aPt; aAnimationBitmap.maSizePixel = aBitmapSize; aAnimationBitmap.mnWait = nTime; aAnimationBitmap.meDisposal = Disposal::Back; aAnimationBitmap.mbUserInput = false; aAnimation.Insert( aAnimationBitmap ); aAnimation.SetDisplaySizePixel( aMaxSizePix ); aAnimation.SetLoopCount( nLoopCount ); } SdrGrafObj* pGrafObj = new SdrGrafObj( rView.getSdrModelFromSdrView(), Graphic(aAnimation)); const Point aOrg( aWindowCenter.X() - ( aMaxSizeLog.Width() >> 1 ), aWindowCenter.Y() - ( aMaxSizeLog.Height() >> 1 ) ); pGrafObj->SetLogicRect( ::tools::Rectangle( aOrg, aMaxSizeLog ) ); rView.InsertObjectAtView( pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER); } else { // calculate offset for the specified direction Size aOffset; SdrObject * pClone = nullptr; SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); for (size_t i = 0; i < nCount; ++i) { pClone = pPage->GetObj(i); ::tools::Rectangle aRect( pClone->GetSnapRect() ); switch( eBA ) { case BA_LEFT_UP: break; case BA_LEFT: aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); break; case BA_LEFT_DOWN: aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); break; case BA_UP: aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); break; case BA_CENTER: aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); break; case BA_DOWN: aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); break; case BA_RIGHT_UP: aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); break; case BA_RIGHT: aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); break; case BA_RIGHT_DOWN: aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); break; } // Unfortunately, SetSnapRect is not implemented for ellipses !!! Point aMovePt( aWindowCenter + Point( aOffset.Width(), aOffset.Height() ) - aRect.TopLeft() ); Size aMoveSize( aMovePt.X(), aMovePt.Y() ); pClone->NbcMove( aMoveSize ); } // #i42894# Caution(!) variable pPage looks right, but it is a page from the local // document the dialog is using (!), so get the target page from the target view SdPage* pTargetSdPage = dynamic_cast< SdPage* >(rView.GetSdrPageView() ? rView.GetSdrPageView()->GetPage() : nullptr); if(pTargetSdPage) { // create animation group SdrObjGroup* pGroup = new SdrObjGroup(rView.getSdrModelFromSdrView()); SdrObjList* pObjList = pGroup->GetSubList(); for (size_t i = 0; i < nCount; ++i) { // the clone remains in the animation; we insert a clone of the // clone into the group pClone = pPage->GetObj(i); SdrObject* pCloneOfClone(pClone->CloneSdrObject(pTargetSdPage->getSdrModelFromSdrPage())); //SdrObject* pCloneOfClone = pPage->GetObj(i)->Clone(); pObjList->InsertObject(pCloneOfClone); } // until now the top left corner of the group is in the window center; // correct the position by half of the size of the group aTemp = aMaxSizeLog; aTemp.setHeight( - aTemp.Height() / 2 ); aTemp.setWidth( - aTemp.Width() / 2 ); pGroup->NbcMove(aTemp); // #i42894# create needed SMIL stuff and move child objects to page directly (see // comments at EffectMigration::CreateAnimatedGroup why this has to be done). EffectMigration::CreateAnimatedGroup(*pGroup, *pTargetSdPage); // #i42894# if that worked, delete the group again if(!pGroup->GetSubList()->GetObjCount()) { // always use SdrObject::Free(...) for SdrObjects (!) SdrObject* pTemp(pGroup); SdrObject::Free(pTemp); } } } ClickFirstHdl(*m_xBtnFirst); } void AnimationWindow::DataChanged( const DataChangedEvent& rDCEvt ) { SfxDockingWindow::DataChanged( rDCEvt ); if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) { UpdateControl(); } } /** * ControllerItem for Animator */ AnimationControllerItem::AnimationControllerItem( sal_uInt16 _nId, AnimationWindow* pAnimWin, SfxBindings* _pBindings) : SfxControllerItem( _nId, *_pBindings ), pAnimationWin( pAnimWin ) { } void AnimationControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, const SfxPoolItem* pItem ) { if( eState >= SfxItemState::DEFAULT && nSId == SID_ANIMATOR_STATE ) { const SfxUInt16Item* pStateItem = dynamic_cast< const SfxUInt16Item*>( pItem ); assert(pStateItem); //SfxUInt16Item expected if (pStateItem) { sal_uInt16 nState = pStateItem->GetValue(); pAnimationWin->m_xBtnGetOneObject->set_sensitive( nState & 1 ); pAnimationWin->m_xBtnGetAllObjects->set_sensitive( nState & 2 ); } } } } // end of namespace sd /* vim:set shiftwidth=4 softtabstop=4 expandtab: */