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