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 <com/sun/star/embed/VerbDescriptor.hpp>
21 #include <com/sun/star/embed/VerbAttributes.hpp>
22 #include <officecfg/Office/Common.hxx>
23 #include <rtl/ustring.hxx>
24 #include <sal/log.hxx>
25 #include <osl/diagnose.h>
26 #include <svl/itempool.hxx>
27 #include <svl/undo.hxx>
28 #include <itemdel.hxx>
29 #include <svtools/asynclink.hxx>
30 #include <unotools/configmgr.hxx>
31 #include <comphelper/lok.hxx>
32 #include <sfx2/shell.hxx>
33 #include <sfx2/bindings.hxx>
34 #include <sfx2/dispatch.hxx>
35 #include <sfx2/viewfrm.hxx>
36 #include <sfx2/objface.hxx>
37 #include <sfx2/objsh.hxx>
38 #include <sfx2/viewsh.hxx>
39 #include <sfx2/request.hxx>
40 #include <sfx2/sfxsids.hrc>
41 #include <statcach.hxx>
42 #include <sidebar/ContextChangeBroadcaster.hxx>
43 #include <com/sun/star/ui/dialogs/XSLTFilterDialog.hpp>
44 #include <tools/debug.hxx>
50 using namespace com::sun::star
;
52 struct SfxShell_Impl
: public SfxBroadcaster
54 OUString aObjectName
; // Name of Sbx-Objects
55 // Maps the Which() field to a pointer to a SfxPoolItem
56 std::map
<sal_uInt16
, std::unique_ptr
<SfxPoolItem
>>
57 m_Items
; // Data exchange on Item level
58 SfxViewShell
* pViewSh
; // SfxViewShell if Shell is
59 // ViewFrame/ViewShell/SubShell list
60 SfxViewFrame
* pFrame
; // Frame, if <UI-active>
61 SfxRepeatTarget
* pRepeatTarget
; // SbxObjectRef xParent;
63 SfxDisableFlags nDisableFlags
;
64 std::unique_ptr
<svtools::AsynchronLink
> pExecuter
;
65 std::unique_ptr
<svtools::AsynchronLink
> pUpdater
;
66 std::vector
<std::unique_ptr
<SfxSlot
> > aSlotArr
;
68 css::uno::Sequence
< css::embed::VerbDescriptor
> aVerbList
;
69 ::sfx2::sidebar::ContextChangeBroadcaster maContextChangeBroadcaster
;
74 , pRepeatTarget(nullptr)
76 , nDisableFlags(SfxDisableFlags::NONE
)
80 virtual ~SfxShell_Impl() override
{ pExecuter
.reset(); pUpdater
.reset();}
84 void SfxShell::EmptyExecStub(SfxShell
*, SfxRequest
&)
88 void SfxShell::EmptyStateStub(SfxShell
*, SfxItemSet
&)
93 : pImpl(new SfxShell_Impl
),
99 SfxShell::SfxShell( SfxViewShell
*pViewSh
)
100 : pImpl(new SfxShell_Impl
),
104 pImpl
->pViewSh
= pViewSh
;
107 SfxShell::~SfxShell()
111 void SfxShell::SetName( const OUString
&rName
)
113 pImpl
->aObjectName
= rName
;
116 const OUString
& SfxShell::GetName() const
118 return pImpl
->aObjectName
;
121 SfxDispatcher
* SfxShell::GetDispatcher() const
123 return pImpl
->pFrame
? pImpl
->pFrame
->GetDispatcher() : nullptr;
126 SfxViewShell
* SfxShell::GetViewShell() const
128 return pImpl
->pViewSh
;
131 SfxViewFrame
* SfxShell::GetFrame() const
134 return pImpl
->pFrame
;
135 if ( pImpl
->pViewSh
)
136 return pImpl
->pViewSh
->GetViewFrame();
140 const SfxPoolItem
* SfxShell::GetItem
142 sal_uInt16 nSlotId
// Slot-Id of the querying <SfxPoolItem>s
145 auto const it
= pImpl
->m_Items
.find( nSlotId
);
146 if (it
!= pImpl
->m_Items
.end())
147 return it
->second
.get();
151 void SfxShell::PutItem
153 const SfxPoolItem
& rItem
/* Instance, of which a copy is created,
154 which is stored in the SfxShell in a list. */
157 DBG_ASSERT( dynamic_cast< const SfxSetItem
* >( &rItem
) == nullptr, "SetItems aren't allowed here" );
158 DBG_ASSERT( SfxItemPool::IsSlot( rItem
.Which() ),
159 "items with Which-Ids aren't allowed here" );
161 // MSC made a mess here of WNT/W95, beware of changes
162 SfxPoolItem
*pItem
= rItem
.Clone();
163 SfxPoolItemHint
aItemHint( pItem
);
164 sal_uInt16 nWhich
= rItem
.Which();
166 auto const it
= pImpl
->m_Items
.find(nWhich
);
167 if (it
!= pImpl
->m_Items
.end())
170 pImpl
->m_Items
.erase( it
);
171 pImpl
->m_Items
.insert(std::make_pair(nWhich
, std::unique_ptr
<SfxPoolItem
>(pItem
)));
173 // if active, notify Bindings
174 SfxDispatcher
*pDispat
= GetDispatcher();
177 SfxBindings
* pBindings
= pDispat
->GetBindings();
178 pBindings
->Broadcast( aItemHint
);
179 sal_uInt16 nSlotId
= nWhich
; //pItem->GetSlotId();
180 SfxStateCache
* pCache
= pBindings
->GetStateCache( nSlotId
);
183 pCache
->SetState( SfxItemState::DEFAULT
, pItem
, true );
184 pCache
->SetCachedState( true );
191 Broadcast( aItemHint
);
192 pImpl
->m_Items
.insert(std::make_pair(nWhich
, std::unique_ptr
<SfxPoolItem
>(pItem
)));
196 SfxInterface
* SfxShell::GetInterface() const
198 return GetStaticInterface();
201 SfxUndoManager
* SfxShell::GetUndoManager()
206 void SfxShell::SetUndoManager( SfxUndoManager
*pNewUndoMgr
)
208 OSL_ENSURE( ( pUndoMgr
== nullptr ) || ( pNewUndoMgr
== nullptr ) || ( pUndoMgr
== pNewUndoMgr
),
209 "SfxShell::SetUndoManager: exchanging one non-NULL manager with another non-NULL manager? Suspicious!" );
210 // there's at least one client of our UndoManager - the DocumentUndoManager at the SfxBaseModel - which
211 // caches the UndoManager, and registers itself as listener. If exchanging non-NULL UndoManagers is really
212 // a supported scenario (/me thinks it is not), then we would need to notify all such clients instances.
214 pUndoMgr
= pNewUndoMgr
;
215 if (pUndoMgr
&& !utl::ConfigManager::IsFuzzing())
217 pUndoMgr
->SetMaxUndoActionCount(
218 officecfg::Office::Common::Undo::Steps::get());
222 SfxRepeatTarget
* SfxShell::GetRepeatTarget() const
224 return pImpl
->pRepeatTarget
;
227 void SfxShell::SetRepeatTarget( SfxRepeatTarget
*pTarget
)
229 pImpl
->pRepeatTarget
= pTarget
;
232 void SfxShell::Invalidate
234 sal_uInt16 nId
/* Invalidated Slot-Id or Which-Id.
235 If these are 0 (default), then all
236 by this Shell currently handled Slot-Ids are
240 if ( !GetViewShell() )
242 OSL_FAIL( "wrong Invalidate method called!" );
246 Invalidate_Impl( GetViewShell()->GetViewFrame()->GetBindings(), nId
);
249 void SfxShell::Invalidate_Impl( SfxBindings
& rBindings
, sal_uInt16 nId
)
253 rBindings
.InvalidateShell( *this );
257 const SfxInterface
*pIF
= GetInterface();
260 const SfxSlot
*pSlot
= pIF
->GetSlot(nId
);
263 // Invalidate the Slot itself
264 rBindings
.Invalidate( pSlot
->GetSlotId() );
268 pIF
= pIF
->GetGenoType();
273 SAL_INFO( "sfx.control", "W3: invalidating slot-id unknown in shell" );
277 void SfxShell::HandleOpenXmlFilterSettings(SfxRequest
& rReq
)
281 uno::Reference
< ui::dialogs::XExecutableDialog
> xDialog
= ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext() );
284 catch (const uno::Exception
&)
290 void SfxShell::DoActivate_Impl( SfxViewFrame
*pFrame
, bool bMDI
)
293 const SfxInterface
*p_IF
= GetInterface();
299 "SfxShell::DoActivate() " << this << " " << GetInterface()->GetClassName()
300 << " bMDI " << (bMDI
? "MDI" : ""));
304 // Remember Frame, in which it was activated
305 pImpl
->pFrame
= pFrame
;
306 pImpl
->bActive
= true;
313 void SfxShell::DoDeactivate_Impl( SfxViewFrame
const *pFrame
, bool bMDI
)
316 const SfxInterface
*p_IF
= GetInterface();
322 "SfxShell::DoDeactivate()" << this << " " << GetInterface()->GetClassName()
323 << " bMDI " << (bMDI
? "MDI" : ""));
325 // Only when it comes from a Frame
326 // (not when for instance by popping BASIC-IDE from AppDisp)
327 if ( bMDI
&& pImpl
->pFrame
== pFrame
)
330 pImpl
->pFrame
= nullptr;
331 pImpl
->bActive
= false;
338 bool SfxShell::IsActive() const
340 return pImpl
->bActive
;
343 void SfxShell::Activate
345 bool /*bMDI*/ /* TRUE
346 the <SfxDispatcher>, on which the SfxShell is
347 located, is activated or the SfxShell instance
348 was pushed on an active SfxDispatcher.
349 (compare with SystemWindow::IsMDIActivate())
352 the <SfxViewFrame>, on which SfxDispatcher
353 the SfxShell instance is located, was
354 activated. (for example by a closing dialog) */
357 BroadcastContextForActivation(true);
360 void SfxShell::Deactivate
362 bool /*bMDI*/ /* TRUE
363 the <SfxDispatcher>, on which the SfxShell is
364 located, is inactivated or the SfxShell instance
365 was popped on an active SfxDispatcher.
366 (compare with SystemWindow::IsMDIActivate())
369 the <SfxViewFrame>, on which SfxDispatcher
370 the SfxShell instance is located, was
371 deactivated. (for example by a dialog) */
374 BroadcastContextForActivation(false);
377 bool SfxShell::CanExecuteSlot_Impl( const SfxSlot
&rSlot
)
380 SfxItemPool
&rPool
= GetPool();
381 const sal_uInt16 nId
= rSlot
.GetWhich( rPool
);
382 SfxItemSet
aSet(rPool
, {{nId
, nId
}});
383 SfxStateFunc pFunc
= rSlot
.GetStateFnc();
384 CallState( pFunc
, aSet
);
385 return aSet
.GetItemState(nId
) != SfxItemState::DISABLED
;
388 bool SfxShell::IsConditionalFastCall( const SfxRequest
&rReq
)
390 sal_uInt16 nId
= rReq
.GetSlot();
393 if (nId
== SID_UNDO
|| nId
== SID_REDO
)
395 const SfxItemSet
* pArgs
= rReq
.GetArgs();
396 if (pArgs
&& pArgs
->HasItem(SID_REPAIRPACKAGE
))
403 static void ShellCall_Impl( void* pObj
, void* pArg
)
405 static_cast<SfxShell
*>(pObj
)->ExecuteSlot( *static_cast<SfxRequest
*>(pArg
) );
408 void SfxShell::ExecuteSlot( SfxRequest
& rReq
, bool bAsync
)
414 if( !pImpl
->pExecuter
)
415 pImpl
->pExecuter
.reset( new svtools::AsynchronLink(
416 Link
<void*,void>( this, ShellCall_Impl
) ) );
417 pImpl
->pExecuter
->Call( new SfxRequest( rReq
) );
421 const SfxPoolItem
* SfxShell::ExecuteSlot
423 SfxRequest
&rReq
, // the relayed <SfxRequest>
424 const SfxInterface
* pIF
// default = 0 means get virtually
428 pIF
= GetInterface();
430 sal_uInt16 nSlot
= rReq
.GetSlot();
431 const SfxSlot
* pSlot
= nullptr;
432 if ( nSlot
>= SID_VERB_START
&& nSlot
<= SID_VERB_END
)
433 pSlot
= GetVerbSlot_Impl(nSlot
);
435 pSlot
= pIF
->GetSlot(nSlot
);
436 DBG_ASSERT( pSlot
, "slot not supported" );
438 SfxExecFunc pFunc
= pSlot
->GetExecFnc();
440 CallExec( pFunc
, rReq
);
442 return rReq
.GetReturnValue();
445 const SfxPoolItem
* SfxShell::GetSlotState
447 sal_uInt16 nSlotId
, // Slot-Id to the Slots in question
448 const SfxInterface
* pIF
, // default = 0 means get virtually
449 SfxItemSet
* pStateSet
// SfxItemSet of the Slot-State method
452 // Get Slot on the given Interface
454 pIF
= GetInterface();
455 SfxItemState eState
= SfxItemState::UNKNOWN
;
456 SfxItemPool
&rPool
= GetPool();
458 const SfxSlot
* pSlot
= nullptr;
459 if ( nSlotId
>= SID_VERB_START
&& nSlotId
<= SID_VERB_END
)
460 pSlot
= GetVerbSlot_Impl(nSlotId
);
462 pSlot
= pIF
->GetSlot(nSlotId
);
464 // Map on Which-Id if possible
465 nSlotId
= pSlot
->GetWhich( rPool
);
467 // Get Item and Item status
468 const SfxPoolItem
*pItem
= nullptr;
469 SfxItemSet
aSet( rPool
, {{nSlotId
, nSlotId
}} ); // else pItem dies too soon
472 // Call Status method
473 SfxStateFunc pFunc
= pSlot
->GetStateFnc();
475 CallState( pFunc
, aSet
);
476 eState
= aSet
.GetItemState( nSlotId
, true, &pItem
);
478 // get default Item if possible
479 if ( eState
== SfxItemState::DEFAULT
)
481 if ( SfxItemPool::IsWhich(nSlotId
) )
482 pItem
= &rPool
.GetDefaultItem(nSlotId
);
484 eState
= SfxItemState::DONTCARE
;
488 eState
= SfxItemState::UNKNOWN
;
490 // Evaluate Item and item status and possibly maintain them in pStateSet
491 std::unique_ptr
<SfxPoolItem
> pRetItem
;
492 if ( eState
<= SfxItemState::DISABLED
)
495 pStateSet
->DisableItem(nSlotId
);
498 else if ( eState
== SfxItemState::DONTCARE
)
501 pStateSet
->ClearItem(nSlotId
);
502 pRetItem
.reset( new SfxVoidItem(0) );
506 if ( pStateSet
&& pStateSet
->Put( *pItem
) )
507 return &pStateSet
->Get( pItem
->Which() );
508 pRetItem
.reset(pItem
->Clone());
510 auto pTemp
= pRetItem
.get();
511 DeleteItemOnIdle(std::move(pRetItem
));
516 static SFX_EXEC_STUB(SfxShell
, VerbExec
)
517 static void SfxStubSfxShellVerbState(SfxShell
*, SfxItemSet
& rSet
)
519 SfxShell::VerbState( rSet
);
522 void SfxShell::SetVerbs(const css::uno::Sequence
< css::embed::VerbDescriptor
>& aVerbs
)
524 SfxViewShell
*pViewSh
= dynamic_cast<SfxViewShell
*>( this );
526 DBG_ASSERT(pViewSh
, "Only call SetVerbs at the ViewShell!");
530 // First make all Statecaches dirty, so that no-one no longer tries to use
533 SfxBindings
*pBindings
=
534 pViewSh
->GetViewFrame()->GetDispatcher()->GetBindings();
535 sal_uInt16 nCount
= pImpl
->aSlotArr
.size();
536 for (sal_uInt16 n1
=0; n1
<nCount
; n1
++)
538 sal_uInt16 nId
= SID_VERB_START
+ n1
;
539 pBindings
->Invalidate(nId
, false, true);
544 for (sal_Int32 n
=0; n
<aVerbs
.getLength(); n
++)
546 sal_uInt16 nSlotId
= SID_VERB_START
+ nr
++;
547 DBG_ASSERT(nSlotId
<= SID_VERB_END
, "Too many Verbs!");
548 if (nSlotId
> SID_VERB_END
)
551 SfxSlot
*pNewSlot
= new SfxSlot
;
552 pNewSlot
->nSlotId
= nSlotId
;
553 pNewSlot
->nGroupId
= SfxGroupId::NONE
;
555 // Verb slots must be executed asynchronously, so that they can be
556 // destroyed while executing.
557 pNewSlot
->nFlags
= SfxSlotMode::ASYNCHRON
| SfxSlotMode::CONTAINER
;
558 pNewSlot
->nMasterSlotId
= 0;
559 pNewSlot
->nValue
= 0;
560 pNewSlot
->fnExec
= SFX_STUB_PTR(SfxShell
,VerbExec
);
561 pNewSlot
->fnState
= SFX_STUB_PTR(SfxShell
,VerbState
);
562 pNewSlot
->pType
= nullptr; // HACK(SFX_TYPE(SfxVoidItem)) ???
563 pNewSlot
->nArgDefCount
= 0;
564 pNewSlot
->pFirstArgDef
= nullptr;
565 pNewSlot
->pUnoName
= nullptr;
567 if (!pImpl
->aSlotArr
.empty())
569 SfxSlot
& rSlot
= *pImpl
->aSlotArr
[0];
570 pNewSlot
->pNextSlot
= rSlot
.pNextSlot
;
571 rSlot
.pNextSlot
= pNewSlot
;
574 pNewSlot
->pNextSlot
= pNewSlot
;
576 pImpl
->aSlotArr
.insert(pImpl
->aSlotArr
.begin() + static_cast<sal_uInt16
>(n
), std::unique_ptr
<SfxSlot
>(pNewSlot
));
579 pImpl
->aVerbList
= aVerbs
;
581 // The status of SID_OBJECT is collected in the controller directly on
582 // the Shell, it is thus enough to encourage a new status update
583 SfxBindings
* pBindings
= pViewSh
->GetViewFrame()->GetDispatcher()->GetBindings();
584 pBindings
->Invalidate(SID_OBJECT
, true, true);
587 const css::uno::Sequence
< css::embed::VerbDescriptor
>& SfxShell::GetVerbs() const
589 return pImpl
->aVerbList
;
592 void SfxShell::VerbExec(SfxRequest
& rReq
)
594 sal_uInt16 nId
= rReq
.GetSlot();
595 SfxViewShell
*pViewShell
= GetViewShell();
599 bool bReadOnly
= pViewShell
->GetObjectShell()->IsReadOnly();
600 const css::uno::Sequence
< css::embed::VerbDescriptor
> aList
= pViewShell
->GetVerbs();
602 for (const auto& rVerb
: aList
)
604 // check for ReadOnly verbs
605 if ( bReadOnly
&& !(rVerb
.VerbAttributes
& embed::VerbAttributes::MS_VERBATTR_NEVERDIRTIES
) )
608 // check for verbs that shouldn't appear in the menu
609 if ( !(rVerb
.VerbAttributes
& embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU
) )
612 if (nId
== SID_VERB_START
+ nVerb
++)
614 pViewShell
->DoVerb(rVerb
.VerbID
);
621 void SfxShell::VerbState(SfxItemSet
& )
625 const SfxSlot
* SfxShell::GetVerbSlot_Impl(sal_uInt16 nId
) const
627 css::uno::Sequence
< css::embed::VerbDescriptor
> rList
= pImpl
->aVerbList
;
629 DBG_ASSERT(nId
>= SID_VERB_START
&& nId
<= SID_VERB_END
,"Wrong VerbId!");
630 sal_uInt16 nIndex
= nId
- SID_VERB_START
;
631 DBG_ASSERT(nIndex
< rList
.getLength(),"Wrong VerbId!");
633 if (nIndex
< rList
.getLength())
634 return pImpl
->aSlotArr
[nIndex
].get();
639 SfxObjectShell
* SfxShell::GetObjectShell()
641 if ( GetViewShell() )
642 return GetViewShell()->GetViewFrame()->GetObjectShell();
647 bool SfxShell::HasUIFeature(SfxShellFeature
) const
652 static void DispatcherUpdate_Impl( void*, void* pArg
)
654 static_cast<SfxDispatcher
*>(pArg
)->Update_Impl( true );
655 static_cast<SfxDispatcher
*>(pArg
)->GetBindings()->InvalidateAll(false);
658 void SfxShell::UIFeatureChanged()
660 SfxViewFrame
*pFrame
= GetFrame();
661 if ( pFrame
&& pFrame
->IsVisible() )
663 // Also force an update, if dispatcher is already updated otherwise
664 // something my get stuck in the bunkered tools. Asynchronous call to
665 // prevent recursion.
666 if ( !pImpl
->pUpdater
)
667 pImpl
->pUpdater
.reset( new svtools::AsynchronLink( Link
<void*,void>( this, DispatcherUpdate_Impl
) ) );
669 // Multiple views allowed
670 pImpl
->pUpdater
->Call( pFrame
->GetDispatcher(), true );
674 void SfxShell::SetDisableFlags( SfxDisableFlags nFlags
)
676 pImpl
->nDisableFlags
= nFlags
;
679 SfxDisableFlags
SfxShell::GetDisableFlags() const
681 return pImpl
->nDisableFlags
;
684 std::unique_ptr
<SfxItemSet
> SfxShell::CreateItemSet( sal_uInt16
)
689 void SfxShell::ApplyItemSet( sal_uInt16
, const SfxItemSet
& )
693 void SfxShell::SetContextName (const OUString
& rsContextName
)
695 pImpl
->maContextChangeBroadcaster
.Initialize(rsContextName
);
698 void SfxShell::SetViewShell_Impl( SfxViewShell
* pView
)
700 pImpl
->pViewSh
= pView
;
703 void SfxShell::BroadcastContextForActivation (const bool bIsActivated
)
705 // Avoids activation and de-activation (can be seen on switching view) from causing
706 // the sidebar to re-build. Such switching can happen as we change view to render
707 // using LOK for example, and is un-necessary for Online.
708 if (comphelper::LibreOfficeKit::isDialogPainting())
711 SfxViewFrame
* pViewFrame
= GetFrame();
712 if (pViewFrame
!= nullptr)
715 pImpl
->maContextChangeBroadcaster
.Activate(pViewFrame
->GetFrame().GetFrameInterface());
717 pImpl
->maContextChangeBroadcaster
.Deactivate(pViewFrame
->GetFrame().GetFrameInterface());
721 bool SfxShell::SetContextBroadcasterEnabled (const bool bIsEnabled
)
723 return pImpl
->maContextChangeBroadcaster
.SetBroadcasterEnabled(bIsEnabled
);
726 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */