Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / control / bindings.cxx
blobd648fdf8753703c60210c5de42dc6e2fff69ded6
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 <sal/config.h>
22 #include <iomanip>
24 #include <sal/log.hxx>
25 #include <svl/itempool.hxx>
26 #include <svl/itemiter.hxx>
27 #include <svl/eitem.hxx>
28 #include <svl/intitem.hxx>
29 #include <svl/stritem.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/timer.hxx>
32 #include <com/sun/star/frame/XDispatch.hpp>
33 #include <com/sun/star/frame/XDispatchProvider.hpp>
34 #include <com/sun/star/frame/DispatchResultState.hpp>
35 #include <itemdel.hxx>
37 //Includes below due to nInReschedule
38 #include <sfx2/bindings.hxx>
39 #include <sfx2/msg.hxx>
40 #include <statcach.hxx>
41 #include <sfx2/ctrlitem.hxx>
42 #include <sfx2/app.hxx>
43 #include <sfx2/dispatch.hxx>
44 #include <sfx2/module.hxx>
45 #include <sfx2/request.hxx>
46 #include <workwin.hxx>
47 #include <unoctitm.hxx>
48 #include <sfx2/viewfrm.hxx>
49 #include <sfx2/objsh.hxx>
50 #include <sfx2/msgpool.hxx>
52 #include <cstddef>
53 #include <memory>
54 #include <unordered_map>
55 #include <utility>
56 #include <vector>
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::uno;
60 using namespace ::com::sun::star::util;
62 #define TIMEOUT_FIRST 300
63 #define TIMEOUT_UPDATING 20
65 struct SfxFoundCache_Impl
67 sal_uInt16 nWhichId; // If available: Which-Id, else: nSlotId
68 const SfxSlot* pSlot; // Pointer to <Master-Slot>
69 SfxStateCache& rCache; // Pointer to StatusCache
71 SfxFoundCache_Impl(sal_uInt16 nW, const SfxSlot *pS, SfxStateCache& rC)
72 : nWhichId(nW)
73 , pSlot(pS)
74 , rCache(rC)
78 class SfxFoundCacheArr_Impl
80 typedef std::vector<std::unique_ptr<SfxFoundCache_Impl> > DataType;
81 DataType maData;
83 public:
85 SfxFoundCache_Impl& operator[] ( size_t i )
87 return *maData[i];
90 size_t size() const
92 return maData.size();
95 void push_back( SfxFoundCache_Impl* p )
97 maData.push_back(std::unique_ptr<SfxFoundCache_Impl>(p));
101 class SfxBindings_Impl
103 public:
104 css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
105 css::uno::Reference< css::frame::XDispatchProvider > xProv;
106 std::unique_ptr<SfxWorkWindow> mxWorkWin;
107 SfxBindings* pSubBindings;
108 std::vector<std::unique_ptr<SfxStateCache>> pCaches; // One cache for each binding
109 std::size_t nCachedFunc1; // index for the last one called
110 std::size_t nCachedFunc2; // index for the second last called
111 std::size_t nMsgPos; // Message-Position relative the one to be updated
112 bool bContextChanged;
113 bool bMsgDirty; // Has a MessageServer been invalidated?
114 bool bAllMsgDirty; // Has a MessageServer been invalidated?
115 bool bAllDirty; // After InvalidateAll
116 bool bCtrlReleased; // while EnterRegistrations
117 AutoTimer aAutoTimer; // for volatile Slots
118 bool bInUpdate; // for Assertions
119 bool bInNextJob; // for Assertions
120 bool bFirstRound; // First round in Update
121 sal_uInt16 nOwnRegLevel; // Counts the real Locks, except those of the Super Bindings
122 std::unordered_map< sal_uInt16, bool >
123 m_aInvalidateSlots; // store slots which are invalidated while in update
126 SfxBindings::SfxBindings()
127 : pImpl(new SfxBindings_Impl),
128 pDispatcher(nullptr),
129 nRegLevel(1) // first becomes 0, when the Dispatcher is set
132 pImpl->nMsgPos = 0;
133 pImpl->bAllMsgDirty = true;
134 pImpl->bContextChanged = false;
135 pImpl->bMsgDirty = true;
136 pImpl->bAllDirty = true;
137 pImpl->nCachedFunc1 = 0;
138 pImpl->nCachedFunc2 = 0;
139 pImpl->bCtrlReleased = false;
140 pImpl->bFirstRound = false;
141 pImpl->bInNextJob = false;
142 pImpl->bInUpdate = false;
143 pImpl->pSubBindings = nullptr;
144 pImpl->nOwnRegLevel = nRegLevel;
146 // all caches are valid (no pending invalidate-job)
147 // create the list of caches
148 pImpl->aAutoTimer.SetInvokeHandler( LINK(this, SfxBindings, NextJob) );
149 pImpl->aAutoTimer.SetDebugName( "sfx::SfxBindings aAutoTimer" );
153 SfxBindings::~SfxBindings()
155 /* [Description]
157 Destructor of the SfxBindings class. The one, for each <SfxApplication>
158 existing Instance is automatically destroyed by the <SfxApplication>
159 after the execution of <SfxApplication::Exit()>.
161 The still existing <SfxControllerItem> instances, which are registered
162 by the SfxBindings instance, are automatically destroyed in the Destructor.
163 These are usually the Floating-Toolboxen, Value-Sets
164 etc. Arrays of SfxControllerItems may at this time no longer exist.
168 // The SubBindings should not be locked!
169 pImpl->pSubBindings = nullptr;
171 ENTERREGISTRATIONS();
173 pImpl->aAutoTimer.Stop();
174 DeleteControllers_Impl();
176 // Delete Caches
177 pImpl->pCaches.clear();
179 pImpl->mxWorkWin.reset();
183 void SfxBindings::DeleteControllers_Impl()
185 // in the first round delete Controllers
186 std::size_t nCount = pImpl->pCaches.size();
187 std::size_t nCache;
188 for ( nCache = 0; nCache < nCount; ++nCache )
190 // Remember were you are
191 SfxStateCache *pCache = pImpl->pCaches[nCache].get();
192 sal_uInt16 nSlotId = pCache->GetId();
194 // Re-align, because the cache may have been reduced
195 std::size_t nNewCount = pImpl->pCaches.size();
196 if ( nNewCount < nCount )
198 nCache = GetSlotPos(nSlotId);
199 if ( nCache >= nNewCount ||
200 nSlotId != pImpl->pCaches[nCache]->GetId() )
201 --nCache;
202 nCount = nNewCount;
206 // Delete all Caches
207 for ( nCache = pImpl->pCaches.size(); nCache > 0; --nCache )
209 // Get Cache via css::sdbcx::Index
210 SfxStateCache *pCache = pImpl->pCaches[ nCache-1 ].get();
212 // unbind all controllers in the cache
213 SfxControllerItem *pNext;
214 for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
215 pCtrl; pCtrl = pNext )
217 pNext = pCtrl->GetItemLink();
218 pCtrl->UnBind();
221 if ( pCache->GetInternalController() )
222 pCache->GetInternalController()->UnBind();
224 // Delete Cache
225 pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1);
230 void SfxBindings::HidePopups( bool bHide )
232 // Hide SfxChildWindows
233 DBG_ASSERT( pDispatcher, "HidePopups not allowed without dispatcher" );
234 if ( pImpl->mxWorkWin )
235 pImpl->mxWorkWin->HidePopups_Impl( bHide );
238 void SfxBindings::Update_Impl(SfxStateCache& rCache /*The up to date SfxStatusCache*/)
240 if (rCache.GetDispatch().is() && rCache.GetItemLink())
242 rCache.SetCachedState(true);
243 if (!rCache.GetInternalController())
244 return;
247 if ( !pDispatcher )
248 return;
250 // gather together all with the same status method which are dirty
251 SfxDispatcher &rDispat = *pDispatcher;
252 const SfxSlot *pRealSlot = nullptr;
253 const SfxSlotServer* pMsgServer = nullptr;
254 SfxFoundCacheArr_Impl aFound;
255 std::unique_ptr<SfxItemSet> pSet = CreateSet_Impl(rCache, pRealSlot, &pMsgServer, aFound);
256 bool bUpdated = false;
257 if ( pSet )
259 // Query Status
260 if ( rDispat.FillState_( *pMsgServer, *pSet, pRealSlot ) )
262 // Post Status
263 for ( size_t nPos = 0; nPos < aFound.size(); ++nPos )
265 const SfxFoundCache_Impl& rFound = aFound[nPos];
266 sal_uInt16 nWhich = rFound.nWhichId;
267 const SfxPoolItem *pItem = nullptr;
268 SfxItemState eState = pSet->GetItemState(nWhich, true, &pItem);
269 if ( eState == SfxItemState::DEFAULT && SfxItemPool::IsWhich(nWhich) )
270 pItem = &pSet->Get(nWhich);
271 UpdateControllers_Impl( rFound, pItem, eState );
273 bUpdated = true;
276 pSet.reset();
279 if (!bUpdated)
281 SfxFoundCache_Impl aFoundCache(0, pRealSlot, rCache);
282 UpdateControllers_Impl( aFoundCache, nullptr, SfxItemState::DISABLED);
286 void SfxBindings::InvalidateSlotsInMap_Impl()
288 for (auto const& slot : pImpl->m_aInvalidateSlots)
289 Invalidate( slot.first );
291 pImpl->m_aInvalidateSlots.clear();
295 void SfxBindings::AddSlotToInvalidateSlotsMap_Impl( sal_uInt16 nId )
297 pImpl->m_aInvalidateSlots[nId] = true;
301 void SfxBindings::Update
303 sal_uInt16 nId // the bound and up-to-date Slot-Id
306 if ( pDispatcher )
307 pDispatcher->Flush();
309 if ( pImpl->pSubBindings )
310 pImpl->pSubBindings->Update( nId );
312 SfxStateCache* pCache = GetStateCache( nId );
313 if ( !pCache )
314 return;
316 pImpl->bInUpdate = true;
317 if ( pImpl->bMsgDirty )
319 UpdateSlotServer_Impl();
320 pCache = GetStateCache( nId );
323 if (pCache)
325 bool bInternalUpdate = true;
326 if( pCache->GetDispatch().is() && pCache->GetItemLink() )
328 pCache->SetCachedState(true);
329 bInternalUpdate = ( pCache->GetInternalController() != nullptr );
332 if ( bInternalUpdate )
334 // Query Status
335 const SfxSlotServer* pMsgServer = pDispatcher ? pCache->GetSlotServer(*pDispatcher, pImpl->xProv) : nullptr;
336 if ( !pCache->IsControllerDirty() )
338 pImpl->bInUpdate = false;
339 InvalidateSlotsInMap_Impl();
340 return;
342 if (!pMsgServer)
344 pCache->SetState(SfxItemState::DISABLED, nullptr);
345 pImpl->bInUpdate = false;
346 InvalidateSlotsInMap_Impl();
347 return;
350 Update_Impl(*pCache);
353 pImpl->bAllDirty = false;
356 pImpl->bInUpdate = false;
357 InvalidateSlotsInMap_Impl();
361 void SfxBindings::Update()
363 if ( pImpl->pSubBindings )
364 pImpl->pSubBindings->Update();
366 if ( !pDispatcher )
367 return;
369 if ( nRegLevel )
370 return;
372 pImpl->bInUpdate = true;
373 pDispatcher->Flush();
374 pDispatcher->Update_Impl();
375 while ( !NextJob_Impl(nullptr) )
376 ; // loop
377 pImpl->bInUpdate = false;
378 InvalidateSlotsInMap_Impl();
382 void SfxBindings::SetState
384 const SfxItemSet& rSet // status values to be set
387 // when locked then only invalidate
388 if ( nRegLevel )
390 SfxItemIter aIter(rSet);
391 for ( const SfxPoolItem *pItem = aIter.GetCurItem();
392 pItem;
393 pItem = aIter.NextItem() )
394 Invalidate( pItem->Which() );
396 else
398 // Status may be accepted only if all slot-pointers are set
399 if ( pImpl->bMsgDirty )
400 UpdateSlotServer_Impl();
402 // Iterate over the itemset, update if the slot bound
403 //! Bug: Use WhichIter and possibly send VoidItems up
404 SfxItemIter aIter(rSet);
405 for ( const SfxPoolItem *pItem = aIter.GetCurItem();
406 pItem;
407 pItem = aIter.NextItem() )
409 SfxStateCache* pCache =
410 GetStateCache( rSet.GetPool()->GetSlotId(pItem->Which()) );
411 if ( pCache )
413 // Update status
414 if ( !pCache->IsControllerDirty() )
415 pCache->Invalidate(false);
416 pCache->SetState( SfxItemState::DEFAULT, pItem );
418 //! Not implemented: Updates from EnumSlots via master slots
425 void SfxBindings::SetState
427 const SfxPoolItem& rItem // Status value to be set
430 if ( nRegLevel )
432 Invalidate( rItem.Which() );
434 else
436 // Status may be accepted only if all slot-pointers are set
437 if ( pImpl->bMsgDirty )
438 UpdateSlotServer_Impl();
440 //update if the slot bound
441 DBG_ASSERT( SfxItemPool::IsSlot( rItem.Which() ),
442 "cannot set items with which-id" );
443 SfxStateCache* pCache = GetStateCache( rItem.Which() );
444 if ( pCache )
446 // Update Status
447 if ( !pCache->IsControllerDirty() )
448 pCache->Invalidate(false);
449 pCache->SetState( SfxItemState::DEFAULT, &rItem );
451 //! Not implemented: Updates from EnumSlots via master slots
457 SfxStateCache* SfxBindings::GetAnyStateCache_Impl( sal_uInt16 nId )
459 SfxStateCache* pCache = GetStateCache( nId );
460 if ( !pCache && pImpl->pSubBindings )
461 return pImpl->pSubBindings->GetAnyStateCache_Impl( nId );
462 return pCache;
465 SfxStateCache* SfxBindings::GetStateCache
467 sal_uInt16 nId /* Slot-Id, which SfxStatusCache is to be found */
470 return GetStateCache(nId, nullptr);
473 SfxStateCache* SfxBindings::GetStateCache
475 sal_uInt16 nId, /* Slot-Id, which SfxStatusCache is to be found */
476 std::size_t * pPos /* NULL for instance the position from which the
477 bindings are to be searched binary. Returns the
478 position back for where the nId was found,
479 or where it was inserted. */
482 // is the specified function bound?
483 const std::size_t nStart = ( pPos ? *pPos : 0 );
484 const std::size_t nPos = GetSlotPos( nId, nStart );
486 if ( nPos < pImpl->pCaches.size() &&
487 pImpl->pCaches[nPos]->GetId() == nId )
489 if ( pPos )
490 *pPos = nPos;
491 return pImpl->pCaches[nPos].get();
493 return nullptr;
497 void SfxBindings::InvalidateAll
499 bool bWithMsg /* true Mark Slot Server as invalid
500 false Slot Server remains valid */
503 DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
505 if ( pImpl->pSubBindings )
506 pImpl->pSubBindings->InvalidateAll( bWithMsg );
508 // everything is already set dirty or downing => nothing to do
509 if ( !pDispatcher ||
510 ( pImpl->bAllDirty && ( !bWithMsg || pImpl->bAllMsgDirty ) ) ||
511 SfxGetpApp()->IsDowning() )
513 return;
516 pImpl->bAllMsgDirty = pImpl->bAllMsgDirty || bWithMsg;
517 pImpl->bMsgDirty = pImpl->bMsgDirty || pImpl->bAllMsgDirty || bWithMsg;
518 pImpl->bAllDirty = true;
520 for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
521 pCache->Invalidate(bWithMsg);
523 pImpl->nMsgPos = 0;
524 if ( !nRegLevel )
526 pImpl->aAutoTimer.Stop();
527 pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
528 pImpl->aAutoTimer.Start();
533 void SfxBindings::Invalidate
535 const sal_uInt16* pIds /* numerically sorted NULL-terminated array of
536 slot IDs (individual, not as a couple!) */
539 if ( pImpl->bInUpdate )
541 sal_Int32 i = 0;
542 while ( pIds[i] != 0 )
543 AddSlotToInvalidateSlotsMap_Impl( pIds[i++] );
545 if ( pImpl->pSubBindings )
546 pImpl->pSubBindings->Invalidate( pIds );
547 return;
550 if ( pImpl->pSubBindings )
551 pImpl->pSubBindings->Invalidate( pIds );
553 // everything is already set dirty or downing => nothing to do
554 if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
555 return;
557 // Search binary in always smaller areas
558 for ( std::size_t n = GetSlotPos(*pIds);
559 *pIds && n < pImpl->pCaches.size();
560 n = GetSlotPos(*pIds, n) )
562 // If SID is ever bound, then invalidate the cache
563 SfxStateCache *pCache = pImpl->pCaches[n].get();
564 if ( pCache->GetId() == *pIds )
565 pCache->Invalidate(false);
567 // Next SID
568 if ( !*++pIds )
569 break;
570 assert( *pIds > *(pIds-1) );
573 // if not enticed to start update timer
574 pImpl->nMsgPos = 0;
575 if ( !nRegLevel )
577 pImpl->aAutoTimer.Stop();
578 pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
579 pImpl->aAutoTimer.Start();
584 void SfxBindings::InvalidateShell
586 const SfxShell& rSh, /* <SfxShell> whose Slot-Ids should be
587 invalidated */
588 bool bDeep /* true
589 also the SfxShell's inherited slot IDs are invalidated
591 false
592 the inherited and not overridden Slot-Ids are
593 invalidated */
594 // for now always bDeep
597 DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
599 if ( pImpl->pSubBindings )
600 pImpl->pSubBindings->InvalidateShell( rSh, bDeep );
602 if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
603 return;
605 // flush now already, it is done in GetShellLevel (rsh) anyway,
606 // important so that is set correctly: pImpl-> ball(Msg)Dirty
607 pDispatcher->Flush();
609 if ((pImpl->bAllDirty && pImpl->bAllMsgDirty) || SfxGetpApp()->IsDowning())
611 // if the next one is anyway, then all the servers are collected
612 return;
615 // Find Level
616 sal_uInt16 nLevel = pDispatcher->GetShellLevel(rSh);
617 if ( nLevel == USHRT_MAX )
618 return;
620 for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
622 const SfxSlotServer *pMsgServer =
623 pCache->GetSlotServer(*pDispatcher, pImpl->xProv);
624 if ( pMsgServer && pMsgServer->GetShellLevel() == nLevel )
625 pCache->Invalidate(false);
627 pImpl->nMsgPos = 0;
628 if ( !nRegLevel )
630 pImpl->aAutoTimer.Stop();
631 pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
632 pImpl->aAutoTimer.Start();
633 pImpl->bFirstRound = true;
638 void SfxBindings::Invalidate
640 sal_uInt16 nId // Status value to be set
643 if ( pImpl->bInUpdate )
645 AddSlotToInvalidateSlotsMap_Impl( nId );
646 if ( pImpl->pSubBindings )
647 pImpl->pSubBindings->Invalidate( nId );
648 return;
651 if ( pImpl->pSubBindings )
652 pImpl->pSubBindings->Invalidate( nId );
654 if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
655 return;
657 SfxStateCache* pCache = GetStateCache(nId);
658 if ( pCache )
660 pCache->Invalidate(false);
661 pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos);
662 if ( !nRegLevel )
664 pImpl->aAutoTimer.Stop();
665 pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
666 pImpl->aAutoTimer.Start();
672 void SfxBindings::Invalidate
674 sal_uInt16 nId, // Status value to be set
675 bool bWithItem, // Clear StateCache?
676 bool bWithMsg // Get new SlotServer?
679 DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
681 if ( pImpl->pSubBindings )
682 pImpl->pSubBindings->Invalidate( nId, bWithItem, bWithMsg );
684 if ( SfxGetpApp()->IsDowning() )
685 return;
687 SfxStateCache* pCache = GetStateCache(nId);
688 if ( !pCache )
689 return;
691 if ( bWithItem )
692 pCache->ClearCache();
693 pCache->Invalidate(bWithMsg);
695 if ( !pDispatcher || pImpl->bAllDirty )
696 return;
698 pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos);
699 if ( !nRegLevel )
701 pImpl->aAutoTimer.Stop();
702 pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
703 pImpl->aAutoTimer.Start();
708 std::size_t SfxBindings::GetSlotPos( sal_uInt16 nId, std::size_t nStartSearchAt )
710 // answer immediately if a function-seek comes repeated
711 if ( pImpl->nCachedFunc1 < pImpl->pCaches.size() &&
712 pImpl->pCaches[pImpl->nCachedFunc1]->GetId() == nId )
714 return pImpl->nCachedFunc1;
716 if ( pImpl->nCachedFunc2 < pImpl->pCaches.size() &&
717 pImpl->pCaches[pImpl->nCachedFunc2]->GetId() == nId )
719 // swap the caches
720 std::swap(pImpl->nCachedFunc1, pImpl->nCachedFunc2);
721 return pImpl->nCachedFunc1;
724 // binary search, if not found, seek to target-position
725 if ( pImpl->pCaches.size() <= nStartSearchAt )
727 return 0;
729 if ( pImpl->pCaches.size() == (nStartSearchAt+1) )
731 return pImpl->pCaches[nStartSearchAt]->GetId() >= nId ? 0 : 1;
733 std::size_t nLow = nStartSearchAt;
734 std::size_t nMid = 0;
735 std::size_t nHigh = 0;
736 bool bFound = false;
737 nHigh = pImpl->pCaches.size() - 1;
738 while ( !bFound && nLow <= nHigh )
740 nMid = (nLow + nHigh) >> 1;
741 DBG_ASSERT( nMid < pImpl->pCaches.size(), "bsearch is buggy" );
742 int nDiff = static_cast<int>(nId) - static_cast<int>( (pImpl->pCaches[nMid])->GetId() );
743 if ( nDiff < 0)
744 { if ( nMid == 0 )
745 break;
746 nHigh = nMid - 1;
748 else if ( nDiff > 0 )
749 { nLow = nMid + 1;
750 if ( nLow == 0 )
751 break;
753 else
754 bFound = true;
756 std::size_t nPos = bFound ? nMid : nLow;
757 DBG_ASSERT( nPos <= pImpl->pCaches.size(), "" );
758 DBG_ASSERT( nPos == pImpl->pCaches.size() ||
759 nId <= pImpl->pCaches[nPos]->GetId(), "" );
760 DBG_ASSERT( nPos == nStartSearchAt ||
761 nId > pImpl->pCaches[nPos-1]->GetId(), "" );
762 DBG_ASSERT( ( (nPos+1) >= pImpl->pCaches.size() ) ||
763 nId < pImpl->pCaches[nPos+1]->GetId(), "" );
764 pImpl->nCachedFunc2 = pImpl->nCachedFunc1;
765 pImpl->nCachedFunc1 = nPos;
766 return nPos;
769 void SfxBindings::RegisterInternal_Impl( SfxControllerItem& rItem )
771 Register_Impl( rItem, true );
775 void SfxBindings::Register( SfxControllerItem& rItem )
777 Register_Impl( rItem, false );
780 void SfxBindings::Register_Impl( SfxControllerItem& rItem, bool bInternal )
782 // DBG_ASSERT( nRegLevel > 0, "registration without EnterRegistrations" );
783 DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Register while status-updating" );
785 // insert new cache if it does not already exist
786 sal_uInt16 nId = rItem.GetId();
787 std::size_t nPos = GetSlotPos(nId);
788 if ( nPos >= pImpl->pCaches.size() ||
789 pImpl->pCaches[nPos]->GetId() != nId )
791 pImpl->pCaches.insert( pImpl->pCaches.begin() + nPos, std::make_unique<SfxStateCache>(nId) );
792 DBG_ASSERT( nPos == 0 ||
793 pImpl->pCaches[nPos]->GetId() >
794 pImpl->pCaches[nPos-1]->GetId(), "" );
795 DBG_ASSERT( (nPos == pImpl->pCaches.size()-1) ||
796 pImpl->pCaches[nPos]->GetId() <
797 pImpl->pCaches[nPos+1]->GetId(), "" );
798 pImpl->bMsgDirty = true;
801 // enqueue the new binding
802 if ( bInternal )
804 pImpl->pCaches[nPos]->SetInternalController( &rItem );
806 else
808 SfxControllerItem *pOldItem = pImpl->pCaches[nPos]->ChangeItemLink(&rItem);
809 rItem.ChangeItemLink(pOldItem);
814 void SfxBindings::Release( SfxControllerItem& rItem )
816 DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Release while status-updating" );
817 ENTERREGISTRATIONS();
819 // find the bound function
820 sal_uInt16 nId = rItem.GetId();
821 std::size_t nPos = GetSlotPos(nId);
822 SfxStateCache* pCache = (nPos < pImpl->pCaches.size()) ? pImpl->pCaches[nPos].get() : nullptr;
823 if ( pCache && pCache->GetId() == nId )
825 if ( pCache->GetInternalController() == &rItem )
827 pCache->ReleaseInternalController();
829 else
831 // is this the first binding in the list?
832 SfxControllerItem* pItem = pCache->GetItemLink();
833 if ( pItem == &rItem )
834 pCache->ChangeItemLink( rItem.GetItemLink() );
835 else
837 // search the binding in the list
838 while ( pItem && pItem->GetItemLink() != &rItem )
839 pItem = pItem->GetItemLink();
841 // unlink it if it was found
842 if ( pItem )
843 pItem->ChangeItemLink( rItem.GetItemLink() );
847 // was this the last controller?
848 if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() )
850 pImpl->bCtrlReleased = true;
854 LEAVEREGISTRATIONS();
858 const SfxPoolItem* SfxBindings::ExecuteSynchron( sal_uInt16 nId, const SfxPoolItem** ppItems )
860 if( !nId || !pDispatcher )
861 return nullptr;
863 return Execute_Impl( nId, ppItems, 0, SfxCallMode::SYNCHRON, nullptr );
866 bool SfxBindings::Execute( sal_uInt16 nId, const SfxPoolItem** ppItems, SfxCallMode nCallMode )
868 if( !nId || !pDispatcher )
869 return false;
871 const SfxPoolItem* pRet = Execute_Impl( nId, ppItems, 0, nCallMode, nullptr );
872 return ( pRet != nullptr );
875 const SfxPoolItem* SfxBindings::Execute_Impl( sal_uInt16 nId, const SfxPoolItem** ppItems, sal_uInt16 nModi, SfxCallMode nCallMode,
876 const SfxPoolItem **ppInternalArgs, bool bGlobalOnly )
878 SfxStateCache *pCache = GetStateCache( nId );
879 if ( !pCache )
881 SfxBindings *pBind = pImpl->pSubBindings;
882 while ( pBind )
884 if ( pBind->GetStateCache( nId ) )
885 return pBind->Execute_Impl( nId, ppItems, nModi, nCallMode, ppInternalArgs, bGlobalOnly );
886 pBind = pBind->pImpl->pSubBindings;
890 SfxDispatcher &rDispatcher = *pDispatcher;
891 rDispatcher.Flush();
893 // get SlotServer (Slot+ShellLevel) and Shell from cache
894 std::unique_ptr<SfxStateCache> xCache;
895 if ( !pCache )
897 // Execution of non cached slots (Accelerators don't use Controllers)
898 // slot is uncached, use SlotCache to handle external dispatch providers
899 xCache.reset(new SfxStateCache(nId));
900 pCache = xCache.get();
901 pCache->GetSlotServer( rDispatcher, pImpl->xProv );
904 if ( pCache->GetDispatch().is() )
906 DBG_ASSERT( !ppInternalArgs, "Internal args get lost when dispatched!" );
908 SfxItemPool &rPool = GetDispatcher()->GetFrame()->GetObjectShell()->GetPool();
909 SfxRequest aReq( nId, nCallMode, rPool );
910 aReq.SetModifier( nModi );
911 if( ppItems )
912 while( *ppItems )
913 aReq.AppendItem( **ppItems++ );
915 // cache binds to an external dispatch provider
916 sal_Int16 eRet = pCache->Dispatch( aReq.GetArgs(), nCallMode == SfxCallMode::SYNCHRON );
917 std::unique_ptr<SfxPoolItem> pPool;
918 if ( eRet == css::frame::DispatchResultState::DONTKNOW )
919 pPool.reset( new SfxVoidItem( nId ) );
920 else
921 pPool.reset( new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS) );
923 auto pTemp = pPool.get();
924 DeleteItemOnIdle( std::move(pPool) );
925 return pTemp;
928 // slot is handled internally by SfxDispatcher
929 if ( pImpl->bMsgDirty )
930 UpdateSlotServer_Impl();
932 SfxShell *pShell=nullptr;
933 const SfxSlot *pSlot=nullptr;
935 const SfxSlotServer* pServer = pCache->GetSlotServer( rDispatcher, pImpl->xProv );
936 if ( !pServer )
938 return nullptr;
940 else
942 pShell = rDispatcher.GetShell( pServer->GetShellLevel() );
943 pSlot = pServer->GetSlot();
946 if ( bGlobalOnly )
947 if ( dynamic_cast< const SfxModule *>( pShell ) == nullptr && dynamic_cast< const SfxApplication *>( pShell ) == nullptr && dynamic_cast< const SfxViewFrame *>( pShell ) == nullptr )
948 return nullptr;
950 SfxItemPool &rPool = pShell->GetPool();
951 SfxRequest aReq( nId, nCallMode, rPool );
952 aReq.SetModifier( nModi );
953 if( ppItems )
954 while( *ppItems )
955 aReq.AppendItem( **ppItems++ );
956 if ( ppInternalArgs )
958 SfxAllItemSet aSet( rPool );
959 for ( const SfxPoolItem **pArg = ppInternalArgs; *pArg; ++pArg )
960 aSet.Put( **pArg );
961 aReq.SetInternalArgs_Impl( aSet );
964 Execute_Impl( aReq, pSlot, pShell );
966 const SfxPoolItem* pRet = aReq.GetReturnValue();
967 if ( !pRet )
969 std::unique_ptr<SfxPoolItem> pVoid(new SfxVoidItem( nId ));
970 pRet = pVoid.get();
971 DeleteItemOnIdle( std::move(pVoid) );
974 return pRet;
977 void SfxBindings::Execute_Impl( SfxRequest& aReq, const SfxSlot* pSlot, SfxShell* pShell )
979 SfxItemPool &rPool = pShell->GetPool();
981 if ( SfxSlotKind::Attribute == pSlot->GetKind() )
983 // Which value has to be mapped for Attribute slots
984 const sal_uInt16 nSlotId = pSlot->GetSlotId();
985 aReq.SetSlot( nSlotId );
986 if ( pSlot->IsMode(SfxSlotMode::TOGGLE) )
988 // The value is attached to a toggleable attribute (Bools)
989 sal_uInt16 nWhich = pSlot->GetWhich(rPool);
990 SfxItemSet aSet(rPool, {{nWhich, nWhich}});
991 SfxStateFunc aFunc = pSlot->GetStateFnc();
992 pShell->CallState( aFunc, aSet );
993 const SfxPoolItem *pOldItem;
994 SfxItemState eState = aSet.GetItemState(nWhich, true, &pOldItem);
995 if ( eState == SfxItemState::DISABLED )
996 return;
998 if ( SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich) )
999 pOldItem = &aSet.Get(nWhich);
1001 if ( SfxItemState::SET == eState ||
1002 ( SfxItemState::DEFAULT == eState &&
1003 SfxItemPool::IsWhich(nWhich) &&
1004 pOldItem ) )
1006 if ( auto pOldBoolItem = dynamic_cast< const SfxBoolItem *>( pOldItem ) )
1008 // we can toggle Bools
1009 bool bOldValue = pOldBoolItem->GetValue();
1010 std::unique_ptr<SfxBoolItem> pNewItem(static_cast<SfxBoolItem*>(pOldItem->Clone()));
1011 pNewItem->SetValue( !bOldValue );
1012 aReq.AppendItem( *pNewItem );
1014 else if ( dynamic_cast< const SfxEnumItemInterface *>( pOldItem ) != nullptr &&
1015 static_cast<const SfxEnumItemInterface *>(pOldItem)->HasBoolValue())
1017 // and Enums with Bool-Interface
1018 std::unique_ptr<SfxEnumItemInterface> pNewItem(
1019 static_cast<SfxEnumItemInterface*>(pOldItem->Clone()));
1020 pNewItem->SetBoolValue(!static_cast<const SfxEnumItemInterface *>(pOldItem)->GetBoolValue());
1021 aReq.AppendItem( *pNewItem );
1023 else {
1024 OSL_FAIL( "Toggle only for Enums and Bools allowed" );
1027 else if ( SfxItemState::DONTCARE == eState )
1029 // Create one Status-Item for each Factory
1030 std::unique_ptr<SfxPoolItem> pNewItem = pSlot->GetType()->CreateItem();
1031 DBG_ASSERT( pNewItem, "Toggle to slot without ItemFactory" );
1032 pNewItem->SetWhich( nWhich );
1034 if ( auto pNewBoolItem = dynamic_cast<SfxBoolItem *>( pNewItem.get() ) )
1036 // we can toggle Bools
1037 pNewBoolItem->SetValue( true );
1038 aReq.AppendItem( *pNewItem );
1040 else if ( dynamic_cast< const SfxEnumItemInterface *>( pNewItem.get() ) != nullptr &&
1041 static_cast<SfxEnumItemInterface *>(pNewItem.get())->HasBoolValue())
1043 // and Enums with Bool-Interface
1044 static_cast<SfxEnumItemInterface*>(pNewItem.get())->SetBoolValue(true);
1045 aReq.AppendItem( *pNewItem );
1047 else {
1048 OSL_FAIL( "Toggle only for Enums and Bools allowed" );
1051 else {
1052 OSL_FAIL( "suspicious Toggle-Slot" );
1056 pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
1058 else
1059 pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
1063 void SfxBindings::UpdateSlotServer_Impl()
1065 // synchronize
1066 pDispatcher->Flush();
1068 if ( pImpl->bAllMsgDirty )
1070 if ( !nRegLevel )
1072 pImpl->bContextChanged = false;
1074 else
1075 pImpl->bContextChanged = true;
1078 for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
1080 //GetSlotServer can modify pImpl->pCaches
1081 pCache->GetSlotServer(*pDispatcher, pImpl->xProv);
1083 pImpl->bMsgDirty = pImpl->bAllMsgDirty = false;
1085 Broadcast( SfxHint(SfxHintId::DocChanged) );
1089 std::unique_ptr<SfxItemSet> SfxBindings::CreateSet_Impl
1091 SfxStateCache& rCache, // in: Status-Cache from nId
1092 const SfxSlot*& pRealSlot, // out: RealSlot to nId
1093 const SfxSlotServer** pMsgServer, // out: Slot-Server to nId
1094 SfxFoundCacheArr_Impl& rFound // out: List of Caches for Siblings
1097 DBG_ASSERT( !pImpl->bMsgDirty, "CreateSet_Impl with dirty MessageServer" );
1098 assert(pDispatcher);
1100 const SfxSlotServer* pMsgSvr = rCache.GetSlotServer(*pDispatcher, pImpl->xProv);
1101 if (!pMsgSvr)
1102 return nullptr;
1104 pRealSlot = nullptr;
1105 *pMsgServer = pMsgSvr;
1107 sal_uInt16 nShellLevel = pMsgSvr->GetShellLevel();
1108 SfxShell *pShell = pDispatcher->GetShell( nShellLevel );
1109 if ( !pShell ) // rare GPF when browsing through update from Inet-Notify
1110 return nullptr;
1112 SfxItemPool &rPool = pShell->GetPool();
1114 // get the status method, which is served by the rCache
1115 SfxStateFunc pFnc = nullptr;
1116 pRealSlot = pMsgSvr->GetSlot();
1118 pFnc = pRealSlot->GetStateFnc();
1120 // the RealSlot is always on
1121 SfxFoundCache_Impl *pFound = new SfxFoundCache_Impl(
1122 pRealSlot->GetWhich(rPool), pRealSlot, rCache);
1123 rFound.push_back( pFound );
1125 // Search through the bindings for slots served by the same function. This , // will only affect slots which are present in the found interface.
1127 // The position of the Statecaches in StateCache-Array
1128 std::size_t nCachePos = pImpl->nMsgPos;
1129 const SfxSlot *pSibling = pRealSlot->GetNextSlot();
1131 // the Slots ODF and interfaces are linked in a circle
1132 while ( pSibling > pRealSlot )
1134 SfxStateFunc pSiblingFnc=nullptr;
1135 SfxStateCache *pSiblingCache =
1136 GetStateCache( pSibling->GetSlotId(), &nCachePos );
1138 // Is the slot cached ?
1139 if ( pSiblingCache )
1141 const SfxSlotServer *pServ = pSiblingCache->GetSlotServer(*pDispatcher, pImpl->xProv);
1142 if ( pServ && pServ->GetShellLevel() == nShellLevel )
1143 pSiblingFnc = pServ->GetSlot()->GetStateFnc();
1146 // Does the slot have to be updated at all?
1147 bool bInsert = pSiblingCache && pSiblingCache->IsControllerDirty();
1149 // It is not enough to ask for the same shell!!
1150 bool bSameMethod = pSiblingCache && pFnc == pSiblingFnc;
1152 if ( bInsert && bSameMethod )
1154 SfxFoundCache_Impl *pFoundCache = new SfxFoundCache_Impl(
1155 pSibling->GetWhich(rPool),
1156 pSibling, *pSiblingCache);
1158 rFound.push_back( pFoundCache );
1161 pSibling = pSibling->GetNextSlot();
1164 // Create a Set from the ranges
1165 std::unique_ptr<sal_uInt16[]> pRanges(new sal_uInt16[rFound.size() * 2 + 1]);
1166 int j = 0;
1167 size_t i = 0;
1168 while ( i < rFound.size() )
1170 pRanges[j++] = rFound[i].nWhichId;
1171 // consecutive numbers
1172 for ( ; i < rFound.size()-1; ++i )
1173 if ( rFound[i].nWhichId+1 != rFound[i+1].nWhichId )
1174 break;
1175 pRanges[j++] = rFound[i++].nWhichId;
1177 pRanges[j] = 0; // terminating NULL
1178 std::unique_ptr<SfxItemSet> pSet(new SfxItemSet(rPool, pRanges.get()));
1179 pRanges.reset();
1180 return pSet;
1184 void SfxBindings::UpdateControllers_Impl
1186 const SfxFoundCache_Impl& rFound, // Cache, Slot, Which etc.
1187 const SfxPoolItem* pItem, // item to send to controller
1188 SfxItemState eState // state of item
1191 SfxStateCache& rCache = rFound.rCache;
1192 const SfxSlot* pSlot = rFound.pSlot;
1193 DBG_ASSERT( !pSlot || rCache.GetId() == pSlot->GetSlotId(), "SID mismatch" );
1195 // bound until now, the Controller to update the Slot.
1196 if (!rCache.IsControllerDirty())
1197 return;
1199 if ( SfxItemState::DONTCARE == eState )
1201 // ambiguous
1202 rCache.SetState( SfxItemState::DONTCARE, INVALID_POOL_ITEM );
1204 else if ( SfxItemState::DEFAULT == eState &&
1205 SfxItemPool::IsSlot(rFound.nWhichId) )
1207 // no Status or Default but without Pool
1208 SfxVoidItem aVoid(0);
1209 rCache.SetState( SfxItemState::UNKNOWN, &aVoid );
1211 else if ( SfxItemState::DISABLED == eState )
1212 rCache.SetState(SfxItemState::DISABLED, nullptr);
1213 else
1214 rCache.SetState(SfxItemState::DEFAULT, pItem);
1217 IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void )
1219 NextJob_Impl(pTimer);
1222 bool SfxBindings::NextJob_Impl(Timer const * pTimer)
1224 const unsigned MAX_INPUT_DELAY = 200;
1226 if ( Application::GetLastInputInterval() < MAX_INPUT_DELAY && pTimer )
1228 pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING);
1229 return true;
1232 SfxApplication *pSfxApp = SfxGetpApp();
1234 if( pDispatcher )
1235 pDispatcher->Update_Impl();
1237 // modifying the SfxObjectInterface-stack without SfxBindings => nothing to do
1238 SfxViewFrame* pFrame = pDispatcher ? pDispatcher->GetFrame() : nullptr;
1239 if ( (pFrame && !pFrame->GetObjectShell()->AcceptStateUpdate()) || pSfxApp->IsDowning() || pImpl->pCaches.empty() )
1241 return true;
1243 if ( !pDispatcher || !pDispatcher->IsFlushed() )
1245 return true;
1248 // if possible Update all server / happens in its own time slice
1249 if ( pImpl->bMsgDirty )
1251 UpdateSlotServer_Impl();
1252 return false;
1255 pImpl->bAllDirty = false;
1256 pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING);
1258 // at least 10 loops and further if more jobs are available but no input
1259 bool bPreEmptive = pTimer;
1260 sal_uInt16 nLoops = 10;
1261 pImpl->bInNextJob = true;
1262 const std::size_t nCount = pImpl->pCaches.size();
1263 while ( pImpl->nMsgPos < nCount )
1265 // iterate through the bound functions
1266 bool bJobDone = false;
1267 while ( !bJobDone )
1269 SfxStateCache* pCache = pImpl->pCaches[pImpl->nMsgPos].get();
1270 DBG_ASSERT( pCache, "invalid SfxStateCache-position in job queue" );
1271 bool bWasDirty = pCache->IsControllerDirty();
1272 if ( bWasDirty )
1274 Update_Impl(*pCache);
1275 DBG_ASSERT(nCount == pImpl->pCaches.size(), "Reschedule in StateChanged => buff");
1278 // skip to next function binding
1279 ++pImpl->nMsgPos;
1281 // keep job if it is not completed, but any input is available
1282 bJobDone = pImpl->nMsgPos >= nCount;
1283 if ( bJobDone && pImpl->bFirstRound )
1286 // Update of the preferred shell has been done, now may
1287 // also the others shells be updated
1288 bJobDone = false;
1289 pImpl->bFirstRound = false;
1290 pImpl->nMsgPos = 0;
1293 if ( bWasDirty && !bJobDone && bPreEmptive && (--nLoops == 0) )
1295 pImpl->bInNextJob = false;
1296 return false;
1301 pImpl->nMsgPos = 0;
1303 pImpl->aAutoTimer.Stop();
1305 // Update round is finished
1306 pImpl->bInNextJob = false;
1307 Broadcast(SfxHint(SfxHintId::UpdateDone));
1308 return true;
1312 sal_uInt16 SfxBindings::EnterRegistrations(const char *pFile, int nLine)
1314 SAL_INFO(
1315 "sfx.control",
1316 std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
1317 << " Level = " << nRegLevel << " SfxBindings::EnterRegistrations "
1318 << (pFile
1319 ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
1321 // When bindings are locked, also lock sub bindings.
1322 if ( pImpl->pSubBindings )
1324 pImpl->pSubBindings->ENTERREGISTRATIONS();
1326 // These EnterRegistrations are not "real" for the SubBindings
1327 pImpl->pSubBindings->pImpl->nOwnRegLevel--;
1329 // Synchronize Bindings
1330 pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel + 1;
1333 pImpl->nOwnRegLevel++;
1335 // check if this is the outer most level
1336 if ( ++nRegLevel == 1 )
1338 // stop background-processing
1339 pImpl->aAutoTimer.Stop();
1341 // flush the cache
1342 pImpl->nCachedFunc1 = 0;
1343 pImpl->nCachedFunc2 = 0;
1345 // Mark if the all of the Caches have disappeared.
1346 pImpl->bCtrlReleased = false;
1349 return nRegLevel;
1353 void SfxBindings::LeaveRegistrations( const char *pFile, int nLine )
1355 DBG_ASSERT( nRegLevel, "Leave without Enter" );
1357 // Only when the SubBindings are still locked by the Superbindings,
1358 // remove this lock (i.e. if there are more locks than "real" ones)
1359 if ( pImpl->pSubBindings && pImpl->pSubBindings->nRegLevel > pImpl->pSubBindings->pImpl->nOwnRegLevel )
1361 // Synchronize Bindings
1362 pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel;
1364 // This LeaveRegistrations is not "real" for SubBindings
1365 pImpl->pSubBindings->pImpl->nOwnRegLevel++;
1366 pImpl->pSubBindings->LEAVEREGISTRATIONS();
1369 pImpl->nOwnRegLevel--;
1371 // check if this is the outer most level
1372 if ( --nRegLevel == 0 && SfxGetpApp() && !SfxGetpApp()->IsDowning() )
1374 if ( pImpl->bContextChanged )
1376 pImpl->bContextChanged = false;
1379 SfxViewFrame* pFrame = pDispatcher->GetFrame();
1381 // If possible remove unused Caches, for example prepare PlugInInfo
1382 if ( pImpl->bCtrlReleased )
1384 for ( sal_uInt16 nCache = pImpl->pCaches.size(); nCache > 0; --nCache )
1386 // Get Cache via css::sdbcx::Index
1387 SfxStateCache *pCache = pImpl->pCaches[nCache-1].get();
1389 // No interested Controller present
1390 if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() )
1392 // Remove Cache. Safety: first remove and then delete
1393 pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1);
1398 // restart background-processing
1399 pImpl->nMsgPos = 0;
1400 if ( !pFrame || !pFrame->GetObjectShell() )
1401 return;
1402 if ( !pImpl->pCaches.empty() )
1404 pImpl->aAutoTimer.Stop();
1405 pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
1406 pImpl->aAutoTimer.Start();
1410 SAL_INFO(
1411 "sfx.control",
1412 std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
1413 << " Level = " << nRegLevel << " SfxBindings::LeaveRegistrations "
1414 << (pFile
1415 ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
1419 void SfxBindings::SetDispatcher( SfxDispatcher *pDisp )
1421 SfxDispatcher *pOldDispat = pDispatcher;
1422 if ( pDisp == pDispatcher )
1423 return;
1425 if ( pOldDispat )
1427 SfxBindings* pBind = pOldDispat->GetBindings();
1428 while ( pBind )
1430 if ( pBind->pImpl->pSubBindings == this && pBind->pDispatcher != pDisp )
1431 pBind->SetSubBindings_Impl( nullptr );
1432 pBind = pBind->pImpl->pSubBindings;
1436 pDispatcher = pDisp;
1438 css::uno::Reference < css::frame::XDispatchProvider > xProv;
1439 if ( pDisp )
1440 xProv.set( pDisp->GetFrame()->GetFrame().GetFrameInterface(), UNO_QUERY );
1442 SetDispatchProvider_Impl( xProv );
1443 InvalidateAll( true );
1445 if ( pDispatcher && !pOldDispat )
1447 if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat )
1449 OSL_FAIL( "SubBindings already set before activating!" );
1450 pImpl->pSubBindings->ENTERREGISTRATIONS();
1452 LEAVEREGISTRATIONS();
1454 else if( !pDispatcher )
1456 ENTERREGISTRATIONS();
1457 if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat )
1459 OSL_FAIL( "SubBindings still set even when deactivating!" );
1460 pImpl->pSubBindings->LEAVEREGISTRATIONS();
1464 Broadcast( SfxHint( SfxHintId::DataChanged ) );
1466 if ( !pDisp )
1467 return;
1469 SfxBindings* pBind = pDisp->GetBindings();
1470 while ( pBind && pBind != this )
1472 if ( !pBind->pImpl->pSubBindings )
1474 pBind->SetSubBindings_Impl( this );
1475 break;
1478 pBind = pBind->pImpl->pSubBindings;
1483 void SfxBindings::ClearCache_Impl( sal_uInt16 nSlotId )
1485 SfxStateCache* pCache = GetStateCache(nSlotId);
1486 if (!pCache)
1487 return;
1488 pCache->ClearCache();
1492 void SfxBindings::StartUpdate_Impl( bool bComplete )
1494 if ( pImpl->pSubBindings )
1495 pImpl->pSubBindings->StartUpdate_Impl( bComplete );
1497 if ( !bComplete )
1498 // Update may be interrupted
1499 NextJob_Impl(&pImpl->aAutoTimer);
1500 else
1501 // Update all slots in a row
1502 NextJob_Impl(nullptr);
1506 SfxItemState SfxBindings::QueryState( sal_uInt16 nSlot, std::unique_ptr<SfxPoolItem> &rpState )
1508 css::uno::Reference< css::frame::XDispatch > xDisp;
1509 SfxStateCache *pCache = GetStateCache( nSlot );
1510 if ( pCache )
1511 xDisp = pCache->GetDispatch();
1512 if ( xDisp.is() || !pCache )
1514 const SfxSlot* pSlot = SfxSlotPool::GetSlotPool( pDispatcher->GetFrame() ).GetSlot( nSlot );
1515 if ( !pSlot || !pSlot->pUnoName )
1516 return SfxItemState::DISABLED;
1518 css::util::URL aURL;
1519 OUString aCmd( ".uno:" );
1520 aURL.Protocol = aCmd;
1521 aURL.Path = OUString::createFromAscii(pSlot->GetUnoName());
1522 aCmd += aURL.Path;
1523 aURL.Complete = aCmd;
1524 aURL.Main = aCmd;
1526 if ( !xDisp.is() )
1527 xDisp = pImpl->xProv->queryDispatch( aURL, OUString(), 0 );
1529 if ( xDisp.is() )
1531 css::uno::Reference< css::lang::XUnoTunnel > xTunnel( xDisp, css::uno::UNO_QUERY );
1532 SfxOfficeDispatch* pDisp = nullptr;
1533 if ( xTunnel.is() )
1535 sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
1536 pDisp = reinterpret_cast< SfxOfficeDispatch* >( sal::static_int_cast< sal_IntPtr >( nImplementation ));
1539 if ( !pDisp )
1541 bool bDeleteCache = false;
1542 if ( !pCache )
1544 pCache = new SfxStateCache( nSlot );
1545 pCache->GetSlotServer( *GetDispatcher_Impl(), pImpl->xProv );
1546 bDeleteCache = true;
1549 SfxItemState eState = SfxItemState::SET;
1550 rtl::Reference<BindDispatch_Impl> xBind(new BindDispatch_Impl( xDisp, aURL, pCache, pSlot ));
1551 xDisp->addStatusListener( xBind.get(), aURL );
1552 if ( !xBind->GetStatus().IsEnabled )
1554 eState = SfxItemState::DISABLED;
1556 else
1558 css::uno::Any aAny = xBind->GetStatus().State;
1559 const css::uno::Type& aType = aAny.getValueType();
1561 if ( aType == cppu::UnoType<bool>::get() )
1563 bool bTemp = false;
1564 aAny >>= bTemp ;
1565 rpState.reset(new SfxBoolItem( nSlot, bTemp ));
1567 else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
1569 sal_uInt16 nTemp = 0;
1570 aAny >>= nTemp ;
1571 rpState.reset(new SfxUInt16Item( nSlot, nTemp ));
1573 else if ( aType == cppu::UnoType<sal_uInt32>::get() )
1575 sal_uInt32 nTemp = 0;
1576 aAny >>= nTemp ;
1577 rpState.reset(new SfxUInt32Item( nSlot, nTemp ));
1579 else if ( aType == cppu::UnoType<OUString>::get() )
1581 OUString sTemp ;
1582 aAny >>= sTemp ;
1583 rpState.reset(new SfxStringItem( nSlot, sTemp ));
1585 else
1586 rpState.reset(new SfxVoidItem( nSlot ));
1589 xDisp->removeStatusListener( xBind.get(), aURL );
1590 xBind->Release();
1591 xBind.clear();
1592 if ( bDeleteCache )
1594 delete pCache;
1595 pCache = nullptr;
1597 return eState;
1602 // Then test at the dispatcher to check if the returned items from
1603 // there are always DELETE_ON_IDLE, a copy of it has to be made in
1604 // order to allow for transition of ownership.
1605 const SfxPoolItem *pItem = nullptr;
1606 SfxItemState eState = pDispatcher->QueryState( nSlot, pItem );
1607 if ( eState == SfxItemState::SET )
1609 DBG_ASSERT( pItem, "SfxItemState::SET but no item!" );
1610 if ( pItem )
1611 rpState.reset(pItem->Clone());
1613 else if ( eState == SfxItemState::DEFAULT && pItem )
1615 rpState.reset(pItem->Clone());
1618 return eState;
1621 void SfxBindings::QueryControlState( sal_uInt16 nSlot, boost::property_tree::ptree& rState )
1623 if ( SfxGetpApp()->IsDowning() )
1624 return;
1626 if ( pDispatcher )
1627 pDispatcher->Flush();
1629 if ( pImpl->pSubBindings )
1630 pImpl->pSubBindings->QueryControlState( nSlot, rState );
1632 SfxStateCache* pCache = GetStateCache( nSlot );
1633 if ( !pCache )
1634 return;
1636 if ( pImpl->bMsgDirty )
1638 UpdateSlotServer_Impl();
1639 pCache = GetStateCache( nSlot );
1642 if (pCache && pCache->GetItemLink() )
1644 pCache->GetState(rState);
1648 void SfxBindings::SetSubBindings_Impl( SfxBindings *pSub )
1650 if ( pImpl->pSubBindings )
1652 pImpl->pSubBindings->SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > () );
1655 pImpl->pSubBindings = pSub;
1657 if ( pSub )
1659 pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
1663 SfxBindings* SfxBindings::GetSubBindings_Impl() const
1665 return pImpl->pSubBindings;
1668 void SfxBindings::SetWorkWindow_Impl( std::unique_ptr<SfxWorkWindow> xWork )
1670 pImpl->mxWorkWin = std::move(xWork);
1673 SfxWorkWindow* SfxBindings::GetWorkWindow_Impl() const
1675 return pImpl->mxWorkWin.get();
1678 bool SfxBindings::IsInUpdate() const
1680 bool bInUpdate = pImpl->bInUpdate;
1681 if ( !bInUpdate && pImpl->pSubBindings )
1682 bInUpdate = pImpl->pSubBindings->IsInUpdate();
1683 return bInUpdate;
1686 void SfxBindings::SetVisibleState( sal_uInt16 nId, bool bShow )
1688 SfxStateCache *pCache = GetStateCache( nId );
1689 if ( pCache )
1690 pCache->SetVisibleState( bShow );
1693 void SfxBindings::SetActiveFrame( const css::uno::Reference< css::frame::XFrame > & rFrame )
1695 if ( rFrame.is() || !pDispatcher )
1696 SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > ( rFrame, css::uno::UNO_QUERY ) );
1697 else
1698 SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > (
1699 pDispatcher->GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY ) );
1702 css::uno::Reference< css::frame::XFrame > SfxBindings::GetActiveFrame() const
1704 const css::uno::Reference< css::frame::XFrame > xFrame( pImpl->xProv, css::uno::UNO_QUERY );
1705 if ( xFrame.is() || !pDispatcher )
1706 return xFrame;
1707 else
1708 return pDispatcher->GetFrame()->GetFrame().GetFrameInterface();
1711 void SfxBindings::SetDispatchProvider_Impl( const css::uno::Reference< css::frame::XDispatchProvider > & rProv )
1713 bool bInvalidate = ( rProv != pImpl->xProv );
1714 if ( bInvalidate )
1716 pImpl->xProv = rProv;
1717 InvalidateAll( true );
1720 if ( pImpl->pSubBindings )
1721 pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
1724 const css::uno::Reference< css::frame::XDispatchRecorder >& SfxBindings::GetRecorder() const
1726 return pImpl->xRecorder;
1729 void SfxBindings::SetRecorder_Impl( css::uno::Reference< css::frame::XDispatchRecorder > const & rRecorder )
1731 pImpl->xRecorder = rRecorder;
1734 void SfxBindings::ContextChanged_Impl()
1736 if ( !pImpl->bInUpdate && ( !pImpl->bContextChanged || !pImpl->bAllMsgDirty ) )
1738 InvalidateAll( true );
1742 uno::Reference < frame::XDispatch > SfxBindings::GetDispatch( const SfxSlot* pSlot, const util::URL& aURL, bool bMasterCommand )
1744 uno::Reference < frame::XDispatch > xRet;
1745 SfxStateCache* pCache = GetStateCache( pSlot->nSlotId );
1746 if ( pCache && !bMasterCommand )
1747 xRet = pCache->GetInternalDispatch();
1748 if ( !xRet.is() )
1750 // dispatches for slaves are unbound, they don't have a state
1751 SfxOfficeDispatch* pDispatch = bMasterCommand ?
1752 new SfxOfficeDispatch( pDispatcher, pSlot, aURL ) :
1753 new SfxOfficeDispatch( *this, pDispatcher, pSlot, aURL );
1755 pDispatch->SetMasterUnoCommand( bMasterCommand );
1756 xRet.set( pDispatch );
1757 if ( !pCache )
1758 pCache = GetStateCache( pSlot->nSlotId );
1760 DBG_ASSERT( pCache, "No cache for OfficeDispatch!" );
1761 if ( pCache && !bMasterCommand )
1762 pCache->SetInternalDispatch( xRet );
1765 return xRet;
1768 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */