diff --git a/officecfg/registry/schema/org/openoffice/Office/Impress.xcs b/officecfg/registry/schema/org/openoffice/Office/Impress.xcs index c4de6da82393..93dbfc342428 100644 --- a/officecfg/registry/schema/org/openoffice/Office/Impress.xcs +++ b/officecfg/registry/schema/org/openoffice/Office/Impress.xcs @@ -639,6 +639,44 @@ false + + + Specifies the timing display mode for rehearsal: 0=Current slide/Total time, 1=Current slide only, 2=Total time only. + + + + + + Minimum value is 0 (Current slide/Total time). + + + + + Maximum value is 2 (Total time only). + + + + 0 + + + + Specifies the position where the rehearsal timer will be displayed: 0=Top left, 1=Bottom left, 2=Top center, 3=Bottom center, 4=Top right, 5=Bottom right. + + + + + + Minimum value is 0 (Top left). + + + + + Maximum value is 5 (Bottom right). + + + + 0 + diff --git a/sd/inc/drawdoc.hxx b/sd/inc/drawdoc.hxx index d4dad173b7fb..62d9d32849ac 100644 --- a/sd/inc/drawdoc.hxx +++ b/sd/inc/drawdoc.hxx @@ -113,6 +113,9 @@ namespace sd bool mbShowPauseLogo; bool mbStartCustomShow; bool mbInteractive; + bool mnRehearseTimerGlobalSetting; // 是否使用全局计时器设置 + sal_Int32 mnTimerMode; // 计时器显示模式: 0=当前/总时间, 1=当前幻灯片, 2=总时间 + sal_Int32 mnTimerPosition; // 计时器位置: 0=左上, 1=左下, 2=中上, 3=中下, 4=右上, 5=右下 PresentationSettings(); }; diff --git a/sd/inc/sdattr.hrc b/sd/inc/sdattr.hrc index 3bc08b04de81..141790367da5 100644 --- a/sd/inc/sdattr.hrc +++ b/sd/inc/sdattr.hrc @@ -56,8 +56,11 @@ class XColorItem; #define ATTR_PRESENT_SHOW_PAUSELOGO ATTR_PRESENT_START + 14 #define ATTR_PRESENT_DISPLAY TypedWhichId(ATTR_PRESENT_START + 15) #define ATTR_PRESENT_INTERACTIVE ATTR_PRESENT_START + 16 +#define ATTR_PRESENT_TIMER_GLOBAL_SETTING TypedWhichId(ATTR_PRESENT_START + 17) +#define ATTR_PRESENT_TIMING_MODE TypedWhichId(ATTR_PRESENT_START + 18) +#define ATTR_PRESENT_TIMER_POSITION TypedWhichId(ATTR_PRESENT_START + 19) -#define ATTR_PRESENT_END ATTR_PRESENT_INTERACTIVE +#define ATTR_PRESENT_END ATTR_PRESENT_TIMER_POSITION // animation attributes #define ATTR_ANIMATION_START ATTR_PRESENT_END + 1 diff --git a/sd/source/core/drawdoc.cxx b/sd/source/core/drawdoc.cxx index a07d24e4ffcb..8e7d1e05e679 100644 --- a/sd/source/core/drawdoc.cxx +++ b/sd/source/core/drawdoc.cxx @@ -112,8 +112,35 @@ PresentationSettings::PresentationSettings() mnPauseTimeout( 0 ), mbShowPauseLogo( false ), mbStartCustomShow( false ), - mbInteractive( true ) + mbInteractive( true ), + mnRehearseTimerGlobalSetting(true), // 默认排练计时器全局设置为True + mnTimerMode( 0 ), // 默认显示当前/总时间 + mnTimerPosition( 0 ) // 默认位置左上 { + // 从全局配置文件读取排练计时器设置 + if (mnRehearseTimerGlobalSetting) + { + try + { + mnTimerMode = officecfg::Office::Impress::Misc::Start::RehearseTimingsMode::get(); + mnTimerPosition = officecfg::Office::Impress::Misc::Start::RehearseTimingsPosition::get(); + + // 校验范围,防止配置文件被手动修改为非法值 + if (mnTimerMode < 0 || mnTimerMode > 2) + { + mnTimerMode = 0; + } + + if (mnTimerPosition < 0 || mnTimerPosition > 5) + { + mnTimerPosition = 0; + } + } + catch (const css::uno::Exception& e) + { + // 保持默认值 0, 0 + } + } } SdDrawDocument::SdDrawDocument(DocumentType eType, SfxObjectShell* pDrDocSh) diff --git a/sd/source/ui/dlg/present.cxx b/sd/source/ui/dlg/present.cxx index d3d79fba41f4..de2bd898c8df 100644 --- a/sd/source/ui/dlg/present.cxx +++ b/sd/source/ui/dlg/present.cxx @@ -79,6 +79,9 @@ SdStartPresentationDlg::SdStartPresentationDlg(weld::Window* pWindow, const SfxI , m_xAllMonitors(m_xBuilder->weld_label(u"allmonitors_str"_ustr)) , m_xMonitorExternal(m_xBuilder->weld_label(u"externalmonitor_str"_ustr)) , m_xExternal(m_xBuilder->weld_label(u"external_str"_ustr)) + , m_xCbxTimerGlobalSettings(m_xBuilder->weld_check_button(u"globalrehearsetimings"_ustr) ) + , m_xLbTimerMode(m_xBuilder->weld_combo_box(u"timingmode_cb"_ustr)) + , m_xLbTimerPosition(m_xBuilder->weld_combo_box(u"timerposition_cb"_ustr)) { m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); m_xFormatter->EnableEmptyField(false); @@ -180,6 +183,45 @@ SdStartPresentationDlg::SdStartPresentationDlg(weld::Window* pWindow, const SfxI m_xCbxInteractiveMode->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_INTERACTIVE ) ).GetValue() ); + // 初始化全局设置复选框状态 + bool bGlobalSettings = static_cast( rOutAttrs.Get( ATTR_PRESENT_TIMER_GLOBAL_SETTING ) ).GetValue(); + m_xCbxTimerGlobalSettings->set_active(bGlobalSettings); + + // 初始化计时器设置 + sal_Int32 nTimerMode = 0; // Default to "Current slide / Total time" (matches drawdoc.cxx) + sal_Int32 nTimerPosition = 0; // Default to "Top left" (matches drawdoc.cxx) + + // 如果启用全局设置,则从配置中读取,否则使用文档中的值 + if (bGlobalSettings) { + nTimerMode = officecfg::Office::Impress::Misc::Start::RehearseTimingsMode::get(); + nTimerPosition = officecfg::Office::Impress::Misc::Start::RehearseTimingsPosition::get(); + } else { + // 从配置文件读取计时器设置 + const SfxPoolItem* pItem; + if (rOutAttrs.GetItemState(ATTR_PRESENT_TIMING_MODE, true, &pItem) == SfxItemState::SET) { + nTimerMode = static_cast(pItem)->GetValue(); + } + + if (rOutAttrs.GetItemState(ATTR_PRESENT_TIMER_POSITION, true, &pItem) == SfxItemState::SET) { + nTimerPosition = static_cast(pItem)->GetValue(); + } + } + + // 校验计时器设置范围,如果超出范围,则重置为默认值 + if (nTimerMode < 0 || nTimerMode > 2) + nTimerMode = 0; // Reset to default if invalid + if (nTimerPosition < 0 || nTimerPosition > 5) + nTimerPosition = 0; // Reset to default if invalid + + // 计时器设置赋值 + if (m_xLbTimerMode) { + m_xLbTimerMode->set_active(nTimerMode); + } + + if (m_xLbTimerPosition) { + m_xLbTimerPosition->set_active(nTimerPosition); + } + InitMonitorSettings(); ChangeRangeHdl(*m_xRbtCustomshow); @@ -211,6 +253,13 @@ short SdStartPresentationDlg::run() m_xCbxShowNavigationButton->get_active(), batch); officecfg::Office::Impress::Layout::Display::NavigationBtnScale::set( m_xLbNavigationButtonsSize->get_active(), batch); + bool bGlobalSettings = m_xCbxTimerGlobalSettings->get_active(); + if (bGlobalSettings){ + officecfg::Office::Impress::Misc::Start::RehearseTimingsMode::set( + m_xLbTimerMode->get_active(), batch); + officecfg::Office::Impress::Misc::Start::RehearseTimingsPosition::set( + m_xLbTimerPosition->get_active(), batch); + } #ifdef ENABLE_SDREMOTE officecfg::Office::Impress::Misc::Start::EnableSdremote::set(m_xCbxEnableRemote->get_active(), batch); @@ -340,6 +389,26 @@ void SdStartPresentationDlg::GetAttr( SfxItemSet& rAttr ) rAttr.Put( SfxUInt32Item ( ATTR_PRESENT_PAUSE_TIMEOUT, m_xFormatter->GetTime().GetMSFromTime() / 1000 ) ); rAttr.Put( SfxBoolItem ( ATTR_PRESENT_SHOW_PAUSELOGO, m_xCbxAutoLogo->get_active() ) ); rAttr.Put( SfxBoolItem ( ATTR_PRESENT_INTERACTIVE, m_xCbxInteractiveMode->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_TIMER_GLOBAL_SETTING, m_xCbxTimerGlobalSettings->get_active() ) ); + + // 保存计时器的属性值 + sal_Int32 nTimingMode = 0; + sal_Int32 nTimerPosition = 0; + + // 从下拉列表中获取计时器的属性值 + if (m_xLbTimerMode) + nTimingMode = m_xLbTimerMode->get_active(); + if (m_xLbTimerPosition) + nTimerPosition = m_xLbTimerPosition->get_active(); + + // 校验计时器属性值的合法性 + if (nTimingMode < 0 || nTimingMode > 2) + nTimingMode = 0; // Reset to default if invalid + if (nTimerPosition < 0 || nTimerPosition > 5) + nTimerPosition = 0; // Reset to default if invalid + + rAttr.Put( SfxInt32Item ( ATTR_PRESENT_TIMING_MODE, nTimingMode ) ); + rAttr.Put( SfxInt32Item ( ATTR_PRESENT_TIMER_POSITION, nTimerPosition ) ); int nPos = m_xLBMonitor->get_active(); if (nPos != -1) diff --git a/sd/source/ui/func/fusldlg.cxx b/sd/source/ui/func/fusldlg.cxx index 9198fa351194..09a2936e4fd8 100644 --- a/sd/source/ui/func/fusldlg.cxx +++ b/sd/source/ui/func/fusldlg.cxx @@ -108,6 +108,9 @@ void FuSlideShowDlg::DoExecute( SfxRequest& ) aDlgSet.Put( SfxUInt32Item( ATTR_PRESENT_PAUSE_TIMEOUT, rPresentationSettings.mnPauseTimeout ) ); aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_SHOW_PAUSELOGO, rPresentationSettings.mbShowPauseLogo ) ); aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_INTERACTIVE, rPresentationSettings.mbInteractive ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_TIMER_GLOBAL_SETTING, rPresentationSettings.mnRehearseTimerGlobalSetting ) ); + aDlgSet.Put( SfxInt32Item( ATTR_PRESENT_TIMING_MODE, rPresentationSettings.mnTimerMode ) ); + aDlgSet.Put( SfxInt32Item( ATTR_PRESENT_TIMER_POSITION, rPresentationSettings.mnTimerPosition ) ); SdOptions* pOptions = SdModule::get()->GetSdOptions(DocumentType::Impress); aDlgSet.Put( SfxInt32Item( ATTR_PRESENT_DISPLAY, pOptions->GetDisplay() ) ); @@ -220,6 +223,27 @@ void FuSlideShowDlg::DoExecute( SfxRequest& ) rPresentationSettings.mbInteractive = bValue; } + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_TIMER_GLOBAL_SETTING, SfxBoolItem ); + if ( bValue != rPresentationSettings.mnRehearseTimerGlobalSetting ) { + bValuesChanged = true; + rPresentationSettings.mnRehearseTimerGlobalSetting = bValue; + } + + // Handle timing mode setting + nValue32 = ITEMVALUE( aDlgSet, ATTR_PRESENT_TIMING_MODE, SfxInt32Item ); + if ( nValue32 != rPresentationSettings.mnTimerMode ) + { + bValuesChanged = true; + rPresentationSettings.mnTimerMode = nValue32; + } + + nValue32 = ITEMVALUE( aDlgSet, ATTR_PRESENT_TIMER_POSITION, SfxInt32Item ); + if ( nValue32 != rPresentationSettings.mnTimerPosition ) + { + bValuesChanged = true; + rPresentationSettings.mnTimerPosition = nValue32; + } + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_PEN, SfxBoolItem ); if ( bValue != rPresentationSettings.mbMouseAsPen ) { diff --git a/sd/source/ui/inc/present.hxx b/sd/source/ui/inc/present.hxx index 36d746958263..8920fbb50db8 100644 --- a/sd/source/ui/inc/present.hxx +++ b/sd/source/ui/inc/present.hxx @@ -72,6 +72,11 @@ private: std::unique_ptr m_xMonitorExternal; std::unique_ptr m_xExternal; + // Rehearse timings controls + std::unique_ptr m_xCbxTimerGlobalSettings; + std::unique_ptr m_xLbTimerMode; + std::unique_ptr m_xLbTimerPosition; + DECL_LINK(ChangeRemoteHdl, weld::Toggleable&, void); DECL_LINK(ChangeRangeHdl, weld::Toggleable&, void); DECL_LINK(ClickWindowPresentationHdl, weld::Toggleable&, void); diff --git a/sd/source/ui/slideshow/slideshow.cxx b/sd/source/ui/slideshow/slideshow.cxx index 49cb4f489bf5..62f3a01d1e26 100644 --- a/sd/source/ui/slideshow/slideshow.cxx +++ b/sd/source/ui/slideshow/slideshow.cxx @@ -123,6 +123,8 @@ static std::span ImplGetPresentationPropertyMap() { u"Pause"_ustr, ATTR_PRESENT_PAUSE_TIMEOUT, ::cppu::UnoType::get(), 0, 0 }, { u"StartWithNavigator"_ustr, ATTR_PRESENT_NAVIGATOR, cppu::UnoType::get(), 0, 0 }, { u"UsePen"_ustr, ATTR_PRESENT_PEN, cppu::UnoType::get(), 0, 0 }, + { u"TimingMode"_ustr, ATTR_PRESENT_TIMING_MODE, ::cppu::UnoType::get(), 0, 0 }, + { u"TimerPosition"_ustr, ATTR_PRESENT_TIMER_POSITION, ::cppu::UnoType::get(), 0, 0 }, }; return aPresentationPropertyMap_Impl; @@ -529,6 +531,37 @@ void SAL_CALL SlideShow::setPropertyValue( const OUString& aPropertyName, const } break; } + // 添加计时模式设置 + case ATTR_PRESENT_TIMING_MODE: + { + sal_Int32 nValue = 0; + if( (aValue >>= nValue) && (nValue >= 0 && nValue <= 2) ) + { + bIllegalArgument = false; + if( rPresSettings.mnTimerMode != nValue ) + { + bValuesChanged = true; + rPresSettings.mnTimerMode = nValue; + } + } + break; + } + + // 添加计时器位置设置 + case ATTR_PRESENT_TIMER_POSITION: + { + sal_Int32 nValue = 0; + if( (aValue >>= nValue) && (nValue >= 0 && nValue <= 5) ) + { + bIllegalArgument = false; + if( rPresSettings.mnTimerPosition != nValue ) + { + bValuesChanged = true; + rPresSettings.mnTimerPosition = nValue; + } + } + break; + } default: throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast(this)); @@ -601,6 +634,10 @@ Any SAL_CALL SlideShow::getPropertyValue( const OUString& PropertyName ) SdOptions* pOptions = SdModule::get()->GetSdOptions(DocumentType::Impress); return Any(pOptions->GetDisplay()); } + case ATTR_PRESENT_TIMING_MODE: + return Any( rPresSettings.mnTimerMode ); + case ATTR_PRESENT_TIMER_POSITION: + return Any( rPresSettings.mnTimerPosition ); default: throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast(this)); @@ -835,7 +872,16 @@ void SAL_CALL SlideShow::end() void SAL_CALL SlideShow::rehearseTimings() { - Sequence< PropertyValue > aArguments{ comphelper::makePropertyValue(u"RehearseTimings"_ustr, true) }; + // Get the presentation settings to retrieve timing mode and timer position + const PresentationSettings& rSettings = mpDoc->getPresentationSettings(); + + // Create arguments array with RehearseTimings, TimingMode, and TimerPosition + Sequence< PropertyValue > aArguments{ + comphelper::makePropertyValue(u"RehearseTimings"_ustr, true), + comphelper::makePropertyValue(u"TimingMode"_ustr, rSettings.mnTimerMode), + comphelper::makePropertyValue(u"TimerPosition"_ustr, rSettings.mnTimerPosition) + }; + startWithArguments( aArguments ); } diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx b/sd/source/ui/slideshow/slideshowimpl.cxx index fd7384f379bb..d04a39c25afe 100644 --- a/sd/source/ui/slideshow/slideshowimpl.cxx +++ b/sd/source/ui/slideshow/slideshowimpl.cxx @@ -554,6 +554,8 @@ SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, , mbWasPaused(false) , mbInputFreeze(false) , mbActive(false) +, mnTimerMode(0) // 默认显示当前/总时间 +, mnTimerPosition(0) // 默认位置左上 , maPresSettings( pDoc->getPresentationSettings() ) , mnUserPaintColor( 0x80ff0000L ) , mbUsePen(false) @@ -1061,6 +1063,8 @@ bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings ) { maPresSettings = *pPresSettings; mbRehearseTimings = pPresSettings->mbRehearseTimings; + mnTimerMode = pPresSettings->mnTimerMode; + mnTimerPosition = pPresSettings->mnTimerPosition; } OUString aPresSlide( maPresSettings.maPresPage ); @@ -1220,6 +1224,10 @@ bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings ) if (mbRehearseTimings) { aProperties.emplace_back( "RehearseTimings" , -1, Any(true), beans::PropertyState_DIRECT_VALUE ); + aProperties.emplace_back( "TimingMode" , + -1, Any(mnTimerMode), beans::PropertyState_DIRECT_VALUE ); + aProperties.emplace_back( "TimerPosition" , + -1, Any(mnTimerPosition), beans::PropertyState_DIRECT_VALUE ); } bRet = startShowImpl( Sequence( @@ -3557,6 +3565,8 @@ PresentationSettingsEx::PresentationSettingsEx( const PresentationSettingsEx& r , mbRehearseTimings(r.mbRehearseTimings) , mbPreview(r.mbPreview) , mpParentWindow( nullptr ) +, mnTimerMode(r.mnTimerMode) +, mnTimerPosition(r.mnTimerPosition) { } @@ -3565,14 +3575,22 @@ PresentationSettingsEx::PresentationSettingsEx( PresentationSettings const & r ) , mbRehearseTimings(false) , mbPreview(false) , mpParentWindow(nullptr) +, mnTimerMode(r.mnTimerMode) // 从基础设置获取计时器显示模式 +, mnTimerPosition(r.mnTimerPosition) // 从基础设置获取计时器位置 { } void PresentationSettingsEx::SetArguments( const Sequence< PropertyValue >& rArguments ) { - for( const PropertyValue& rValue : rArguments ) + // for( const PropertyValue& rValue : rArguments ) + // { + // SetPropertyValue( rValue.Name, rValue.Value ); + // } + for (const PropertyValue& rValue : rArguments) { - SetPropertyValue( rValue.Name, rValue.Value ); + OUString aName = rValue.Name; + + SetPropertyValue(aName, rValue.Value); } } @@ -3663,6 +3681,16 @@ void PresentationSettingsEx::SetPropertyValue( std::u16string_view rProperty, co if( rValue >>= mbMouseAsPen ) return; } + else if ( rProperty == u"TimingMode" ) + { + if( rValue >>= mnTimerMode ) + return; + } + else if ( rProperty == u"TimerPosition" ) + { + if( rValue >>= mnTimerPosition ) + return; + } throw IllegalArgumentException(); } diff --git a/sd/source/ui/slideshow/slideshowimpl.hxx b/sd/source/ui/slideshow/slideshowimpl.hxx index 00f9c80003b7..01fb5cd24c8e 100644 --- a/sd/source/ui/slideshow/slideshowimpl.hxx +++ b/sd/source/ui/slideshow/slideshowimpl.hxx @@ -58,6 +58,8 @@ struct PresentationSettingsEx : public PresentationSettings VclPtr mpParentWindow; css::uno::Reference< css::drawing::XDrawPage > mxStartPage; css::uno::Reference< css::animations::XAnimationNode > mxAnimationNode; + sal_Int32 mnTimerMode; + sal_Int32 mnTimerPosition; PresentationSettingsEx( const PresentationSettingsEx& ); explicit PresentationSettingsEx( PresentationSettings const & ); @@ -340,6 +342,8 @@ private: bool mbWasPaused; // used to cache pause state during context menu bool mbInputFreeze; bool mbActive; + sal_Int32 mnTimerMode; // 计时器显示模式 + sal_Int32 mnTimerPosition; // 计时器位置 PresentationSettings maPresSettings; sal_Int32 mnUserPaintColor; diff --git a/sd/uiconfig/simpress/ui/presentationdialog.ui b/sd/uiconfig/simpress/ui/presentationdialog.ui index 33bfc4f9d64a..d4e5b58171ed 100644 --- a/sd/uiconfig/simpress/ui/presentationdialog.ui +++ b/sd/uiconfig/simpress/ui/presentationdialog.ui @@ -80,7 +80,7 @@ - + True False @@ -786,6 +786,142 @@ 1 + + + True + False + True + True + 0 + none + + + + True + False + 12 + 6 + True + True + 6 + 12 + + + + _Global Settings + True + True + False + True + True + + + Apply rehearsal timing settings globally to all presentations. + + + + + 0 + 0 + 2 + + + + + + True + False + _Timing mode: + True + timingmode_cb + 0 + + + 0 + 1 + + + + + + True + False + True + + Current slide / Total time + Current slide only + Total time only + + + + Select the timing display mode for rehearsal. Choose to display current slide time, total time, or both. + + + + + 1 + 1 + + + + + + True + False + Timer _position: + True + timerposition_cb + 0 + + + 0 + 2 + + + + + + True + False + True + + Top left + Bottom left + Top center + Bottom center + Top right + Bottom right + + + + Select the screen position where the rehearsal timer will be displayed during presentation. + + + + + 1 + 2 + + + + + + + + True + False + Rehearse Timings + + + + + + + + 0 + 3 + + True @@ -864,7 +1000,7 @@ 0 - 3 + 4 diff --git a/slideshow/source/engine/rehearsetimingsactivity.cxx b/slideshow/source/engine/rehearsetimingsactivity.cxx index 0d8295716092..7dd88d29186a 100644 --- a/slideshow/source/engine/rehearsetimingsactivity.cxx +++ b/slideshow/source/engine/rehearsetimingsactivity.cxx @@ -17,7 +17,6 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ - #include #include #include @@ -134,12 +133,17 @@ private: const sal_Int32 LEFT_BORDER_SPACE = 10; const sal_Int32 LOWER_BORDER_SPACE = 30; -RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext ) : +RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rContext, + sal_Int32 nTimingMode, + sal_Int32 nTimerPosition ) : mrEventQueue(rContext.mrEventQueue), mrScreenUpdater(rContext.mrScreenUpdater), mrEventMultiplexer(rContext.mrEventMultiplexer), mrActivitiesQueue(rContext.mrActivitiesQueue), maElapsedTime( rContext.mrEventQueue.getTimer() ), + maTotalElapsedTime( rContext.mrEventQueue.getTimer() ), // 添加一个总的计时器 + mnTimerMode( nTimingMode ), // 计时器显示模式 + mnTimerPosition( nTimerPosition ), // 计时器位置 maViews(), maSpriteRectangle(), maFont( Application::GetSettings().GetStyleSettings().GetLabelFont() ), @@ -155,15 +159,38 @@ RehearseTimingsActivity::RehearseTimingsActivity( const SlideShowContext& rConte maFont.SetAlignment( ALIGN_BASELINE ); maFont.SetColor( COL_BLACK ); - // determine sprite size (in pixel): + // determine sprite size (in pixel) for dual timing display: ScopedVclPtrInstance< VirtualDevice > blackHole; blackHole->EnableOutput(false); blackHole->SetFont( maFont ); blackHole->SetMapMode(MapMode(MapUnit::MapPixel)); tools::Rectangle rect; const FontMetric metric( blackHole->GetFontMetric() ); - blackHole->GetTextBoundRect( rect, u"XX:XX:XX"_ustr ); - maSpriteSizePixel.setX( rect.getOpenWidth() * 12 / 10 ); + // 根据计时器显示模式选择不同的样本字符串来计算宽度 + OUString aSampleText; + sal_Int32 nWidthFactor; // 宽度系数(分子,分母固定为10) + + switch (mnTimerMode) + { + case 1: // 只显示当前幻灯片计时 + case 2: // 只显示总时间计时 + // 单计时显示:"XX:XX:XX" + aSampleText = u"XX:XX:XX"_ustr; + nWidthFactor = 12; // 1.2倍 + break; + case 0: // 显示当前幻灯片/总时间(双计时显示) + default: + // 双计时显示:"XX:XX:XX / XX:XX:XX" + aSampleText = u"XX:XX:XX / XX:XX:XX"_ustr; + nWidthFactor = 11; // 1.1倍 + break; + } + + // 先计算文本边界 + blackHole->GetTextBoundRect( rect, aSampleText ); + + // 再根据计算结果和对应的系数设置宽度 + maSpriteSizePixel.setX( rect.getOpenWidth() * nWidthFactor / 10 ); maSpriteSizePixel.setY( metric.GetLineHeight() * 11 / 10 ); mnYOffset = (metric.GetAscent() + (metric.GetLineHeight() / 20)); @@ -184,10 +211,12 @@ RehearseTimingsActivity::~RehearseTimingsActivity() } std::shared_ptr RehearseTimingsActivity::create( - const SlideShowContext& rContext ) + const SlideShowContext& rContext, + sal_Int32 nTimingMode, + sal_Int32 nTimerPosition ) { std::shared_ptr pActivity( - new RehearseTimingsActivity( rContext )); + new RehearseTimingsActivity( rContext, nTimingMode, nTimerPosition )); pActivity->mpMouseHandler = std::make_shared(*pActivity); @@ -204,6 +233,11 @@ std::shared_ptr RehearseTimingsActivity::create( void RehearseTimingsActivity::start() { maElapsedTime.reset(); + // 当总时间为0时,重置总时间计时器 + if (maTotalElapsedTime.getElapsedTime() == 0.0) + { + maTotalElapsedTime.reset(); + } mbDrawPressed = false; mbActive = true; @@ -306,11 +340,50 @@ basegfx::B2DRange RehearseTimingsActivity::calcSpriteRectangle( UnoViewSharedPtr return basegfx::B2DRange(); const geometry::IntegerSize2D realSize( xBitmap->getSize() ); - // pixel: - basegfx::B2DPoint spritePos( - std::min( realSize.Width, LEFT_BORDER_SPACE ), - std::max( 0, realSize.Height - maSpriteSizePixel.getY() - - LOWER_BORDER_SPACE ) ); + + // 根据计时器位置设置计算显示位置 + basegfx::B2DPoint spritePos; + + switch (mnTimerPosition) + { + case 0: // 左上 + spritePos = basegfx::B2DPoint( + std::min( realSize.Width, LEFT_BORDER_SPACE ), + std::min( realSize.Height, LOWER_BORDER_SPACE ) ); + break; + case 1: // 左下 + spritePos = basegfx::B2DPoint( + std::min( realSize.Width, LEFT_BORDER_SPACE ), + std::max( 0, realSize.Height - maSpriteSizePixel.getY() + - LOWER_BORDER_SPACE ) ); + break; + case 2: // 中上 + spritePos = basegfx::B2DPoint( + std::max( 0, (realSize.Width - maSpriteSizePixel.getX()) / 2 ), + std::min( realSize.Height, LOWER_BORDER_SPACE ) ); + break; + case 3: // 中下 + spritePos = basegfx::B2DPoint( + std::max( 0, (realSize.Width - maSpriteSizePixel.getX()) / 2 ), + std::max( 0, realSize.Height - maSpriteSizePixel.getY() + - LOWER_BORDER_SPACE ) ); + break; + case 4: // 右上 + spritePos = basegfx::B2DPoint( + std::max( 0, realSize.Width - maSpriteSizePixel.getX() + - LEFT_BORDER_SPACE ), + std::min( realSize.Height, LOWER_BORDER_SPACE ) ); + break; + case 5: // 右下 + default: + spritePos = basegfx::B2DPoint( + std::max( 0, realSize.Width - maSpriteSizePixel.getX() + - LEFT_BORDER_SPACE ), + std::max( 0, realSize.Height - maSpriteSizePixel.getY() + - LOWER_BORDER_SPACE ) ); + break; + } + basegfx::B2DHomMatrix transformation( rView->getTransformation() ); transformation.invert(); spritePos *= transformation; @@ -408,23 +481,98 @@ void RehearseTimingsActivity::paintAllSprites() const void RehearseTimingsActivity::paint( cppcanvas::CanvasSharedPtr const & canvas ) const { - // build timer string: - const sal_Int32 nTimeSecs = + // 只在第一次绘制时打印 + static bool bFirstPaint = true; + if (bFirstPaint) { + bFirstPaint = false; + } + // build timer strings based on timing mode setting + const sal_Int32 nCurrentSlideTimeSecs = static_cast(maElapsedTime.getElapsedTime()); - OUStringBuffer buf; - sal_Int32 n = nTimeSecs / 3600; - if (n < 10) - buf.append( '0' ); - buf.append( OUString::number(n) + ":" ); - n = ((nTimeSecs % 3600) / 60); - if (n < 10) - buf.append( '0' ); - buf.append( OUString::number(n) + ":" ); - n = (nTimeSecs % 60); - if (n < 10) - buf.append( '0' ); - buf.append( n ); - const OUString time = buf.makeStringAndClear(); + const sal_Int32 nTotalTimeSecs = + static_cast(maTotalElapsedTime.getElapsedTime()); + + OUString time; + + // 根据计时器显示模式设置显示不同的计时信息 + switch (mnTimerMode) + { + case 1: // 只显示当前幻灯片计时 + { + OUStringBuffer buf; + sal_Int32 n = nCurrentSlideTimeSecs / 3600; + if (n < 10) + buf.append( '0' ); + buf.append( OUString::number(n) + ":" ); + n = ((nCurrentSlideTimeSecs % 3600) / 60); + if (n < 10) + buf.append( '0' ); + buf.append( OUString::number(n) + ":" ); + n = (nCurrentSlideTimeSecs % 60); + if (n < 10) + buf.append( '0' ); + buf.append( n ); + time = buf.makeStringAndClear(); + break; + } + case 2: // 只显示总时间计时 + { + OUStringBuffer buf; + sal_Int32 n = nTotalTimeSecs / 3600; + if (n < 10) + buf.append( '0' ); + buf.append( OUString::number(n) + ":" ); + n = ((nTotalTimeSecs % 3600) / 60); + if (n < 10) + buf.append( '0' ); + buf.append( OUString::number(n) + ":" ); + n = (nTotalTimeSecs % 60); + if (n < 10) + buf.append( '0' ); + buf.append( n ); + time = buf.makeStringAndClear(); + break; + } + case 0: // 显示当前幻灯片/总时间(双计时显示) + default: + { + // 格式化当前幻灯片的排练计时 + OUStringBuffer currentBuf; + sal_Int32 n = nCurrentSlideTimeSecs / 3600; + if (n < 10) + currentBuf.append( '0' ); + currentBuf.append( OUString::number(n) + ":" ); + n = ((nCurrentSlideTimeSecs % 3600) / 60); + if (n < 10) + currentBuf.append( '0' ); + currentBuf.append( OUString::number(n) + ":" ); + n = (nCurrentSlideTimeSecs % 60); + if (n < 10) + currentBuf.append( '0' ); + currentBuf.append( n ); + const OUString currentTime = currentBuf.makeStringAndClear(); + + // 格式化当前总的排练计时 + OUStringBuffer totalBuf; + n = nTotalTimeSecs / 3600; + if (n < 10) + totalBuf.append( '0' ); + totalBuf.append( OUString::number(n) + ":" ); + n = ((nTotalTimeSecs % 3600) / 60); + if (n < 10) + totalBuf.append( '0' ); + totalBuf.append( OUString::number(n) + ":" ); + n = (nTotalTimeSecs % 60); + if (n < 10) + totalBuf.append( '0' ); + totalBuf.append( n ); + const OUString totalTime = totalBuf.makeStringAndClear(); + + // 组装当前幻灯片计时和总计时 + time = currentTime + " / " + totalTime; + break; + } + } // create the MetaFile: GDIMetaFile metaFile; diff --git a/slideshow/source/engine/rehearsetimingsactivity.hxx b/slideshow/source/engine/rehearsetimingsactivity.hxx index 784ebfb2f584..3f5276976753 100644 --- a/slideshow/source/engine/rehearsetimingsactivity.hxx +++ b/slideshow/source/engine/rehearsetimingsactivity.hxx @@ -53,7 +53,9 @@ public: /** Creates the activity. */ static std::shared_ptr create( - const SlideShowContext& rContext ); + const SlideShowContext& rContext, + sal_Int32 nTimingMode = 0, // 计时器显示模式 + sal_Int32 nTimerPosition = 0 ); // 计时器位置 virtual ~RehearseTimingsActivity() override; RehearseTimingsActivity(const RehearseTimingsActivity&) = delete; @@ -90,7 +92,9 @@ public: private: class WakeupEvent; - explicit RehearseTimingsActivity( const SlideShowContext& rContext ); + explicit RehearseTimingsActivity( const SlideShowContext& rContext, + sal_Int32 nTimingMode = 0, // 计时器显示模式 + sal_Int32 nTimerPosition = 0 ); // 计时器位置 void paint( ::cppcanvas::CanvasSharedPtr const & canvas ) const; void paintAllSprites() const; @@ -119,6 +123,9 @@ private: EventMultiplexer& mrEventMultiplexer; ActivitiesQueue& mrActivitiesQueue; canvas::tools::ElapsedTime maElapsedTime; + canvas::tools::ElapsedTime maTotalElapsedTime; // 添加总的计时器 + sal_Int32 mnTimerMode; // 计时器显示模式 + sal_Int32 mnTimerPosition; // 计时器位置 ViewsVecT maViews; diff --git a/slideshow/source/engine/slideshowimpl.cxx b/slideshow/source/engine/slideshowimpl.cxx index f0e91d0bec15..046bf747f39f 100644 --- a/slideshow/source/engine/slideshowimpl.cxx +++ b/slideshow/source/engine/slideshowimpl.cxx @@ -17,7 +17,6 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ - #include #include @@ -378,6 +377,9 @@ private: /// stops the current slide transition sound void stopSlideTransitionSound(); + // 创建RehearseTimingsActivity + void maybeCreateOrUpdateRehearseTimingsActivity(); + /** Prepare a slide transition This method registers all necessary events and @@ -494,6 +496,11 @@ private: bool mbSlideShowIdle; bool mbDisableAnimationZOrder; bool mbMovingForward; + bool mbRehearseTimings; // 排练计时功能开关 + sal_Int32 mnTimerMode; // 计时器显示模式 + sal_Int32 mnTimerPosition; // 计时器位置 + bool mbTimerPositionSet; // 计时器显示模式标识 + bool mbTimerModeSet; // 计时器位置标识 EffectRewinder maEffectRewinder; FrameSynchronization maFrameSynchronization; @@ -606,6 +613,11 @@ SlideShowImpl::SlideShowImpl( mbSlideShowIdle( true ), mbDisableAnimationZOrder( false ), mbMovingForward( true ), + mbRehearseTimings( false ), // 默认关闭排练计时 + mnTimerMode( 0 ), // 默认显示当前/总时间 + mnTimerPosition( 0 ), // 默认位置左上 + mbTimerPositionSet(false), // 默认计时器位置未发生改变 + mbTimerModeSet(false), // 默认计时器显示模式未发生改变 maEffectRewinder(maEventMultiplexer, maEventQueue, maUserEventQueue), maFrameSynchronization(1.0 / FrameRate::PreferredFramesPerSecond) @@ -1852,28 +1864,49 @@ sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty ) return (rProperty.Value >>= mbForceManualAdvance); } + if ( rProperty.Name == "TimingMode" ) + { + sal_Int32 nTimingMode = 0; + if (! (rProperty.Value >>= nTimingMode)) + return false; + + mnTimerMode = nTimingMode; + mbTimerModeSet = true; + + // 统一调用一个 helper + maybeCreateOrUpdateRehearseTimingsActivity(); + + return true; + } + + if ( rProperty.Name == "TimerPosition" ) + { + sal_Int32 nTimerPosition = 0; + if (! (rProperty.Value >>= nTimerPosition)) + return false; + + mnTimerPosition = nTimerPosition; + mbTimerPositionSet = true; + + // 统一调用一个 helper + maybeCreateOrUpdateRehearseTimingsActivity(); + + return true; + } + if ( rProperty.Name == "RehearseTimings" ) { bool bRehearseTimings = false; if (! (rProperty.Value >>= bRehearseTimings)) return false; + mbRehearseTimings = bRehearseTimings; + if (bRehearseTimings) { - // TODO(Q3): Move to slide - mpRehearseTimingsActivity = RehearseTimingsActivity::create( - SlideShowContext( - mpDummyPtr, - maEventQueue, - maEventMultiplexer, - maScreenUpdater, - maActivitiesQueue, - maUserEventQueue, - *this, - *this, - maViewContainer, - mxComponentContext, - mpBox2DDummyPtr ) ); + // 启用排练计时,但不直接创建,让 helper 根据当前参数决定 + maybeCreateOrUpdateRehearseTimingsActivity(); + } else if (mpRehearseTimingsActivity) { @@ -2017,6 +2050,37 @@ sal_Bool SlideShowImpl::setProperty( beans::PropertyValue const& rProperty ) return false; } +void SlideShowImpl::maybeCreateOrUpdateRehearseTimingsActivity() +{ + // 1) 没启用排练计时就不用创建 + if (!mbRehearseTimings) + return; + + // 2) 两个参数都至少要被设置过一次 + if (!mbTimerModeSet || !mbTimerPositionSet) + return; + + // 3) 如果还没创建过,就创建一次 + if (!mpRehearseTimingsActivity) + { + mpRehearseTimingsActivity = RehearseTimingsActivity::create( + SlideShowContext( + mpDummyPtr, + maEventQueue, + maEventMultiplexer, + maScreenUpdater, + maActivitiesQueue, + maUserEventQueue, + *this, + *this, + maViewContainer, + mxComponentContext, + mpBox2DDummyPtr ), + mnTimerMode, + mnTimerPosition ); + } +} + void SlideShowImpl::addSlideShowListener( uno::Reference const& xListener ) {