xref: /core/sfx2/source/control/bindings.cxx (revision 50fa7d25)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <iomanip>
23 
24 #include <comphelper/servicehelper.hxx>
25 #include <osl/diagnose.h>
26 #include <sal/log.hxx>
27 #include <svl/itempool.hxx>
28 #include <svl/itemiter.hxx>
29 #include <svl/eitem.hxx>
30 #include <svl/intitem.hxx>
31 #include <svl/stritem.hxx>
32 #include <svl/voiditem.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/timer.hxx>
35 #include <com/sun/star/frame/XDispatch.hpp>
36 #include <com/sun/star/frame/XDispatchProvider.hpp>
37 #include <com/sun/star/frame/DispatchResultState.hpp>
38 
39 //Includes below due to nInReschedule
40 #include <sfx2/bindings.hxx>
41 #include <sfx2/msg.hxx>
42 #include <statcach.hxx>
43 #include <sfx2/ctrlitem.hxx>
44 #include <sfx2/app.hxx>
45 #include <sfx2/dispatch.hxx>
46 #include <sfx2/module.hxx>
47 #include <sfx2/request.hxx>
48 #include <workwin.hxx>
49 #include <unoctitm.hxx>
50 #include <sfx2/viewfrm.hxx>
51 #include <sfx2/objsh.hxx>
52 #include <sfx2/msgpool.hxx>
53 #include <sfx2/lokhelper.hxx>
54 #include <comphelper/lok.hxx>
55 
56 #include <cstddef>
57 #include <memory>
58 #include <unordered_map>
59 #include <utility>
60 #include <vector>
61 
62 using namespace ::com::sun::star;
63 using namespace ::com::sun::star::uno;
64 using namespace ::com::sun::star::util;
65 
66 #define TIMEOUT_FIRST       300
67 #define TIMEOUT_UPDATING     20
68 
69 struct SfxFoundCache_Impl
70 {
71     sal_uInt16      nWhichId;  // If available: Which-Id, else: nSlotId
72     const SfxSlot*  pSlot;     // Pointer to <Master-Slot>
73     SfxStateCache&  rCache;    // Pointer to StatusCache
74 
SfxFoundCache_ImplSfxFoundCache_Impl75     SfxFoundCache_Impl(sal_uInt16 nW, const SfxSlot *pS, SfxStateCache& rC)
76         : nWhichId(nW)
77         , pSlot(pS)
78         , rCache(rC)
79     {}
80 };
81 
82 class SfxFoundCacheArr_Impl
83 {
84     std::vector<SfxFoundCache_Impl> maData;
85 
86 public:
87 
operator [](size_t i)88     SfxFoundCache_Impl& operator[] ( size_t i )
89     {
90         return maData[i];
91     }
92 
size() const93     size_t size() const
94     {
95         return maData.size();
96     }
97 
push_back(SfxFoundCache_Impl p)98     void push_back( SfxFoundCache_Impl p )
99     {
100         maData.push_back(p);
101     }
102 };
103 
104 class SfxBindings_Impl
105 {
106 public:
107     css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
108     css::uno::Reference< css::frame::XDispatchProvider >  xProv;
109     std::unique_ptr<SfxWorkWindow> mxWorkWin;
110     SfxBindings*            pSubBindings;
111     std::vector<std::unique_ptr<SfxStateCache>> pCaches; // One cache for each binding
112     std::size_t             nCachedFunc1;   // index for the last one called
113     std::size_t             nCachedFunc2;   // index for the second last called
114     std::size_t             nMsgPos;        // Message-Position relative the one to be updated
115     bool                    bContextChanged;
116     bool                    bMsgDirty;      // Has a MessageServer been invalidated?
117     bool                    bAllMsgDirty;   //  Has a MessageServer been invalidated?
118     bool                    bAllDirty;      // After InvalidateAll
119     bool                    bCtrlReleased;  // while EnterRegistrations
120     AutoTimer               aAutoTimer { "sfx::SfxBindings aAutoTimer" }; // for volatile Slots
121     bool                    bInUpdate;      // for Assertions
122     bool                    bInNextJob;     // for Assertions
123     bool                    bFirstRound;    // First round in Update
124     sal_uInt16              nOwnRegLevel;   // Counts the real Locks, except those of the Super Bindings
125     std::unordered_map< sal_uInt16, bool >
126                             m_aInvalidateSlots; // store slots which are invalidated while in update
127 };
128 
SfxBindings()129 SfxBindings::SfxBindings()
130 :   pImpl(new SfxBindings_Impl),
131     pDispatcher(nullptr),
132     nRegLevel(1)    // first becomes 0, when the Dispatcher is set
133 
134 {
135     pImpl->nMsgPos = 0;
136     pImpl->bAllMsgDirty = true;
137     pImpl->bContextChanged = false;
138     pImpl->bMsgDirty = true;
139     pImpl->bAllDirty = true;
140     pImpl->nCachedFunc1 = 0;
141     pImpl->nCachedFunc2 = 0;
142     pImpl->bCtrlReleased = false;
143     pImpl->bFirstRound = false;
144     pImpl->bInNextJob = false;
145     pImpl->bInUpdate = false;
146     pImpl->pSubBindings = nullptr;
147     pImpl->nOwnRegLevel = nRegLevel;
148 
149     // all caches are valid (no pending invalidate-job)
150     // create the list of caches
151     pImpl->aAutoTimer.SetPriority(TaskPriority::HIGH_IDLE);
152     pImpl->aAutoTimer.SetInvokeHandler( LINK(this, SfxBindings, NextJob) );
153 }
154 
155 
~SfxBindings()156 SfxBindings::~SfxBindings()
157 
158 /*  [Description]
159 
160     Destructor of the SfxBindings class. The one, for each <SfxApplication>
161     existing Instance is automatically destroyed by the <SfxApplication>
162     after the execution of <SfxApplication::Exit()>.
163 
164     The still existing <SfxControllerItem> instances, which are registered
165     by the SfxBindings instance, are automatically destroyed in the Destructor.
166     These are usually the Floating-Toolboxen, Value-Sets
167     etc. Arrays of SfxControllerItems may at this time no longer exist.
168 */
169 
170 {
171     // The SubBindings should not be locked!
172     pImpl->pSubBindings = nullptr;
173 
174     ENTERREGISTRATIONS();
175 
176     pImpl->aAutoTimer.Stop();
177     DeleteControllers_Impl();
178 
179     // Delete Caches
180     pImpl->pCaches.clear();
181 
182     pImpl->mxWorkWin.reset();
183 }
184 
185 
DeleteControllers_Impl()186 void SfxBindings::DeleteControllers_Impl()
187 {
188     // in the first round delete Controllers
189     std::size_t nCount = pImpl->pCaches.size();
190     std::size_t nCache;
191     for ( nCache = 0; nCache < nCount; ++nCache )
192     {
193         // Remember were you are
194         SfxStateCache *pCache = pImpl->pCaches[nCache].get();
195         sal_uInt16 nSlotId = pCache->GetId();
196 
197         // Re-align, because the cache may have been reduced
198         std::size_t nNewCount = pImpl->pCaches.size();
199         if ( nNewCount < nCount )
200         {
201             nCache = GetSlotPos(nSlotId);
202             if ( nCache >= nNewCount ||
203                  nSlotId != pImpl->pCaches[nCache]->GetId() )
204                 --nCache;
205             nCount = nNewCount;
206         }
207     }
208 
209     // Delete all Caches
210     for ( nCache = pImpl->pCaches.size(); nCache > 0; --nCache )
211     {
212         // Get Cache via css::sdbcx::Index
213         SfxStateCache *pCache = pImpl->pCaches[ nCache-1 ].get();
214 
215         // unbind all controllers in the cache
216         SfxControllerItem *pNext;
217         for ( SfxControllerItem *pCtrl = pCache->GetItemLink();
218               pCtrl; pCtrl = pNext )
219         {
220             pNext = pCtrl->GetItemLink();
221             pCtrl->UnBind();
222         }
223 
224         if ( pCache->GetInternalController() )
225             pCache->GetInternalController()->UnBind();
226 
227         // Delete Cache
228         pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1);
229     }
230 }
231 
232 
HidePopups(bool bHide)233 void SfxBindings::HidePopups( bool bHide )
234 {
235     // Hide SfxChildWindows
236     DBG_ASSERT( pDispatcher, "HidePopups not allowed without dispatcher" );
237     if ( pImpl->mxWorkWin )
238         pImpl->mxWorkWin->HidePopups_Impl( bHide );
239 }
240 
Update_Impl(SfxStateCache & rCache)241 void SfxBindings::Update_Impl(SfxStateCache& rCache /*The up to date SfxStatusCache*/)
242 {
243     if (rCache.GetDispatch().is() && rCache.GetItemLink())
244     {
245         rCache.SetCachedState(true);
246         if (!rCache.GetInternalController())
247             return;
248     }
249 
250     if ( !pDispatcher )
251         return;
252 
253     // gather together all with the same status method which are dirty
254     SfxDispatcher &rDispat = *pDispatcher;
255     const SfxSlot *pRealSlot = nullptr;
256     const SfxSlotServer* pMsgServer = nullptr;
257     SfxFoundCacheArr_Impl aFound;
258     std::optional<SfxItemSet> pSet = CreateSet_Impl(rCache, pRealSlot, &pMsgServer, aFound);
259     bool bUpdated = false;
260     if ( pSet )
261     {
262         // Query Status
263         if ( rDispat.FillState_( *pMsgServer, *pSet, pRealSlot ) )
264         {
265             // Post Status
266             for ( size_t nPos = 0; nPos < aFound.size(); ++nPos )
267             {
268                 const SfxFoundCache_Impl& rFound = aFound[nPos];
269                 sal_uInt16 nWhich = rFound.nWhichId;
270                 const SfxPoolItem *pItem = nullptr;
271                 SfxItemState eState = pSet->GetItemState(nWhich, true, &pItem);
272                 if ( eState == SfxItemState::DEFAULT && SfxItemPool::IsWhich(nWhich) )
273                     pItem = &pSet->Get(nWhich);
274                 UpdateControllers_Impl( rFound, pItem, eState );
275             }
276             bUpdated = true;
277         }
278 
279         pSet.reset();
280     }
281 
282     if (!bUpdated)
283     {
284         SfxFoundCache_Impl aFoundCache(0, pRealSlot, rCache);
285         UpdateControllers_Impl( aFoundCache, nullptr, SfxItemState::DISABLED);
286     }
287 }
288 
InvalidateSlotsInMap_Impl()289 void SfxBindings::InvalidateSlotsInMap_Impl()
290 {
291     for (auto const& slot : pImpl->m_aInvalidateSlots)
292         Invalidate( slot.first );
293 
294     pImpl->m_aInvalidateSlots.clear();
295 }
296 
297 
AddSlotToInvalidateSlotsMap_Impl(sal_uInt16 nId)298 void SfxBindings::AddSlotToInvalidateSlotsMap_Impl( sal_uInt16 nId )
299 {
300     pImpl->m_aInvalidateSlots[nId] = true;
301 }
302 
303 
Update(sal_uInt16 nId)304 void SfxBindings::Update
305 (
306     sal_uInt16      nId     // the bound and up-to-date Slot-Id
307 )
308 {
309     if ( pDispatcher )
310         pDispatcher->Flush();
311 
312     if ( pImpl->pSubBindings )
313         pImpl->pSubBindings->Update( nId );
314 
315     SfxStateCache* pCache = GetStateCache( nId );
316     if ( !pCache )
317         return;
318 
319     pImpl->bInUpdate = true;
320     if ( pImpl->bMsgDirty )
321     {
322         UpdateSlotServer_Impl();
323         pCache = GetStateCache( nId );
324     }
325 
326     if (pCache)
327     {
328         bool bInternalUpdate = true;
329         if( pCache->GetDispatch().is() && pCache->GetItemLink() )
330         {
331             pCache->SetCachedState(true);
332             bInternalUpdate = ( pCache->GetInternalController() != nullptr );
333         }
334 
335         if ( bInternalUpdate )
336         {
337             // Query Status
338             const SfxSlotServer* pMsgServer = pDispatcher ? pCache->GetSlotServer(*pDispatcher, pImpl->xProv) : nullptr;
339             if ( !pCache->IsControllerDirty() )
340             {
341                 pImpl->bInUpdate = false;
342                 InvalidateSlotsInMap_Impl();
343                 return;
344             }
345             if (!pMsgServer)
346             {
347                 pCache->SetState(SfxItemState::DISABLED, nullptr);
348                 pImpl->bInUpdate = false;
349                 InvalidateSlotsInMap_Impl();
350                 return;
351             }
352 
353             Update_Impl(*pCache);
354         }
355 
356         pImpl->bAllDirty = false;
357     }
358 
359     pImpl->bInUpdate = false;
360     InvalidateSlotsInMap_Impl();
361 }
362 
363 
Update()364 void SfxBindings::Update()
365 {
366     if ( pImpl->pSubBindings )
367         pImpl->pSubBindings->Update();
368 
369     if ( !pDispatcher )
370         return;
371 
372     if ( nRegLevel )
373         return;
374 
375     pImpl->bInUpdate = true;
376     pDispatcher->Flush();
377     pDispatcher->Update_Impl();
378     while ( !NextJob_Impl(nullptr) )
379         ; // loop
380     pImpl->bInUpdate = false;
381     InvalidateSlotsInMap_Impl();
382 }
383 
384 
SetState(const SfxItemSet & rSet)385 void SfxBindings::SetState
386 (
387     const SfxItemSet&   rSet    // status values to be set
388 )
389 {
390     // when locked then only invalidate
391     if ( nRegLevel )
392     {
393         SfxItemIter aIter(rSet);
394         for ( const SfxPoolItem *pItem = aIter.GetCurItem();
395               pItem;
396               pItem = aIter.NextItem() )
397             Invalidate( pItem->Which() );
398     }
399     else
400     {
401         // Status may be accepted only if all slot-pointers are set
402         if ( pImpl->bMsgDirty )
403             UpdateSlotServer_Impl();
404 
405         // Iterate over the itemset, update if the slot bound
406         //! Bug: Use WhichIter and possibly send VoidItems up
407         SfxItemIter aIter(rSet);
408         for ( const SfxPoolItem *pItem = aIter.GetCurItem();
409               pItem;
410               pItem = aIter.NextItem() )
411         {
412             SfxStateCache* pCache =
413                     GetStateCache( rSet.GetPool()->GetSlotId(pItem->Which()) );
414             if ( pCache )
415             {
416                 // Update status
417                 if ( !pCache->IsControllerDirty() )
418                     pCache->Invalidate(false);
419                 pCache->SetState( SfxItemState::DEFAULT, pItem );
420 
421                 //! Not implemented: Updates from EnumSlots via master slots
422             }
423         }
424     }
425 }
426 
427 
SetState(const SfxPoolItem & rItem)428 void SfxBindings::SetState
429 (
430     const SfxPoolItem&  rItem   // Status value to be set
431 )
432 {
433     if ( nRegLevel )
434     {
435         Invalidate( rItem.Which() );
436     }
437     else
438     {
439         // Status may be accepted only if all slot-pointers are set
440         if ( pImpl->bMsgDirty )
441             UpdateSlotServer_Impl();
442 
443         //update if the slot bound
444         DBG_ASSERT( SfxItemPool::IsSlot( rItem.Which() ),
445                     "cannot set items with which-id" );
446         SfxStateCache* pCache = GetStateCache( rItem.Which() );
447         if ( pCache )
448         {
449             // Update Status
450             if ( !pCache->IsControllerDirty() )
451                 pCache->Invalidate(false);
452             pCache->SetState( SfxItemState::DEFAULT, &rItem );
453 
454             //! Not implemented: Updates from EnumSlots via master slots
455         }
456     }
457 }
458 
459 
GetAnyStateCache_Impl(sal_uInt16 nId)460 SfxStateCache* SfxBindings::GetAnyStateCache_Impl( sal_uInt16 nId )
461 {
462     SfxStateCache* pCache = GetStateCache( nId );
463     if ( !pCache && pImpl->pSubBindings )
464         return pImpl->pSubBindings->GetAnyStateCache_Impl( nId );
465     return pCache;
466 }
467 
GetStateCache(sal_uInt16 nId)468 SfxStateCache* SfxBindings::GetStateCache
469 (
470     sal_uInt16   nId   /*  Slot-Id, which SfxStatusCache is to be found */
471 )
472 {
473     return GetStateCache(nId, nullptr);
474 }
475 
GetStateCache(sal_uInt16 nId,std::size_t * pPos)476 SfxStateCache* SfxBindings::GetStateCache
477 (
478     sal_uInt16   nId,   /*  Slot-Id, which SfxStatusCache is to be found */
479     std::size_t * pPos  /*  NULL for instance the position from which the
480                            bindings are to be searched binary. Returns the
481                            position back for where the nId was found,
482                            or where it was inserted. */
483 )
484 {
485     // is the specified function bound?
486     const std::size_t nStart = ( pPos ? *pPos : 0 );
487     const std::size_t nPos = GetSlotPos( nId, nStart );
488 
489     if ( nPos < pImpl->pCaches.size() &&
490          pImpl->pCaches[nPos]->GetId() == nId )
491     {
492         if ( pPos )
493             *pPos = nPos;
494         return pImpl->pCaches[nPos].get();
495     }
496     return nullptr;
497 }
498 
499 
InvalidateAll(bool bWithMsg)500 void SfxBindings::InvalidateAll
501 (
502     bool  bWithMsg  /* true   Mark Slot Server as invalid
503                        false  Slot Server remains valid */
504 )
505 {
506     DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
507 
508     if ( pImpl->pSubBindings )
509         pImpl->pSubBindings->InvalidateAll( bWithMsg );
510 
511     // everything is already set dirty or downing => nothing to do
512     if ( !pDispatcher ||
513          ( pImpl->bAllDirty && ( !bWithMsg || pImpl->bAllMsgDirty ) ) ||
514          SfxGetpApp()->IsDowning() )
515     {
516         return;
517     }
518 
519     pImpl->bAllMsgDirty = pImpl->bAllMsgDirty || bWithMsg;
520     pImpl->bMsgDirty = pImpl->bMsgDirty || pImpl->bAllMsgDirty || bWithMsg;
521     pImpl->bAllDirty = true;
522 
523     for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
524         pCache->Invalidate(bWithMsg);
525 
526     pImpl->nMsgPos = 0;
527     if ( !nRegLevel )
528     {
529         pImpl->aAutoTimer.Stop();
530         pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
531         pImpl->aAutoTimer.Start();
532     }
533 }
534 
535 
Invalidate(const sal_uInt16 * pIds)536 void SfxBindings::Invalidate
537 (
538     const sal_uInt16* pIds /* numerically sorted NULL-terminated array of
539                               slot IDs (individual, not as a couple!) */
540 )
541 {
542     if ( pImpl->bInUpdate )
543     {
544         sal_Int32 i = 0;
545         while ( pIds[i] != 0 )
546             AddSlotToInvalidateSlotsMap_Impl( pIds[i++] );
547 
548         if ( pImpl->pSubBindings )
549             pImpl->pSubBindings->Invalidate( pIds );
550         return;
551     }
552 
553     if ( pImpl->pSubBindings )
554         pImpl->pSubBindings->Invalidate( pIds );
555 
556     // everything is already set dirty or downing => nothing to do
557     if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
558         return;
559 
560     // Search binary in always smaller areas
561     for ( std::size_t n = GetSlotPos(*pIds);
562           *pIds && n < pImpl->pCaches.size();
563           n = GetSlotPos(*pIds, n) )
564     {
565         // If SID is ever bound, then invalidate the cache
566         SfxStateCache *pCache = pImpl->pCaches[n].get();
567         if ( pCache->GetId() == *pIds )
568             pCache->Invalidate(false);
569 
570         // Next SID
571         if ( !*++pIds )
572             break;
573         assert( *pIds > *(pIds-1) );
574     }
575 
576     // if not enticed to start update timer
577     pImpl->nMsgPos = 0;
578     if ( !nRegLevel )
579     {
580         pImpl->aAutoTimer.Stop();
581         pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
582         pImpl->aAutoTimer.Start();
583     }
584 }
585 
586 
InvalidateShell(const SfxShell & rSh,bool bDeep)587 void SfxBindings::InvalidateShell
588 (
589     const SfxShell&  rSh,  /* <SfxShell> whose Slot-Ids should be
590                               invalidated */
591     bool             bDeep /* true
592                               also the SfxShell's inherited slot IDs are invalidated
593 
594                               false
595                               the inherited and not overridden Slot-Ids are
596                               invalidated */
597                              // for now always bDeep
598 )
599 {
600     DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
601 
602     if ( pImpl->pSubBindings )
603         pImpl->pSubBindings->InvalidateShell( rSh, bDeep );
604 
605     if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
606         return;
607 
608     // flush now already, it is done in GetShellLevel (rsh) anyway,
609     // important so that is set correctly: pImpl-> ball(Msg)Dirty
610     pDispatcher->Flush();
611 
612     if ((pImpl->bAllDirty && pImpl->bAllMsgDirty) || SfxGetpApp()->IsDowning())
613     {
614         // if the next one is anyway, then all the servers are collected
615         return;
616     }
617 
618     // Find Level
619     sal_uInt16 nLevel = pDispatcher->GetShellLevel(rSh);
620     if ( nLevel == USHRT_MAX )
621         return;
622 
623     for (std::unique_ptr<SfxStateCache>& pCache : pImpl->pCaches)
624     {
625         const SfxSlotServer *pMsgServer =
626             pCache->GetSlotServer(*pDispatcher, pImpl->xProv);
627         if ( pMsgServer && pMsgServer->GetShellLevel() == nLevel )
628             pCache->Invalidate(false);
629     }
630     pImpl->nMsgPos = 0;
631     if ( !nRegLevel )
632     {
633         pImpl->aAutoTimer.Stop();
634         pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
635         pImpl->aAutoTimer.Start();
636         pImpl->bFirstRound = true;
637     }
638 }
639 
640 
Invalidate(sal_uInt16 nId)641 void SfxBindings::Invalidate
642 (
643     sal_uInt16 nId              // Status value to be set
644 )
645 {
646     if ( pImpl->bInUpdate )
647     {
648         AddSlotToInvalidateSlotsMap_Impl( nId );
649         if ( pImpl->pSubBindings )
650             pImpl->pSubBindings->Invalidate( nId );
651         return;
652     }
653 
654     if ( pImpl->pSubBindings )
655         pImpl->pSubBindings->Invalidate( nId );
656 
657     if ( !pDispatcher || pImpl->bAllDirty || SfxGetpApp()->IsDowning() )
658         return;
659 
660     SfxStateCache* pCache = GetStateCache(nId);
661     if ( pCache )
662     {
663         pCache->Invalidate(false);
664         pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos);
665         if ( !nRegLevel )
666         {
667             pImpl->aAutoTimer.Stop();
668             pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
669             pImpl->aAutoTimer.Start();
670         }
671     }
672 }
673 
674 
Invalidate(sal_uInt16 nId,bool bWithItem,bool bWithMsg)675 void SfxBindings::Invalidate
676 (
677     sal_uInt16  nId,                // Status value to be set
678     bool        bWithItem,          // Clear StateCache?
679     bool        bWithMsg            // Get new SlotServer?
680 )
681 {
682     DBG_ASSERT( !pImpl->bInUpdate, "SfxBindings::Invalidate while in update" );
683 
684     if ( pImpl->pSubBindings )
685         pImpl->pSubBindings->Invalidate( nId, bWithItem, bWithMsg );
686 
687     if ( SfxGetpApp()->IsDowning() )
688         return;
689 
690     SfxStateCache* pCache = GetStateCache(nId);
691     if ( !pCache )
692         return;
693 
694     if ( bWithItem )
695         pCache->ClearCache();
696     pCache->Invalidate(bWithMsg);
697 
698     if ( !pDispatcher || pImpl->bAllDirty )
699         return;
700 
701     pImpl->nMsgPos = std::min(GetSlotPos(nId), pImpl->nMsgPos);
702     if ( !nRegLevel )
703     {
704         pImpl->aAutoTimer.Stop();
705         pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
706         pImpl->aAutoTimer.Start();
707     }
708 }
709 
710 
GetSlotPos(sal_uInt16 nId,std::size_t nStartSearchAt)711 std::size_t SfxBindings::GetSlotPos( sal_uInt16 nId, std::size_t nStartSearchAt )
712 {
713     // answer immediately if a function-seek comes repeated
714     if ( pImpl->nCachedFunc1 < pImpl->pCaches.size() &&
715          pImpl->pCaches[pImpl->nCachedFunc1]->GetId() == nId )
716     {
717         return pImpl->nCachedFunc1;
718     }
719     if ( pImpl->nCachedFunc2 < pImpl->pCaches.size() &&
720          pImpl->pCaches[pImpl->nCachedFunc2]->GetId() == nId )
721     {
722         // swap the caches
723         std::swap(pImpl->nCachedFunc1, pImpl->nCachedFunc2);
724         return pImpl->nCachedFunc1;
725     }
726 
727     // binary search, if not found, seek to target-position
728     if ( pImpl->pCaches.size() <= nStartSearchAt )
729     {
730         return 0;
731     }
732     if ( pImpl->pCaches.size() == (nStartSearchAt+1) )
733     {
734         return pImpl->pCaches[nStartSearchAt]->GetId() >= nId ? 0 : 1;
735     }
736     std::size_t nLow = nStartSearchAt;
737     std::size_t nMid = 0;
738     std::size_t nHigh = 0;
739     bool bFound = false;
740     nHigh = pImpl->pCaches.size() - 1;
741     while ( !bFound && nLow <= nHigh )
742     {
743         nMid = (nLow + nHigh) >> 1;
744         DBG_ASSERT( nMid < pImpl->pCaches.size(), "bsearch is buggy" );
745         int nDiff = static_cast<int>(nId) - static_cast<int>( (pImpl->pCaches[nMid])->GetId() );
746         if ( nDiff < 0)
747         {   if ( nMid == 0 )
748                 break;
749             nHigh = nMid - 1;
750         }
751         else if ( nDiff > 0 )
752         {   nLow = nMid + 1;
753             if ( nLow == 0 )
754                 break;
755         }
756         else
757             bFound = true;
758     }
759     std::size_t nPos = bFound ? nMid : nLow;
760     DBG_ASSERT( nPos <= pImpl->pCaches.size(), "" );
761     DBG_ASSERT( nPos == pImpl->pCaches.size() ||
762                 nId <= pImpl->pCaches[nPos]->GetId(), "" );
763     DBG_ASSERT( nPos == nStartSearchAt ||
764                 nId > pImpl->pCaches[nPos-1]->GetId(), "" );
765     DBG_ASSERT( ( (nPos+1) >= pImpl->pCaches.size() ) ||
766                 nId < pImpl->pCaches[nPos+1]->GetId(), "" );
767     pImpl->nCachedFunc2 = pImpl->nCachedFunc1;
768     pImpl->nCachedFunc1 = nPos;
769     return nPos;
770 }
771 
RegisterInternal_Impl(SfxControllerItem & rItem)772 void SfxBindings::RegisterInternal_Impl( SfxControllerItem& rItem )
773 {
774     Register_Impl( rItem, true );
775 
776 }
777 
Register(SfxControllerItem & rItem)778 void SfxBindings::Register( SfxControllerItem& rItem )
779 {
780     Register_Impl( rItem, false );
781 }
782 
Register_Impl(SfxControllerItem & rItem,bool bInternal)783 void SfxBindings::Register_Impl( SfxControllerItem& rItem, bool bInternal )
784 {
785 //    DBG_ASSERT( nRegLevel > 0, "registration without EnterRegistrations" );
786     DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Register while status-updating" );
787 
788     // insert new cache if it does not already exist
789     sal_uInt16 nId = rItem.GetId();
790     std::size_t nPos = GetSlotPos(nId);
791     if ( nPos >= pImpl->pCaches.size() ||
792          pImpl->pCaches[nPos]->GetId() != nId )
793     {
794         pImpl->pCaches.insert( pImpl->pCaches.begin() + nPos, std::make_unique<SfxStateCache>(nId) );
795         DBG_ASSERT( nPos == 0 ||
796                     pImpl->pCaches[nPos]->GetId() >
797                         pImpl->pCaches[nPos-1]->GetId(), "" );
798         DBG_ASSERT( (nPos == pImpl->pCaches.size()-1) ||
799                     pImpl->pCaches[nPos]->GetId() <
800                         pImpl->pCaches[nPos+1]->GetId(), "" );
801         pImpl->bMsgDirty = true;
802     }
803 
804     // enqueue the new binding
805     if ( bInternal )
806     {
807         pImpl->pCaches[nPos]->SetInternalController( &rItem );
808     }
809     else
810     {
811         SfxControllerItem *pOldItem = pImpl->pCaches[nPos]->ChangeItemLink(&rItem);
812         rItem.ChangeItemLink(pOldItem);
813     }
814 }
815 
816 
Release(SfxControllerItem & rItem)817 void SfxBindings::Release( SfxControllerItem& rItem )
818 {
819     DBG_ASSERT( !pImpl->bInNextJob, "SfxBindings::Release while status-updating" );
820     ENTERREGISTRATIONS();
821 
822     // find the bound function
823     sal_uInt16 nId = rItem.GetId();
824     std::size_t nPos = GetSlotPos(nId);
825     SfxStateCache* pCache = (nPos < pImpl->pCaches.size()) ? pImpl->pCaches[nPos].get() : nullptr;
826     if ( pCache && pCache->GetId() == nId )
827     {
828         if ( pCache->GetInternalController() == &rItem )
829         {
830             pCache->ReleaseInternalController();
831         }
832         else
833         {
834             // is this the first binding in the list?
835             SfxControllerItem* pItem = pCache->GetItemLink();
836             if ( pItem == &rItem )
837                 pCache->ChangeItemLink( rItem.GetItemLink() );
838             else
839             {
840                 // search the binding in the list
841                 while ( pItem && pItem->GetItemLink() != &rItem )
842                     pItem = pItem->GetItemLink();
843 
844                 // unlink it if it was found
845                 if ( pItem )
846                     pItem->ChangeItemLink( rItem.GetItemLink() );
847             }
848         }
849 
850         // was this the last controller?
851         if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() )
852         {
853             pImpl->bCtrlReleased = true;
854         }
855     }
856 
857     LEAVEREGISTRATIONS();
858 }
859 
860 
ExecuteSynchron(sal_uInt16 nId,const SfxPoolItem ** ppItems)861 SfxPoolItemHolder SfxBindings::ExecuteSynchron( sal_uInt16 nId, const SfxPoolItem** ppItems )
862 {
863     if( !nId || !pDispatcher )
864         return SfxPoolItemHolder();
865 
866     return Execute_Impl( nId, ppItems, 0, SfxCallMode::SYNCHRON, nullptr );
867 }
868 
Execute(sal_uInt16 nId,const SfxPoolItem ** ppItems,SfxCallMode nCallMode)869 bool SfxBindings::Execute( sal_uInt16 nId, const SfxPoolItem** ppItems, SfxCallMode nCallMode )
870 {
871     if( !nId || !pDispatcher )
872         return false;
873 
874     const SfxPoolItemHolder aRet(Execute_Impl(nId, ppItems, 0, nCallMode, nullptr));
875     return aRet.is();
876 }
877 
Execute_Impl(sal_uInt16 nId,const SfxPoolItem ** ppItems,sal_uInt16 nModi,SfxCallMode nCallMode,const SfxPoolItem ** ppInternalArgs)878 SfxPoolItemHolder SfxBindings::Execute_Impl( sal_uInt16 nId, const SfxPoolItem** ppItems, sal_uInt16 nModi, SfxCallMode nCallMode,
879                         const SfxPoolItem **ppInternalArgs )
880 {
881     SfxStateCache *pCache = GetStateCache( nId );
882     if ( !pCache )
883     {
884         SfxBindings *pBind = pImpl->pSubBindings;
885         while ( pBind )
886         {
887             if ( pBind->GetStateCache( nId ) )
888                 return pBind->Execute_Impl( nId, ppItems, nModi, nCallMode, ppInternalArgs );
889             pBind = pBind->pImpl->pSubBindings;
890         }
891     }
892 
893     SfxDispatcher &rDispatcher = *pDispatcher;
894     rDispatcher.Flush();
895 
896     // get SlotServer (Slot+ShellLevel) and Shell from cache
897     std::unique_ptr<SfxStateCache> xCache;
898     if ( !pCache )
899     {
900         // Execution of non cached slots (Accelerators don't use Controllers)
901         // slot is uncached, use SlotCache to handle external dispatch providers
902         xCache.reset(new SfxStateCache(nId));
903         pCache = xCache.get();
904     }
905 
906     pCache->GetSlotServer( rDispatcher, pImpl->xProv ); // make pCache->GetDispatch() up to date
907     if ( pCache->GetDispatch().is() )
908     {
909         DBG_ASSERT( !ppInternalArgs, "Internal args get lost when dispatched!" );
910 
911         SfxItemPool &rPool = GetDispatcher()->GetFrame()->GetObjectShell()->GetPool();
912         SfxRequest aReq( nId, nCallMode, rPool );
913         aReq.SetModifier( nModi );
914         if( ppItems )
915             while( *ppItems )
916                 aReq.AppendItem( **ppItems++ );
917 
918         // cache binds to an external dispatch provider
919         sal_Int16 eRet = pCache->Dispatch( aReq.GetArgs(), nCallMode == SfxCallMode::SYNCHRON );
920         SfxPoolItem* pPoolItem(nullptr);
921         if ( eRet == css::frame::DispatchResultState::DONTKNOW )
922             pPoolItem = new SfxVoidItem( nId );
923         else
924             pPoolItem = new SfxBoolItem( nId, eRet == css::frame::DispatchResultState::SUCCESS);
925 
926         return SfxPoolItemHolder(rPool, pPoolItem, true);
927     }
928 
929     // slot is handled internally by SfxDispatcher
930     if ( pImpl->bMsgDirty )
931         UpdateSlotServer_Impl();
932 
933     SfxShell *pShell=nullptr;
934     const SfxSlot *pSlot=nullptr;
935 
936     const SfxSlotServer* pServer = pCache->GetSlotServer( rDispatcher, pImpl->xProv );
937     if ( !pServer )
938     {
939         return SfxPoolItemHolder();
940     }
941     else
942     {
943         pShell = rDispatcher.GetShell( pServer->GetShellLevel() );
944         pSlot = pServer->GetSlot();
945     }
946 
947     if (!pShell)
948         return SfxPoolItemHolder();
949 
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 )
957     {
958         SfxAllItemSet aSet( rPool );
959         for ( const SfxPoolItem **pArg = ppInternalArgs; *pArg; ++pArg )
960             aSet.Put( **pArg );
961         aReq.SetInternalArgs_Impl( aSet );
962     }
963 
964     Execute_Impl( aReq, pSlot, pShell );
965 
966     const SfxPoolItemHolder& rRetval(aReq.GetReturnValue());
967 
968     if (!rRetval)
969         return SfxPoolItemHolder(rPool, new SfxVoidItem( nId ), true);
970 
971     return rRetval;
972 }
973 
Execute_Impl(SfxRequest & aReq,const SfxSlot * pSlot,SfxShell * pShell)974 void SfxBindings::Execute_Impl( SfxRequest& aReq, const SfxSlot* pSlot, SfxShell* pShell )
975 {
976     SfxItemPool &rPool = pShell->GetPool();
977 
978     if ( SfxSlotKind::Attribute == pSlot->GetKind() )
979     {
980         // Which value has to be mapped for Attribute slots
981         const sal_uInt16 nSlotId = pSlot->GetSlotId();
982         aReq.SetSlot( nSlotId );
983         if ( pSlot->IsMode(SfxSlotMode::TOGGLE) )
984         {
985             // The value is attached to a toggleable attribute (Bools)
986             sal_uInt16 nWhich = pSlot->GetWhich(rPool);
987             SfxItemSet aSet(rPool, nWhich, nWhich);
988             SfxStateFunc pFunc = pSlot->GetStateFnc();
989             (*pFunc)(pShell, aSet);
990             const SfxPoolItem *pOldItem;
991             SfxItemState eState = aSet.GetItemState(nWhich, true, &pOldItem);
992             if ( eState == SfxItemState::DISABLED )
993                 return;
994 
995             if ( SfxItemState::DEFAULT == eState && SfxItemPool::IsWhich(nWhich) )
996                 pOldItem = &aSet.Get(nWhich);
997 
998             if ( SfxItemState::SET == eState ||
999                  ( SfxItemState::DEFAULT == eState &&
1000                    SfxItemPool::IsWhich(nWhich) &&
1001                    pOldItem ) )
1002             {
1003                 if ( auto pOldBoolItem = dynamic_cast< const SfxBoolItem *>( pOldItem ) )
1004                 {
1005                     // we can toggle Bools
1006                     bool bOldValue = pOldBoolItem->GetValue();
1007                     std::unique_ptr<SfxBoolItem> pNewItem(static_cast<SfxBoolItem*>(pOldItem->Clone()));
1008                     pNewItem->SetValue( !bOldValue );
1009                     aReq.AppendItem( *pNewItem );
1010                 }
1011                 else if ( auto pOldEnumItem = dynamic_cast< const SfxEnumItemInterface *>( pOldItem ) )
1012                 {
1013                     if (pOldEnumItem->HasBoolValue())
1014                     {
1015                         // and Enums with Bool-Interface
1016                         std::unique_ptr<SfxEnumItemInterface> pNewItem(
1017                             static_cast<SfxEnumItemInterface*>(pOldEnumItem->Clone()));
1018                         pNewItem->SetBoolValue(!pOldEnumItem->GetBoolValue());
1019                         aReq.AppendItem( *pNewItem );
1020                     }
1021                 }
1022                 else {
1023                     OSL_FAIL( "Toggle only for Enums and Bools allowed" );
1024                 }
1025             }
1026             else if ( SfxItemState::INVALID == eState )
1027             {
1028                 // Create one Status-Item for each Factory
1029                 std::unique_ptr<SfxPoolItem> pNewItem = pSlot->GetType()->CreateItem();
1030                 DBG_ASSERT( pNewItem, "Toggle to slot without ItemFactory" );
1031                 pNewItem->SetWhich( nWhich );
1032 
1033                 if ( auto pNewBoolItem = dynamic_cast<SfxBoolItem *>( pNewItem.get() ) )
1034                 {
1035                   // we can toggle Bools
1036                     pNewBoolItem->SetValue( true );
1037                     aReq.AppendItem( *pNewItem );
1038                 }
1039                 else if ( auto pEnumItem = dynamic_cast<SfxEnumItemInterface *>( pNewItem.get() ) )
1040                 {
1041                     if (pEnumItem->HasBoolValue())
1042                     {
1043                         // and Enums with Bool-Interface
1044                         pEnumItem->SetBoolValue(true);
1045                         aReq.AppendItem( *pNewItem );
1046                     }
1047                 }
1048                 else {
1049                     OSL_FAIL( "Toggle only for Enums and Bools allowed" );
1050                 }
1051             }
1052             else {
1053                 OSL_FAIL( "suspicious Toggle-Slot" );
1054             }
1055         }
1056 
1057         pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
1058     }
1059     else
1060         pDispatcher->Execute_( *pShell, *pSlot, aReq, aReq.GetCallMode() | SfxCallMode::RECORD );
1061 }
1062 
1063 
UpdateSlotServer_Impl()1064 void SfxBindings::UpdateSlotServer_Impl()
1065 {
1066     // synchronize
1067     pDispatcher->Flush();
1068 
1069     if ( pImpl->bAllMsgDirty )
1070     {
1071         if ( !nRegLevel )
1072         {
1073             pImpl->bContextChanged = false;
1074         }
1075         else
1076             pImpl->bContextChanged = true;
1077     }
1078 
1079     for (size_t i = 0; i < pImpl->pCaches.size(); ++i)
1080     {
1081         //GetSlotServer can modify pImpl->pCaches
1082         pImpl->pCaches[i]->GetSlotServer(*pDispatcher, pImpl->xProv);
1083     }
1084     pImpl->bMsgDirty = pImpl->bAllMsgDirty = false;
1085 
1086     Broadcast( SfxHint(SfxHintId::DocChanged) );
1087 }
1088 
1089 
CreateSet_Impl(SfxStateCache & rCache,const SfxSlot * & pRealSlot,const SfxSlotServer ** pMsgServer,SfxFoundCacheArr_Impl & rFound)1090 std::optional<SfxItemSet> SfxBindings::CreateSet_Impl
1091 (
1092     SfxStateCache&          rCache,     // in: Status-Cache from nId
1093     const SfxSlot*&         pRealSlot,  // out: RealSlot to nId
1094     const SfxSlotServer**   pMsgServer, // out: Slot-Server to nId
1095     SfxFoundCacheArr_Impl&  rFound      // out: List of Caches for Siblings
1096 )
1097 {
1098     DBG_ASSERT( !pImpl->bMsgDirty, "CreateSet_Impl with dirty MessageServer" );
1099     assert(pDispatcher);
1100 
1101     const SfxSlotServer* pMsgSvr = rCache.GetSlotServer(*pDispatcher, pImpl->xProv);
1102     if (!pMsgSvr)
1103         return {};
1104 
1105     pRealSlot = nullptr;
1106     *pMsgServer = pMsgSvr;
1107 
1108     sal_uInt16 nShellLevel = pMsgSvr->GetShellLevel();
1109     SfxShell *pShell = pDispatcher->GetShell( nShellLevel );
1110     if ( !pShell ) // rare GPF when browsing through update from Inet-Notify
1111         return {};
1112 
1113     SfxItemPool &rPool = pShell->GetPool();
1114 
1115     // get the status method, which is served by the rCache
1116     SfxStateFunc pFnc = nullptr;
1117     pRealSlot = pMsgSvr->GetSlot();
1118 
1119     pFnc = pRealSlot->GetStateFnc();
1120 
1121     // the RealSlot is always on
1122     SfxFoundCache_Impl aFound(pRealSlot->GetWhich(rPool), pRealSlot, rCache);
1123     rFound.push_back( aFound );
1124 
1125     // Search through the bindings for slots served by the same function. This ,    // will only affect slots which are present in the found interface.
1126 
1127     // The position of the  Statecaches in StateCache-Array
1128     std::size_t nCachePos = pImpl->nMsgPos;
1129     const SfxSlot *pSibling = pRealSlot->GetNextSlot();
1130 
1131     // the Slots ODF and interfaces are linked in a circle
1132     while ( pSibling > pRealSlot )
1133     {
1134         SfxStateFunc pSiblingFnc=nullptr;
1135         SfxStateCache *pSiblingCache =
1136                 GetStateCache( pSibling->GetSlotId(), &nCachePos );
1137 
1138         // Is the slot cached ?
1139         if ( pSiblingCache )
1140         {
1141             const SfxSlotServer *pServ = pSiblingCache->GetSlotServer(*pDispatcher, pImpl->xProv);
1142             if ( pServ && pServ->GetShellLevel() == nShellLevel )
1143                 pSiblingFnc = pServ->GetSlot()->GetStateFnc();
1144         }
1145 
1146         // Does the slot have to be updated at all?
1147         bool bInsert = pSiblingCache && pSiblingCache->IsControllerDirty();
1148 
1149         // It is not enough to ask for the same shell!!
1150         bool bSameMethod = pSiblingCache && pFnc == pSiblingFnc;
1151 
1152         if ( bInsert && bSameMethod )
1153         {
1154             SfxFoundCache_Impl aFoundCache(
1155                 pSibling->GetWhich(rPool),
1156                 pSibling, *pSiblingCache);
1157 
1158             rFound.push_back( aFoundCache );
1159         }
1160 
1161         pSibling = pSibling->GetNextSlot();
1162     }
1163 
1164     // Create a Set from the ranges
1165     WhichRangesContainer ranges;
1166     size_t i = 0;
1167     while ( i < rFound.size() )
1168     {
1169         const sal_uInt16 nWhich1 = rFound[i].nWhichId;
1170             // consecutive numbers
1171         for ( ; i < rFound.size()-1; ++i )
1172             if ( rFound[i].nWhichId+1 != rFound[i+1].nWhichId )
1173                 break;
1174         const sal_uInt16 nWhich2 = rFound[i++].nWhichId;
1175         ranges = ranges.MergeRange(nWhich1, nWhich2);
1176     }
1177     SfxItemSet aSet(rPool, std::move(ranges));
1178     return aSet;
1179 }
1180 
1181 
UpdateControllers_Impl(const SfxFoundCache_Impl & rFound,const SfxPoolItem * pItem,SfxItemState eState)1182 void SfxBindings::UpdateControllers_Impl
1183 (
1184     const SfxFoundCache_Impl&   rFound, // Cache, Slot, Which etc.
1185     const SfxPoolItem*          pItem,  // item to send to controller
1186     SfxItemState                eState  // state of item
1187 )
1188 {
1189     SfxStateCache& rCache = rFound.rCache;
1190     const SfxSlot* pSlot = rFound.pSlot;
1191     DBG_ASSERT( !pSlot || rCache.GetId() == pSlot->GetSlotId(), "SID mismatch" );
1192 
1193     // bound until now, the Controller to update the Slot.
1194     if (!rCache.IsControllerDirty())
1195         return;
1196 
1197     if ( SfxItemState::INVALID == eState )
1198     {
1199         // ambiguous
1200         rCache.SetState( SfxItemState::INVALID, INVALID_POOL_ITEM );
1201     }
1202     else if ( SfxItemState::DEFAULT == eState &&
1203               SfxItemPool::IsSlot(rFound.nWhichId) )
1204     {
1205         // no Status or Default but without Pool
1206         rCache.SetState( SfxItemState::UNKNOWN, DISABLED_POOL_ITEM );
1207     }
1208     else if ( SfxItemState::DISABLED == eState )
1209         rCache.SetState(SfxItemState::DISABLED, nullptr);
1210     else
1211         rCache.SetState(SfxItemState::DEFAULT, pItem);
1212 }
1213 
IMPL_LINK(SfxBindings,NextJob,Timer *,pTimer,void)1214 IMPL_LINK( SfxBindings, NextJob, Timer *, pTimer, void )
1215 {
1216     SfxViewFrame* pFrame = pDispatcher ? pDispatcher->GetFrame() : nullptr;
1217     SfxLokLanguageGuard aGuard(pFrame ? pFrame->GetViewShell() : nullptr);
1218 
1219     NextJob_Impl(pTimer);
1220 }
1221 
NextJob_Impl(Timer const * pTimer)1222 bool SfxBindings::NextJob_Impl(Timer const * pTimer)
1223 {
1224     const unsigned MAX_INPUT_DELAY = 200;
1225 
1226     if ( Application::GetLastInputInterval() < MAX_INPUT_DELAY && pTimer )
1227     {
1228         pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING);
1229         return true;
1230     }
1231 
1232     SfxApplication *pSfxApp = SfxGetpApp();
1233 
1234     if( pDispatcher )
1235         pDispatcher->Update_Impl();
1236 
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() )
1240     {
1241         return true;
1242     }
1243     if ( !pDispatcher || !pDispatcher->IsFlushed() )
1244     {
1245         return true;
1246     }
1247 
1248     // if possible Update all server / happens in its own time slice
1249     if ( pImpl->bMsgDirty )
1250     {
1251         UpdateSlotServer_Impl();
1252         return false;
1253     }
1254 
1255     pImpl->bAllDirty = false;
1256     pImpl->aAutoTimer.SetTimeout(TIMEOUT_UPDATING);
1257 
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 )
1264     {
1265         // iterate through the bound functions
1266         bool bJobDone = false;
1267         while ( !bJobDone )
1268         {
1269             SfxStateCache* pCache = pImpl->pCaches[pImpl->nMsgPos].get();
1270             assert(pCache && "invalid SfxStateCache-position in job queue");
1271             bool bWasDirty = pCache->IsControllerDirty();
1272             if ( bWasDirty )
1273             {
1274                 Update_Impl(*pCache);
1275                 DBG_ASSERT(nCount == pImpl->pCaches.size(), "Reschedule in StateChanged => buff");
1276             }
1277 
1278             // skip to next function binding
1279             ++pImpl->nMsgPos;
1280 
1281             // keep job if it is not completed, but any input is available
1282             bJobDone = pImpl->nMsgPos >= nCount;
1283             if ( bJobDone && pImpl->bFirstRound )
1284             {
1285 
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;
1291             }
1292 
1293             if ( bWasDirty && !bJobDone && bPreEmptive && (--nLoops == 0) )
1294             {
1295                 pImpl->bInNextJob = false;
1296                 return false;
1297             }
1298         }
1299     }
1300 
1301     pImpl->nMsgPos = 0;
1302 
1303     pImpl->aAutoTimer.Stop();
1304 
1305     // Update round is finished
1306     pImpl->bInNextJob = false;
1307     Broadcast(SfxHint(SfxHintId::UpdateDone));
1308     return true;
1309 }
1310 
1311 
EnterRegistrations(std::string_view pFile,int nLine)1312 sal_uInt16 SfxBindings::EnterRegistrations(std::string_view pFile, int nLine)
1313 {
1314     SAL_INFO(
1315         "sfx.control",
1316         std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
1317             << " Level = " << nRegLevel << " SfxBindings::EnterRegistrations "
1318             << (!pFile.empty()
1319                 ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
1320 
1321     // When bindings are locked, also lock sub bindings.
1322     if ( pImpl->pSubBindings )
1323     {
1324         pImpl->pSubBindings->ENTERREGISTRATIONS();
1325 
1326         // These EnterRegistrations are not "real" for the SubBindings
1327         pImpl->pSubBindings->pImpl->nOwnRegLevel--;
1328 
1329         // Synchronize Bindings
1330         pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel + 1;
1331     }
1332 
1333     pImpl->nOwnRegLevel++;
1334 
1335     // check if this is the outer most level
1336     if ( ++nRegLevel == 1 )
1337     {
1338         // stop background-processing
1339         pImpl->aAutoTimer.Stop();
1340 
1341         // flush the cache
1342         pImpl->nCachedFunc1 = 0;
1343         pImpl->nCachedFunc2 = 0;
1344 
1345         // Mark if the all of the Caches have disappeared.
1346         pImpl->bCtrlReleased = false;
1347     }
1348 
1349     return nRegLevel;
1350 }
1351 
1352 
LeaveRegistrations(std::string_view pFile,int nLine)1353 void SfxBindings::LeaveRegistrations(  std::string_view pFile, int nLine )
1354 {
1355     DBG_ASSERT( nRegLevel, "Leave without Enter" );
1356 
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 )
1360     {
1361         // Synchronize Bindings
1362         pImpl->pSubBindings->nRegLevel = nRegLevel + pImpl->pSubBindings->pImpl->nOwnRegLevel;
1363 
1364         // This LeaveRegistrations is not "real" for SubBindings
1365         pImpl->pSubBindings->pImpl->nOwnRegLevel++;
1366         pImpl->pSubBindings->LEAVEREGISTRATIONS();
1367     }
1368 
1369     pImpl->nOwnRegLevel--;
1370 
1371     // check if this is the outer most level
1372     if ( --nRegLevel == 0 && SfxGetpApp() && !SfxGetpApp()->IsDowning() )
1373     {
1374         if ( pImpl->bContextChanged )
1375         {
1376             pImpl->bContextChanged = false;
1377         }
1378 
1379         SfxViewFrame* pFrame = pDispatcher->GetFrame();
1380 
1381         // If possible remove unused Caches, for example prepare PlugInInfo
1382         if ( pImpl->bCtrlReleased )
1383         {
1384             for ( sal_uInt16 nCache = pImpl->pCaches.size(); nCache > 0; --nCache )
1385             {
1386                 // Get Cache via css::sdbcx::Index
1387                 SfxStateCache *pCache = pImpl->pCaches[nCache-1].get();
1388 
1389                 // No interested Controller present
1390                 if ( pCache->GetItemLink() == nullptr && !pCache->GetInternalController() )
1391                 {
1392                     // Remove Cache. Safety: first remove and then delete
1393                     pImpl->pCaches.erase(pImpl->pCaches.begin() + nCache - 1);
1394                 }
1395             }
1396         }
1397 
1398         // restart background-processing
1399         pImpl->nMsgPos = 0;
1400         if ( !pFrame || !pFrame->GetObjectShell() )
1401             return;
1402         if ( !pImpl->pCaches.empty() )
1403         {
1404             pImpl->aAutoTimer.Stop();
1405             pImpl->aAutoTimer.SetTimeout(TIMEOUT_FIRST);
1406             pImpl->aAutoTimer.Start();
1407         }
1408     }
1409 
1410     SAL_INFO(
1411         "sfx.control",
1412         std::setw(std::min(nRegLevel, sal_uInt16(8))) << ' ' << "this = " << this
1413             << " Level = " << nRegLevel << " SfxBindings::LeaveRegistrations "
1414             << (!pFile.empty()
1415                 ? SAL_STREAM("File: " << pFile << " Line: " << nLine) : ""));
1416 }
1417 
1418 
SetDispatcher(SfxDispatcher * pDisp)1419 void SfxBindings::SetDispatcher( SfxDispatcher *pDisp )
1420 {
1421     SfxDispatcher *pOldDispat = pDispatcher;
1422     if ( pDisp == pDispatcher )
1423         return;
1424 
1425     if ( pOldDispat )
1426     {
1427         SfxBindings* pBind = pOldDispat->GetBindings();
1428         while ( pBind )
1429         {
1430             if ( pBind->pImpl->pSubBindings == this && pBind->pDispatcher != pDisp )
1431                 pBind->SetSubBindings_Impl( nullptr );
1432             pBind = pBind->pImpl->pSubBindings;
1433         }
1434     }
1435 
1436     pDispatcher = pDisp;
1437 
1438     css::uno::Reference < css::frame::XDispatchProvider > xProv;
1439     if ( pDisp )
1440         xProv.set( pDisp->GetFrame()->GetFrame().GetFrameInterface(), UNO_QUERY );
1441 
1442     SetDispatchProvider_Impl( xProv );
1443     InvalidateAll( true );
1444 
1445     if ( pDispatcher && !pOldDispat )
1446     {
1447         if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat )
1448         {
1449             OSL_FAIL( "SubBindings already set before activating!" );
1450             pImpl->pSubBindings->ENTERREGISTRATIONS();
1451         }
1452         LEAVEREGISTRATIONS();
1453     }
1454     else if( !pDispatcher )
1455     {
1456         ENTERREGISTRATIONS();
1457         if ( pImpl->pSubBindings && pImpl->pSubBindings->pDispatcher != pOldDispat )
1458         {
1459             OSL_FAIL( "SubBindings still set even when deactivating!" );
1460             pImpl->pSubBindings->LEAVEREGISTRATIONS();
1461         }
1462     }
1463 
1464     Broadcast( SfxHint( SfxHintId::DataChanged ) );
1465 
1466     if ( !pDisp )
1467         return;
1468 
1469     SfxBindings* pBind = pDisp->GetBindings();
1470     while ( pBind && pBind != this )
1471     {
1472         if ( !pBind->pImpl->pSubBindings )
1473         {
1474             pBind->SetSubBindings_Impl( this );
1475             break;
1476         }
1477 
1478         pBind = pBind->pImpl->pSubBindings;
1479     }
1480 }
1481 
1482 
ClearCache_Impl(sal_uInt16 nSlotId)1483 void SfxBindings::ClearCache_Impl( sal_uInt16 nSlotId )
1484 {
1485     SfxStateCache* pCache = GetStateCache(nSlotId);
1486     if (!pCache)
1487         return;
1488     pCache->ClearCache();
1489 }
1490 
1491 
StartUpdate_Impl(bool bComplete)1492 void SfxBindings::StartUpdate_Impl( bool bComplete )
1493 {
1494     if ( pImpl->pSubBindings )
1495         pImpl->pSubBindings->StartUpdate_Impl( bComplete );
1496 
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);
1503 }
1504 
1505 
QueryState(sal_uInt16 nSlot,std::unique_ptr<SfxPoolItem> & rpState)1506 SfxItemState SfxBindings::QueryState( sal_uInt16 nSlot, std::unique_ptr<SfxPoolItem> &rpState )
1507 {
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 )
1513     {
1514         const SfxSlot* pSlot = SfxSlotPool::GetSlotPool( pDispatcher->GetFrame() ).GetSlot( nSlot );
1515         if ( !pSlot || pSlot->aUnoName.isEmpty() )
1516             return SfxItemState::DISABLED;
1517 
1518         css::util::URL aURL;
1519         OUString aCmd( u".uno:"_ustr );
1520         aURL.Protocol = aCmd;
1521         aURL.Path = pSlot->GetUnoName();
1522         aCmd += aURL.Path;
1523         aURL.Complete = aCmd;
1524         aURL.Main = aCmd;
1525 
1526         if ( !xDisp.is() )
1527             xDisp = pImpl->xProv->queryDispatch( aURL, OUString(), 0 );
1528 
1529         if ( xDisp.is() )
1530         {
1531             if (!dynamic_cast<SfxOfficeDispatch*>(xDisp.get()))
1532             {
1533                 bool bDeleteCache = false;
1534                 if ( !pCache )
1535                 {
1536                     pCache = new SfxStateCache( nSlot );
1537                     pCache->GetSlotServer( *GetDispatcher_Impl(), pImpl->xProv );
1538                     bDeleteCache = true;
1539                 }
1540 
1541                 SfxItemState eState = SfxItemState::SET;
1542                 rtl::Reference<BindDispatch_Impl> xBind(new BindDispatch_Impl( xDisp, aURL, pCache, pSlot ));
1543                 xDisp->addStatusListener( xBind, aURL );
1544                 if ( !xBind->GetStatus().IsEnabled )
1545                 {
1546                     eState = SfxItemState::DISABLED;
1547                 }
1548                 else
1549                 {
1550                     css::uno::Any aAny = xBind->GetStatus().State;
1551                     const css::uno::Type& aType = aAny.getValueType();
1552 
1553                     if ( aType == cppu::UnoType<bool>::get() )
1554                     {
1555                         bool bTemp = false;
1556                         aAny >>= bTemp ;
1557                         rpState.reset(new SfxBoolItem( nSlot, bTemp ));
1558                     }
1559                     else if ( aType == ::cppu::UnoType< ::cppu::UnoUnsignedShortType >::get() )
1560                     {
1561                         sal_uInt16 nTemp = 0;
1562                         aAny >>= nTemp ;
1563                         rpState.reset(new SfxUInt16Item( nSlot, nTemp ));
1564                     }
1565                     else if ( aType == cppu::UnoType<sal_uInt32>::get() )
1566                     {
1567                         sal_uInt32 nTemp = 0;
1568                         aAny >>= nTemp ;
1569                         rpState.reset(new SfxUInt32Item( nSlot, nTemp ));
1570                     }
1571                     else if ( aType == cppu::UnoType<OUString>::get() )
1572                     {
1573                         OUString sTemp ;
1574                         aAny >>= sTemp ;
1575                         rpState.reset(new SfxStringItem( nSlot, sTemp ));
1576                     }
1577                     else
1578                         rpState.reset(new SfxVoidItem( nSlot ));
1579                 }
1580 
1581                 xDisp->removeStatusListener( xBind, aURL );
1582                 xBind->Release();
1583                 xBind.clear();
1584                 if ( bDeleteCache )
1585                 {
1586                     delete pCache;
1587                     pCache = nullptr;
1588                 }
1589                 return eState;
1590             }
1591         }
1592     }
1593 
1594     // Then test at the dispatcher to check if the returned items from
1595     // there are always DELETE_ON_IDLE, a copy of it has to be made in
1596     // order to allow for transition of ownership.
1597     SfxPoolItemHolder aResult;
1598     const SfxItemState eState(pDispatcher->QueryState(nSlot, aResult));
1599 
1600     if (SfxItemState::SET == eState)
1601     {
1602         DBG_ASSERT( aResult.getItem(), "SfxItemState::SET but no item!" );
1603         if (aResult)
1604             rpState.reset(aResult.getItem()->Clone());
1605     }
1606     else if (SfxItemState::DEFAULT == eState && aResult)
1607     {
1608         rpState.reset(aResult.getItem()->Clone());
1609     }
1610 
1611     return eState;
1612 }
1613 
QueryControlState(sal_uInt16 nSlot,boost::property_tree::ptree & rState)1614 void SfxBindings::QueryControlState( sal_uInt16 nSlot, boost::property_tree::ptree& rState )
1615 {
1616     if ( SfxGetpApp()->IsDowning() )
1617         return;
1618 
1619     if ( pDispatcher )
1620         pDispatcher->Flush();
1621 
1622     if ( pImpl->pSubBindings )
1623         pImpl->pSubBindings->QueryControlState( nSlot, rState );
1624 
1625     SfxStateCache* pCache = GetStateCache( nSlot );
1626     if ( !pCache )
1627         return;
1628 
1629     if ( pImpl->bMsgDirty )
1630     {
1631         UpdateSlotServer_Impl();
1632         pCache = GetStateCache( nSlot );
1633     }
1634 
1635     if (pCache && pCache->GetItemLink() )
1636     {
1637         pCache->GetState(rState);
1638     }
1639 }
1640 
QuerySlotId(const util::URL & aURL)1641 sal_uInt16 SfxBindings::QuerySlotId( const util::URL& aURL )
1642 {
1643     if (!pImpl)
1644         return 0;
1645 
1646     css::uno::Reference<css::frame::XDispatch> xDispatch =
1647         pImpl->xProv->queryDispatch(aURL, OUString(), 0);
1648     if (!xDispatch.is())
1649         return 0;
1650 
1651     SfxOfficeDispatch* pDispatch = dynamic_cast<SfxOfficeDispatch*>(xDispatch.get());
1652     if (!pDispatch)
1653         return 0;
1654 
1655     return pDispatch->GetId();
1656 }
1657 
SetSubBindings_Impl(SfxBindings * pSub)1658 void SfxBindings::SetSubBindings_Impl( SfxBindings *pSub )
1659 {
1660     if ( pImpl->pSubBindings )
1661     {
1662         pImpl->pSubBindings->SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > () );
1663     }
1664 
1665     pImpl->pSubBindings = pSub;
1666 
1667     if ( pSub )
1668     {
1669         pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
1670     }
1671 }
1672 
GetSubBindings_Impl() const1673 SfxBindings* SfxBindings::GetSubBindings_Impl() const
1674 {
1675     return pImpl->pSubBindings;
1676 }
1677 
SetWorkWindow_Impl(std::unique_ptr<SfxWorkWindow> xWork)1678 void SfxBindings::SetWorkWindow_Impl( std::unique_ptr<SfxWorkWindow> xWork )
1679 {
1680     pImpl->mxWorkWin = std::move(xWork);
1681 }
1682 
GetWorkWindow_Impl() const1683 SfxWorkWindow* SfxBindings::GetWorkWindow_Impl() const
1684 {
1685     return pImpl->mxWorkWin.get();
1686 }
1687 
IsInUpdate() const1688 bool SfxBindings::IsInUpdate() const
1689 {
1690     bool bInUpdate = pImpl->bInUpdate;
1691     if ( !bInUpdate && pImpl->pSubBindings )
1692         bInUpdate = pImpl->pSubBindings->IsInUpdate();
1693     return bInUpdate;
1694 }
1695 
SetVisibleState(sal_uInt16 nId,bool bShow)1696 void SfxBindings::SetVisibleState( sal_uInt16 nId, bool bShow )
1697 {
1698     SfxStateCache *pCache = GetStateCache( nId );
1699     if ( pCache )
1700         pCache->SetVisibleState( bShow );
1701 }
1702 
SetActiveFrame(const css::uno::Reference<css::frame::XFrame> & rFrame)1703 void SfxBindings::SetActiveFrame( const css::uno::Reference< css::frame::XFrame > & rFrame )
1704 {
1705     if ( rFrame.is() || !pDispatcher )
1706         SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > ( rFrame, css::uno::UNO_QUERY ) );
1707     else
1708         SetDispatchProvider_Impl( css::uno::Reference< css::frame::XDispatchProvider > (
1709             pDispatcher->GetFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY ) );
1710 }
1711 
GetActiveFrame() const1712 css::uno::Reference< css::frame::XFrame > SfxBindings::GetActiveFrame() const
1713 {
1714     const css::uno::Reference< css::frame::XFrame > xFrame( pImpl->xProv, css::uno::UNO_QUERY );
1715     if ( xFrame.is() || !pDispatcher )
1716         return xFrame;
1717     else
1718         return pDispatcher->GetFrame()->GetFrame().GetFrameInterface();
1719 }
1720 
SetDispatchProvider_Impl(const css::uno::Reference<css::frame::XDispatchProvider> & rProv)1721 void SfxBindings::SetDispatchProvider_Impl( const css::uno::Reference< css::frame::XDispatchProvider > & rProv )
1722 {
1723     bool bInvalidate = ( rProv != pImpl->xProv );
1724     if ( bInvalidate )
1725     {
1726         pImpl->xProv = rProv;
1727         InvalidateAll( true );
1728     }
1729 
1730     if ( pImpl->pSubBindings )
1731         pImpl->pSubBindings->SetDispatchProvider_Impl( pImpl->xProv );
1732 }
1733 
GetRecorder() const1734 const css::uno::Reference< css::frame::XDispatchRecorder >& SfxBindings::GetRecorder() const
1735 {
1736     return pImpl->xRecorder;
1737 }
1738 
SetRecorder_Impl(css::uno::Reference<css::frame::XDispatchRecorder> const & rRecorder)1739 void SfxBindings::SetRecorder_Impl( css::uno::Reference< css::frame::XDispatchRecorder > const & rRecorder )
1740 {
1741     pImpl->xRecorder = rRecorder;
1742 }
1743 
ContextChanged_Impl()1744 void SfxBindings::ContextChanged_Impl()
1745 {
1746     if ( !pImpl->bInUpdate && ( !pImpl->bContextChanged || !pImpl->bAllMsgDirty ) )
1747     {
1748         InvalidateAll( true );
1749     }
1750 }
1751 
GetDispatch(const SfxSlot * pSlot,const util::URL & aURL,bool bMasterCommand)1752 uno::Reference < frame::XDispatch > SfxBindings::GetDispatch( const SfxSlot* pSlot, const util::URL& aURL, bool bMasterCommand )
1753 {
1754     uno::Reference < frame::XDispatch > xRet;
1755     SfxStateCache* pCache = GetStateCache( pSlot->nSlotId );
1756     if ( pCache && !bMasterCommand )
1757         xRet = pCache->GetInternalDispatch();
1758     if ( !xRet.is() )
1759     {
1760         // dispatches for slaves are unbound, they don't have a state
1761         SfxOfficeDispatch* pDispatch = bMasterCommand ?
1762             new SfxOfficeDispatch( pDispatcher, pSlot, aURL ) :
1763             new SfxOfficeDispatch( *this, pDispatcher, pSlot, aURL );
1764 
1765         pDispatch->SetMasterUnoCommand( bMasterCommand );
1766         xRet.set( pDispatch );
1767         if ( !pCache )
1768             pCache = GetStateCache( pSlot->nSlotId );
1769 
1770         DBG_ASSERT( pCache, "No cache for OfficeDispatch!" );
1771         if ( pCache && !bMasterCommand )
1772             pCache->SetInternalDispatch( xRet );
1773     }
1774 
1775     return xRet;
1776 }
1777 
GetTimer()1778 Timer& SfxBindings::GetTimer()
1779 {
1780     return pImpl->aAutoTimer;
1781 }
1782 
1783 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1784