1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <uielement/statusbarmanager.hxx>
21 #include <uielement/genericstatusbarcontroller.hxx>
23 #include <framework/sfxhelperfunctions.hxx>
24 #include <framework/addonsoptions.hxx>
25 #include <uielement/statusbarmerger.hxx>
26 #include <uielement/statusbaritem.hxx>
27 #include <com/sun/star/frame/XLayoutManager.hpp>
28 #include <com/sun/star/frame/theStatusbarControllerFactory.hpp>
29 #include <com/sun/star/ui/ItemStyle.hpp>
30 #include <com/sun/star/ui/ItemType.hpp>
31 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
32 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/awt/Command.hpp>
35 #include <com/sun/star/ui/XStatusbarItem.hpp>
36 #include <comphelper/processfactory.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <toolkit/helper/vclunohelper.hxx>
39 #include <svtools/statusbarcontroller.hxx>
40 #include <tools/debug.hxx>
43 #include <vcl/commandevent.hxx>
44 #include <vcl/event.hxx>
45 #include <vcl/status.hxx>
46 #include <vcl/svapp.hxx>
47 #include <vcl/settings.hxx>
48 #include <vcl/commandinfoprovider.hxx>
52 using namespace ::com::sun::star
;
61 struct lcl_UpdateController
63 void operator()( typename
MAP::value_type
&rElement
) const
67 if ( rElement
.second
.is() )
68 rElement
.second
->update();
70 catch ( uno::Exception
& )
77 struct lcl_RemoveController
79 void operator()( typename
MAP::value_type
&rElement
) const
83 if ( rElement
.second
.is() )
84 rElement
.second
->dispose();
86 catch ( uno::Exception
& )
92 StatusBarItemBits
impl_convertItemStyleToItemBits( sal_Int16 nStyle
)
94 StatusBarItemBits
nItemBits( StatusBarItemBits::NONE
);
96 if (( nStyle
& css::ui::ItemStyle::ALIGN_RIGHT
) == css::ui::ItemStyle::ALIGN_RIGHT
)
97 nItemBits
|= StatusBarItemBits::Right
;
98 else if ( nStyle
& css::ui::ItemStyle::ALIGN_LEFT
)
99 nItemBits
|= StatusBarItemBits::Left
;
101 nItemBits
|= StatusBarItemBits::Center
;
103 if (( nStyle
& css::ui::ItemStyle::DRAW_FLAT
) == css::ui::ItemStyle::DRAW_FLAT
)
104 nItemBits
|= StatusBarItemBits::Flat
;
105 else if ( nStyle
& css::ui::ItemStyle::DRAW_OUT3D
)
106 nItemBits
|= StatusBarItemBits::Out
;
108 nItemBits
|= StatusBarItemBits::In
;
110 if (( nStyle
& css::ui::ItemStyle::AUTO_SIZE
) == css::ui::ItemStyle::AUTO_SIZE
)
111 nItemBits
|= StatusBarItemBits::AutoSize
;
112 if ( nStyle
& css::ui::ItemStyle::OWNER_DRAW
)
113 nItemBits
|= StatusBarItemBits::UserDraw
;
115 if ( nStyle
& css::ui::ItemStyle::MANDATORY
)
116 nItemBits
|= StatusBarItemBits::Mandatory
;
123 StatusBarManager::StatusBarManager(
124 uno::Reference
< uno::XComponentContext
> xContext
,
125 uno::Reference
< frame::XFrame
> rFrame
,
126 StatusBar
* pStatusBar
) :
127 m_bDisposed( false ),
128 m_bFrameActionRegistered( false ),
129 m_bUpdateControllers( false ),
130 m_pStatusBar( pStatusBar
),
131 m_xFrame(std::move( rFrame
)),
132 m_xContext(std::move( xContext
))
135 m_xStatusbarControllerFactory
= frame::theStatusbarControllerFactory::get(
136 ::comphelper::getProcessComponentContext());
138 m_pStatusBar
->AdjustItemWidthsForHiDPI();
139 m_pStatusBar
->SetClickHdl( LINK( this, StatusBarManager
, Click
) );
140 m_pStatusBar
->SetDoubleClickHdl( LINK( this, StatusBarManager
, DoubleClick
) );
143 StatusBarManager::~StatusBarManager()
147 StatusBar
* StatusBarManager::GetStatusBar() const
153 void StatusBarManager::frameAction( const frame::FrameActionEvent
& Action
)
156 if ( Action
.Action
== frame::FrameAction_CONTEXT_CHANGED
)
160 void SAL_CALL
StatusBarManager::disposing( const lang::EventObject
& Source
)
169 if ( Source
.Source
== uno::Reference
< uno::XInterface
>( m_xFrame
, uno::UNO_QUERY
))
176 void SAL_CALL
StatusBarManager::dispose()
178 uno::Reference
< lang::XComponent
> xThis(this );
181 lang::EventObject
aEvent( xThis
);
182 std::unique_lock
aGuard(m_mutex
);
183 m_aListenerContainer
.disposeAndClear( aGuard
, aEvent
);
192 // destroy the item data
193 for ( sal_uInt16 n
= 0; n
< m_pStatusBar
->GetItemCount(); n
++ )
195 AddonStatusbarItemData
*pUserData
= static_cast< AddonStatusbarItemData
*>(
196 m_pStatusBar
->GetItemData( m_pStatusBar
->GetItemId( n
) ) );
200 m_pStatusBar
.disposeAndClear();
202 if ( m_bFrameActionRegistered
&& m_xFrame
.is() )
206 m_xFrame
->removeFrameActionListener( uno::Reference
< frame::XFrameActionListener
>(this) );
208 catch ( const uno::Exception
& )
220 void SAL_CALL
StatusBarManager::addEventListener( const uno::Reference
< lang::XEventListener
>& xListener
)
224 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
226 throw lang::DisposedException();
228 std::unique_lock
aGuard(m_mutex
);
229 m_aListenerContainer
.addInterface( aGuard
, xListener
);
232 void SAL_CALL
StatusBarManager::removeEventListener( const uno::Reference
< lang::XEventListener
>& xListener
)
234 std::unique_lock
aGuard(m_mutex
);
235 m_aListenerContainer
.removeInterface( aGuard
, xListener
);
238 // XUIConfigurationListener
239 void SAL_CALL
StatusBarManager::elementInserted( const css::ui::ConfigurationEvent
& )
247 void SAL_CALL
StatusBarManager::elementRemoved( const css::ui::ConfigurationEvent
& )
255 void SAL_CALL
StatusBarManager::elementReplaced( const css::ui::ConfigurationEvent
& )
263 void StatusBarManager::UpdateControllers()
265 if ( !m_bUpdateControllers
)
267 m_bUpdateControllers
= true;
268 std::for_each( m_aControllerMap
.begin(),
269 m_aControllerMap
.end(),
270 lcl_UpdateController
< StatusBarControllerMap
>() );
272 m_bUpdateControllers
= false;
275 void StatusBarManager::RemoveControllers()
277 DBG_TESTSOLARMUTEX();
278 assert(!m_bDisposed
);
280 std::for_each( m_aControllerMap
.begin(),
281 m_aControllerMap
.end(),
282 lcl_RemoveController
< StatusBarControllerMap
>() );
283 m_aControllerMap
.clear();
286 void StatusBarManager::CreateControllers()
288 uno::Reference
< awt::XWindow
> xStatusbarWindow
= VCLUnoHelper::GetInterface( m_pStatusBar
);
290 for ( sal_uInt16 i
= 0; i
< m_pStatusBar
->GetItemCount(); i
++ )
292 sal_uInt16 nId
= m_pStatusBar
->GetItemId( i
);
296 OUString
aCommandURL( m_pStatusBar
->GetItemCommand( nId
));
298 uno::Reference
< frame::XStatusbarController
> xController
;
299 AddonStatusbarItemData
*pItemData
= static_cast< AddonStatusbarItemData
*>( m_pStatusBar
->GetItemData( nId
) );
300 uno::Reference
< ui::XStatusbarItem
> xStatusbarItem
= new StatusbarItem( m_pStatusBar
, nId
, aCommandURL
);
302 beans::PropertyValue aPropValue
;
303 std::vector
< uno::Any
> aPropVector
;
305 aPropValue
.Name
= "CommandURL";
306 aPropValue
.Value
<<= aCommandURL
;
307 aPropVector
.push_back( uno::Any( aPropValue
) );
309 aPropValue
.Name
= "ModuleIdentifier";
310 aPropValue
.Value
<<= OUString();
311 aPropVector
.push_back( uno::Any( aPropValue
) );
313 aPropValue
.Name
= "Frame";
314 aPropValue
.Value
<<= m_xFrame
;
315 aPropVector
.push_back( uno::Any( aPropValue
) );
318 aPropValue
.Name
= "ServiceManager";
319 aPropValue
.Value
<<= uno::Reference
<lang::XMultiServiceFactory
>(m_xContext
->getServiceManager(), uno::UNO_QUERY_THROW
);
320 aPropVector
.push_back( uno::Any( aPropValue
) );
322 aPropValue
.Name
= "ParentWindow";
323 aPropValue
.Value
<<= xStatusbarWindow
;
324 aPropVector
.push_back( uno::Any( aPropValue
) );
326 // TODO still needing with the css::ui::XStatusbarItem?
327 aPropValue
.Name
= "Identifier";
328 aPropValue
.Value
<<= nId
;
329 aPropVector
.push_back( uno::Any( aPropValue
) );
331 aPropValue
.Name
= "StatusbarItem";
332 aPropValue
.Value
<<= xStatusbarItem
;
333 aPropVector
.push_back( uno::Any( aPropValue
) );
335 uno::Sequence
< uno::Any
> aArgs( comphelper::containerToSequence( aPropVector
) );
337 // 1) UNO Statusbar controllers, registered in Controllers.xcu
338 if ( m_xStatusbarControllerFactory
.is() &&
339 m_xStatusbarControllerFactory
->hasController( aCommandURL
, "" ))
341 xController
.set(m_xStatusbarControllerFactory
->createInstanceWithArgumentsAndContext(
342 aCommandURL
, aArgs
, m_xContext
),
344 bInit
= false; // Initialization is done through the factory service
347 if ( !xController
.is() )
349 // 2) Old SFX2 Statusbar controllers
350 xController
= CreateStatusBarController( m_xFrame
, m_pStatusBar
, nId
, aCommandURL
);
353 // 3) Is Add-on? Generic statusbar controller
356 xController
= new GenericStatusbarController( m_xContext
,
363 // 4) Default Statusbar controller
364 xController
= new svt::StatusbarController( m_xContext
, m_xFrame
, aCommandURL
, nId
);
369 m_aControllerMap
[nId
] = xController
;
372 xController
->initialize( aArgs
);
376 // add frame action listeners
377 if ( !m_bFrameActionRegistered
&& m_xFrame
.is() )
379 m_bFrameActionRegistered
= true;
380 m_xFrame
->addFrameActionListener( uno::Reference
< frame::XFrameActionListener
>(this) );
384 void StatusBarManager::FillStatusBar( const uno::Reference
< container::XIndexAccess
>& rItemContainer
)
388 if ( m_bDisposed
|| !m_pStatusBar
)
395 // reset and fill command map
396 m_pStatusBar
->Clear();
397 m_aControllerMap
.clear();// TODO already done in RemoveControllers
399 for ( sal_Int32 n
= 0; n
< rItemContainer
->getCount(); n
++ )
401 uno::Sequence
< beans::PropertyValue
> aProps
;
402 OUString aCommandURL
;
403 sal_Int16
nOffset( 0 );
404 sal_Int16
nStyle( 0 );
405 sal_Int16
nWidth( 0 );
406 sal_uInt16
nType( css::ui::ItemType::DEFAULT
);
410 if ( rItemContainer
->getByIndex( n
) >>= aProps
)
412 for ( beans::PropertyValue
const & prop
: std::as_const(aProps
) )
414 if ( prop
.Name
== "CommandURL" )
416 prop
.Value
>>= aCommandURL
;
418 else if ( prop
.Name
== "Style" )
420 prop
.Value
>>= nStyle
;
422 else if ( prop
.Name
== "Type" )
424 prop
.Value
>>= nType
;
426 else if ( prop
.Name
== "Width" )
428 prop
.Value
>>= nWidth
;
430 else if ( prop
.Name
== "Offset" )
432 prop
.Value
>>= nOffset
;
436 if (( nType
== css::ui::ItemType::DEFAULT
) && !aCommandURL
.isEmpty() )
438 auto aProperties
= vcl::CommandInfoProvider::GetCommandProperties(aCommandURL
, "");
439 OUString
aString(vcl::CommandInfoProvider::GetLabelForCommand(aProperties
));
440 StatusBarItemBits
nItemBits( impl_convertItemStyleToItemBits( nStyle
));
442 m_pStatusBar
->InsertItem( nId
, nWidth
, nItemBits
, nOffset
);
443 m_pStatusBar
->SetItemCommand( nId
, aCommandURL
);
444 m_pStatusBar
->SetAccessibleName( nId
, aString
);
449 catch ( const css::lang::IndexOutOfBoundsException
& )
456 constexpr sal_uInt16 STATUSBAR_ITEM_STARTID
= 1000;
457 MergeStatusbarInstructionContainer aMergeInstructions
= AddonsOptions().GetMergeStatusbarInstructions();
458 if ( !aMergeInstructions
.empty() )
460 const sal_uInt32 nCount
= aMergeInstructions
.size();
461 sal_uInt16
nItemId( STATUSBAR_ITEM_STARTID
);
463 for ( sal_uInt32 i
= 0; i
< nCount
; i
++ )
465 MergeStatusbarInstruction
&rInstruction
= aMergeInstructions
[i
];
466 if ( !StatusbarMerger::IsCorrectContext( rInstruction
.aMergeContext
) )
469 AddonStatusbarItemContainer aItems
;
470 StatusbarMerger::ConvertSeqSeqToVector( rInstruction
.aMergeStatusbarItems
, aItems
);
472 sal_uInt16 nRefPos
= StatusbarMerger::FindReferencePos( m_pStatusBar
, rInstruction
.aMergePoint
);
473 if ( nRefPos
!= STATUSBAR_ITEM_NOTFOUND
)
475 StatusbarMerger::ProcessMergeOperation( m_pStatusBar
,
478 rInstruction
.aMergeCommand
,
479 rInstruction
.aMergeCommandParameter
,
484 StatusbarMerger::ProcessMergeFallback( m_pStatusBar
,
486 rInstruction
.aMergeCommand
,
487 rInstruction
.aMergeCommandParameter
,
493 // Create controllers
496 // Notify controllers that they are now correctly initialized and can start listening
500 void StatusBarManager::DataChanged( const DataChangedEvent
& rDCEvt
)
502 SolarMutexClearableGuard aGuard
;
504 if ((( rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) ||
505 ( rDCEvt
.GetType() == DataChangedEventType::FONTS
) ||
506 ( rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
) ||
507 ( rDCEvt
.GetType() == DataChangedEventType::DISPLAY
)) &&
508 ( rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
))
510 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
511 css::uno::Reference
< css::beans::XPropertySet
> xPropSet( m_xFrame
, css::uno::UNO_QUERY
);
513 xPropSet
->getPropertyValue("LayoutManager") >>= xLayoutManager
;
514 if ( xLayoutManager
.is() )
517 xLayoutManager
->doLayout();
522 void StatusBarManager::UserDraw( const UserDrawEvent
& rUDEvt
)
524 SolarMutexClearableGuard aGuard
;
529 sal_uInt16
nId( rUDEvt
.GetItemId() );
530 StatusBarControllerMap::const_iterator it
= m_aControllerMap
.find( nId
);
531 if (( nId
<= 0 ) || ( it
== m_aControllerMap
.end() ))
534 uno::Reference
< frame::XStatusbarController
> xController( it
->second
);
535 if (xController
.is() && rUDEvt
.GetRenderContext())
537 uno::Reference
< awt::XGraphics
> xGraphics
= rUDEvt
.GetRenderContext()->CreateUnoGraphics();
539 awt::Rectangle
aRect( rUDEvt
.GetRect().Left(),
540 rUDEvt
.GetRect().Top(),
541 rUDEvt
.GetRect().GetWidth(),
542 rUDEvt
.GetRect().GetHeight() );
544 xController
->paint(xGraphics
, aRect
, 0);
548 void StatusBarManager::Command( const CommandEvent
& rEvt
)
555 if ( rEvt
.GetCommand() != CommandEventId::ContextMenu
)
558 sal_uInt16 nId
= m_pStatusBar
->GetItemId( rEvt
.GetMousePosPixel() );
559 StatusBarControllerMap::const_iterator it
= m_aControllerMap
.find( nId
);
560 if (( nId
> 0 ) && ( it
!= m_aControllerMap
.end() ))
562 uno::Reference
< frame::XStatusbarController
> xController( it
->second
);
563 if ( xController
.is() )
566 aPos
.X
= rEvt
.GetMousePosPixel().X();
567 aPos
.Y
= rEvt
.GetMousePosPixel().Y();
568 xController
->command( aPos
, awt::Command::CONTEXTMENU
, true, uno::Any() );
573 void StatusBarManager::MouseMove( const MouseEvent
& rMEvt
)
575 MouseButton(rMEvt
,&frame::XStatusbarController::mouseMove
);
578 void StatusBarManager::MouseButton( const MouseEvent
& rMEvt
,sal_Bool ( SAL_CALL
frame::XStatusbarController::*_pMethod
)(const css::awt::MouseEvent
&))
585 sal_uInt16 nId
= m_pStatusBar
->GetItemId( rMEvt
.GetPosPixel() );
586 StatusBarControllerMap::const_iterator it
= m_aControllerMap
.find( nId
);
587 if (( nId
<= 0 ) || ( it
== m_aControllerMap
.end() ))
590 uno::Reference
< frame::XStatusbarController
> xController( it
->second
);
591 if ( xController
.is() )
593 css::awt::MouseEvent aMouseEvent
;
594 aMouseEvent
.Buttons
= rMEvt
.GetButtons();
595 aMouseEvent
.X
= rMEvt
.GetPosPixel().X();
596 aMouseEvent
.Y
= rMEvt
.GetPosPixel().Y();
597 aMouseEvent
.ClickCount
= rMEvt
.GetClicks();
598 (xController
.get()->*_pMethod
)( aMouseEvent
);
602 void StatusBarManager::MouseButtonDown( const MouseEvent
& rMEvt
)
604 MouseButton(rMEvt
,&frame::XStatusbarController::mouseButtonDown
);
607 void StatusBarManager::MouseButtonUp( const MouseEvent
& rMEvt
)
609 MouseButton(rMEvt
,&frame::XStatusbarController::mouseButtonUp
);
612 IMPL_LINK_NOARG(StatusBarManager
, Click
, StatusBar
*, void)
619 sal_uInt16 nId
= m_pStatusBar
->GetCurItemId();
620 StatusBarControllerMap::const_iterator it
= m_aControllerMap
.find( nId
);
621 if (( nId
> 0 ) && ( it
!= m_aControllerMap
.end() ))
623 uno::Reference
< frame::XStatusbarController
> xController( it
->second
);
624 if ( xController
.is() )
626 const Point aVCLPos
= m_pStatusBar
->GetPointerPosPixel();
627 const awt::Point
aAWTPoint( aVCLPos
.X(), aVCLPos
.Y() );
628 xController
->click( aAWTPoint
);
633 IMPL_LINK_NOARG(StatusBarManager
, DoubleClick
, StatusBar
*, void)
640 sal_uInt16 nId
= m_pStatusBar
->GetCurItemId();
641 StatusBarControllerMap::const_iterator it
= m_aControllerMap
.find( nId
);
642 if (( nId
> 0 ) && ( it
!= m_aControllerMap
.end() ))
644 uno::Reference
< frame::XStatusbarController
> xController( it
->second
);
645 if ( xController
.is() )
647 const Point aVCLPos
= m_pStatusBar
->GetPointerPosPixel();
648 const awt::Point
aAWTPoint( aVCLPos
.X(), aVCLPos
.Y() );
649 xController
->doubleClick( aAWTPoint
);
656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */