Avoid potential negative array index access to cached text.
[LibreOffice.git] / framework / source / uielement / statusbarmanager.cxx
blobdbc305c696fa8680553938c5305618f7b59eb959
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
42 #include <utility>
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>
50 #include <cassert>
52 using namespace ::com::sun::star;
54 namespace framework
57 namespace
60 template< class MAP >
61 struct lcl_UpdateController
63 void operator()( typename MAP::value_type &rElement ) const
65 try
67 if ( rElement.second.is() )
68 rElement.second->update();
70 catch ( uno::Exception& )
76 template< class MAP >
77 struct lcl_RemoveController
79 void operator()( typename MAP::value_type &rElement ) const
81 try
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;
100 else
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;
107 else
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;
118 return nItemBits;
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
149 SolarMutexGuard g;
150 return m_pStatusBar;
153 void StatusBarManager::frameAction( const frame::FrameActionEvent& Action )
155 SolarMutexGuard g;
156 if ( Action.Action == frame::FrameAction_CONTEXT_CHANGED )
157 UpdateControllers();
160 void SAL_CALL StatusBarManager::disposing( const lang::EventObject& Source )
162 SolarMutexGuard g;
164 if ( m_bDisposed )
165 return;
167 RemoveControllers();
169 if ( Source.Source == uno::Reference< uno::XInterface >( m_xFrame, uno::UNO_QUERY ))
170 m_xFrame.clear();
172 m_xContext.clear();
175 // XComponent
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 );
186 SolarMutexGuard g;
187 if ( m_bDisposed )
188 return;
190 RemoveControllers();
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 ) ) );
197 delete pUserData;
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& )
213 m_xFrame.clear();
214 m_xContext.clear();
216 m_bDisposed = true;
220 void SAL_CALL StatusBarManager::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
222 SolarMutexGuard g;
224 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
225 if ( m_bDisposed )
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& )
241 SolarMutexGuard g;
243 if ( m_bDisposed )
244 return;
247 void SAL_CALL StatusBarManager::elementRemoved( const css::ui::ConfigurationEvent& )
249 SolarMutexGuard g;
251 if ( m_bDisposed )
252 return;
255 void SAL_CALL StatusBarManager::elementReplaced( const css::ui::ConfigurationEvent& )
257 SolarMutexGuard g;
259 if ( m_bDisposed )
260 return;
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 );
293 if ( nId == 0 )
294 continue;
296 OUString aCommandURL( m_pStatusBar->GetItemCommand( nId ));
297 bool bInit( true );
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 ) );
317 // TODO remove this
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 ),
343 uno::UNO_QUERY );
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 );
351 if ( !xController )
353 // 3) Is Add-on? Generic statusbar controller
354 if ( pItemData )
356 xController = new GenericStatusbarController( m_xContext,
357 m_xFrame,
358 xStatusbarItem,
359 pItemData );
361 else
363 // 4) Default Statusbar controller
364 xController = new svt::StatusbarController( m_xContext, m_xFrame, aCommandURL, nId );
369 m_aControllerMap[nId] = xController;
370 if ( bInit )
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 )
386 SolarMutexGuard g;
388 if ( m_bDisposed || !m_pStatusBar )
389 return;
391 sal_uInt16 nId( 1 );
393 RemoveControllers();
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 );
445 ++nId;
449 catch ( const css::lang::IndexOutOfBoundsException& )
451 break;
455 // Statusbar Merging
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 ) )
467 continue;
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,
476 nRefPos,
477 nItemId,
478 rInstruction.aMergeCommand,
479 rInstruction.aMergeCommandParameter,
480 aItems );
482 else
484 StatusbarMerger::ProcessMergeFallback( m_pStatusBar,
485 nItemId,
486 rInstruction.aMergeCommand,
487 rInstruction.aMergeCommandParameter,
488 aItems );
493 // Create controllers
494 CreateControllers();
496 // Notify controllers that they are now correctly initialized and can start listening
497 UpdateControllers();
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 );
512 if ( xPropSet.is() )
513 xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
514 if ( xLayoutManager.is() )
516 aGuard.clear();
517 xLayoutManager->doLayout();
522 void StatusBarManager::UserDraw( const UserDrawEvent& rUDEvt )
524 SolarMutexClearableGuard aGuard;
526 if ( m_bDisposed )
527 return;
529 sal_uInt16 nId( rUDEvt.GetItemId() );
530 StatusBarControllerMap::const_iterator it = m_aControllerMap.find( nId );
531 if (( nId <= 0 ) || ( it == m_aControllerMap.end() ))
532 return;
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() );
543 aGuard.clear();
544 xController->paint(xGraphics, aRect, 0);
548 void StatusBarManager::Command( const CommandEvent& rEvt )
550 SolarMutexGuard g;
552 if ( m_bDisposed )
553 return;
555 if ( rEvt.GetCommand() != CommandEventId::ContextMenu )
556 return;
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() )
565 awt::Point aPos;
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&))
580 SolarMutexGuard g;
582 if ( m_bDisposed )
583 return;
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() ))
588 return;
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)
614 SolarMutexGuard g;
616 if ( m_bDisposed )
617 return;
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)
635 SolarMutexGuard g;
637 if ( m_bDisposed )
638 return;
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: */