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 <config_feature_desktop.h>
21 
22 #include <algorithm>
23 #include <cstddef>
24 #include <deque>
25 #include <vector>
26 
27 #include <stdarg.h>
28 #include <stdlib.h>
29 
30 #include <boost/property_tree/json_parser.hpp>
31 
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/frame/XDispatchRecorderSupplier.hpp>
34 #include <com/sun/star/frame/XLayoutManager.hpp>
35 #include <com/sun/star/frame/XPopupMenuController.hpp>
36 #include <com/sun/star/uno/XComponentContext.hpp>
37 #include <com/sun/star/ui/ContextMenuExecuteEvent.hpp>
38 
39 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
40 #include <comphelper/lok.hxx>
41 #include <comphelper/processfactory.hxx>
42 #include <comphelper/propertyvalue.hxx>
43 #include <rtl/strbuf.hxx>
44 #include <sal/log.hxx>
45 #include <sfx2/app.hxx>
46 #include <sfx2/bindings.hxx>
47 #include <sfx2/childwin.hxx>
48 #include <sfx2/dispatch.hxx>
49 #include <sfx2/docfac.hxx>
50 #include <sfx2/docfile.hxx>
51 #include <sfx2/hintpost.hxx>
52 #include <sfx2/ipclient.hxx>
53 #include <sfx2/module.hxx>
54 #include <sfx2/msg.hxx>
55 #include <sfx2/msgpool.hxx>
56 #include <sfx2/objface.hxx>
57 #include <sfx2/request.hxx>
58 #include <sfx2/sfxhelp.hxx>
59 #include <sfx2/sfxsids.hrc>
60 #include <sfx2/sfxuno.hxx>
61 #include <sfx2/viewfrm.hxx>
62 #include <sfx2/viewsh.hxx>
63 #include <svl/eitem.hxx>
64 #include <svl/intitem.hxx>
65 #include <svl/itemiter.hxx>
66 #include <svl/itempool.hxx>
67 #include <svl/undo.hxx>
68 #include <svl/whiter.hxx>
69 #include <svtools/helpopt.hxx>
70 #include <toolkit/awt/vclxmenu.hxx>
71 #include <toolkit/helper/vclunohelper.hxx>
72 #include <vcl/wrkwin.hxx>
73 #include <vcl/idle.hxx>
74 
75 #include <appdata.hxx>
76 #include <sfxtypes.hxx>
77 #include <slotserv.hxx>
78 #include <workwin.hxx>
79 
80 typedef std::vector<SfxShell*> SfxShellStack_Impl;
81 
82 struct SfxToDo_Impl
83 {
84     SfxShell*  pCluster;
85     bool       bPush;
86     bool       bDelete;
87     bool       bDeleted;
88     bool       bUntil;
89 
SfxToDo_ImplSfxToDo_Impl90     SfxToDo_Impl( bool bOpPush, bool bOpDelete, bool bOpUntil, SfxShell& rCluster )
91         : pCluster(&rCluster)
92         , bPush(bOpPush)
93         , bDelete(bOpDelete)
94         , bDeleted(false)
95         , bUntil(bOpUntil)
96                 {}
97 };
98 
99 struct SfxObjectBars_Impl
100 {
101     ToolbarId          eId;      // ConfigId of the Toolbox
102     sal_uInt16         nPos;
103     SfxVisibilityFlags nFlags;   // special visibility flags
104 
SfxObjectBars_ImplSfxObjectBars_Impl105     SfxObjectBars_Impl() : eId(ToolbarId::None), nPos(0), nFlags(SfxVisibilityFlags::Invisible) {}
106 };
107 
108 struct SfxDispatcher_Impl
109 {
110     //When the dispatched is locked, SfxRequests accumulate in aReqArr for
111     //later dispatch when unlocked via Post
112 
113     //The pointers are typically deleted in Post, only if we never get around
114     //to posting them do we delete the unposted requests.
115     std::vector<std::unique_ptr<SfxRequest>>
116                          aReqArr;
117     SfxShellStack_Impl   aStack;        // active functionality
118     Idle                 aIdle;        // for Flush
119     std::deque<SfxToDo_Impl> aToDoStack;    // not processed Push/Pop
120     SfxViewFrame*        pFrame;        // NULL or associated Frame
121     tools::SvRef<SfxHintPoster>
122                          xPoster;       // Execute asynchronous
123     bool                 bFlushing;     // sal_True during Flush //?
124     bool                 bUpdated;      // Update_Impl has run
125     bool                 bLocked;       // No Execute
126     bool                 bInvalidateOnUnlock;   // because someone asked
127     bool                 bActive;       // not to be confused with set!
128     bool*                pInCallAliveFlag;   // view the Destructor Stack
129     SfxObjectBars_Impl   aObjBars[SFX_OBJECTBAR_MAX];
130     SfxObjectBars_Impl   aFixedObjBars[SFX_OBJECTBAR_MAX];
131     std::vector<sal_uInt32> aChildWins;
132     bool                 bNoUI;         // UI only from Parent Dispatcher
133     bool                 bReadOnly;     // Document is ReadOnly
134     bool                 bQuiet;        // Only use parent dispatcher
135 
136     SfxSlotFilterState   nFilterEnabling; // 1==filter enabled slots,
137                                           // 2==ReadOnlyDoc overturned
138     o3tl::span<sal_uInt16 const>
139                          pFilterSIDs;   // sorted Array of SIDs
140     SfxDisableFlags      nDisableFlags;
141     bool                 bFlushed;
142     std::deque< std::deque<SfxToDo_Impl> > aToDoCopyStack;
143 };
144 
145 namespace {
146 
fillPopupMenu(Menu * pMenu)147     boost::property_tree::ptree fillPopupMenu(Menu* pMenu)
148     {
149         // Activate this menu first
150         pMenu->HandleMenuActivateEvent(pMenu);
151         pMenu->HandleMenuDeActivateEvent(pMenu);
152 
153         boost::property_tree::ptree aTree;
154         // If last item inserted is some valid text
155         bool bIsLastItemText = false;
156         sal_uInt16 nCount = pMenu->GetItemCount();
157         for (sal_uInt16 nPos = 0; nPos < nCount; nPos++)
158         {
159             boost::property_tree::ptree aItemTree;
160             const MenuItemType aItemType = pMenu->GetItemType(nPos);
161 
162             if (aItemType == MenuItemType::DONTKNOW)
163                 continue;
164 
165             if (aItemType == MenuItemType::SEPARATOR)
166             {
167                 if (bIsLastItemText)
168                     aItemTree.put("type", "separator");
169                 bIsLastItemText = false;
170             }
171             else
172             {
173                 const sal_uInt16 nItemId = pMenu->GetItemId(nPos);
174                 OUString aCommandURL = pMenu->GetItemCommand(nItemId);
175 
176                 if (aCommandURL.isEmpty())
177                 {
178                     const SfxSlot *pSlot = SFX_SLOTPOOL().GetSlot(nItemId);
179                     if (pSlot)
180                         aCommandURL = pSlot->GetCommandString();
181                 }
182 
183                 const OUString aItemText = pMenu->GetItemText(nItemId);
184                 Menu* pPopupSubmenu = pMenu->GetPopupMenu(nItemId);
185 
186                 if (!aItemText.isEmpty())
187                     aItemTree.put("text", aItemText.toUtf8().getStr());
188 
189                 if (pPopupSubmenu)
190                 {
191                     boost::property_tree::ptree aSubmenu = fillPopupMenu(pPopupSubmenu);
192                     if (aSubmenu.empty())
193                         continue;
194 
195                     aItemTree.put("type", "menu");
196                     if (!aCommandURL.isEmpty())
197                         aItemTree.put("command", aCommandURL.toUtf8().getStr());
198                     aItemTree.push_back(std::make_pair("menu", aSubmenu));
199                 }
200                 else
201                 {
202                     // no point in exposing choices that don't have the .uno:
203                     // command
204                     if (aCommandURL.isEmpty())
205                         continue;
206 
207                     aItemTree.put("type", "command");
208                     aItemTree.put("command", aCommandURL.toUtf8().getStr());
209                 }
210 
211                 aItemTree.put("enabled", pMenu->IsItemEnabled(nItemId));
212 
213                 MenuItemBits aItemBits = pMenu->GetItemBits(nItemId);
214                 bool bHasChecks = true;
215                 if (aItemBits & MenuItemBits::CHECKABLE)
216                     aItemTree.put("checktype", "checkmark");
217                 else if (aItemBits & MenuItemBits::RADIOCHECK)
218                     aItemTree.put("checktype", "radio");
219                 else if (aItemBits & MenuItemBits::AUTOCHECK)
220                     aItemTree.put("checktype", "auto");
221                 else
222                     bHasChecks = false;
223 
224                 if (bHasChecks)
225                     aItemTree.put("checked", pMenu->IsItemChecked(nItemId));
226             }
227 
228             if (!aItemTree.empty())
229             {
230                 aTree.push_back(std::make_pair("", aItemTree));
231                 if (aItemType != MenuItemType::SEPARATOR)
232                     bIsLastItemText = true;
233             }
234         }
235 
236         return aTree;
237     }
238 
239 } // end anonymous namespace
240 
241 
242 /** This method checks if the stack of the SfxDispatchers is flushed, or if
243     push- or pop- commands are pending.
244 */
IsFlushed() const245 bool SfxDispatcher::IsFlushed() const
246 {
247      return xImp->bFlushed;
248 }
249 
250 /** This method performs outstanding push- and pop- commands. For <SfxShell>s,
251     which are new on the stack, the <SfxShell::Activate(bool)> is invoked
252     with bMDI == sal_True, for SfxShells that are removed from the stack, the
253     <SfxShell::Deactivate(bool)> is invoked with bMDI == sal_True
254 */
Flush()255 void SfxDispatcher::Flush()
256 {
257     if (!xImp->bFlushed) FlushImpl();
258 }
259 
260 /** With this method, a <SfxShell> pushed on to the SfxDispatcher.
261     The SfxShell is first marked for push and a timer is set up.
262     First when the timer has counted down to zero the push
263     ( <SfxDispatcher::Flush()> ) is actually performed and the
264     <SfxBindings> is invalidated. While the timer is counting down
265     the opposing push and pop commands on the same SfxShell are
266     leveled out.
267 */
Push(SfxShell & rShell)268 void SfxDispatcher::Push(SfxShell& rShell)
269 
270 {
271     Pop( rShell, SfxDispatcherPopFlags::PUSH );
272 }
273 
274 /** This method checks whether a particular <SfxShell> instance is
275     on the SfxDispatcher.
276 
277     @returns true   The SfxShell instance is on the SfxDispatcher.
278              false  The SfxShell instance is not on the SfxDispatcher.
279 */
IsActive(const SfxShell & rShell)280 bool SfxDispatcher::IsActive(const SfxShell& rShell)
281 
282 {
283     return CheckVirtualStack(rShell);
284 }
285 
286 /** With this method it can be determined whether the SfxDispatcher is
287     locked or unlocked. A locked SfxDispatcher does not perform <SfxRequest>s
288     and no longer provides any status information. It behaves as if all the
289     slots are disabled.
290 
291     The dispatcher is also marked as blocked, if all Dispatcher are locked
292     (<SfxApplication::LockDispatcher()>) or the associated top frame is in the
293     modal-mode and if the specified slot are handled as frame-specific
294     (ie, not served by the application).
295 */
IsLocked() const296 bool SfxDispatcher::IsLocked() const
297 {
298     return xImp->bLocked;
299 }
300 
301 /** With this method it can be determined if the SfxDispacher is the
302     applications dispatcher.
303 
304     @return bool it is the application dispatcher.
305 */
IsAppDispatcher() const306 bool SfxDispatcher::IsAppDispatcher() const
307 {
308     return !xImp->pFrame;
309 }
310 
311 /** Helper function to check whether a slot can be executed and
312     check the execution itself
313 */
Call_Impl(SfxShell & rShell,const SfxSlot & rSlot,SfxRequest & rReq,bool bRecord)314 void SfxDispatcher::Call_Impl(SfxShell& rShell, const SfxSlot &rSlot, SfxRequest &rReq, bool bRecord)
315 {
316     SFX_STACK(SfxDispatcher::Call_Impl);
317 
318     // The slot may be called (meaning enabled)
319     if ( !rSlot.IsMode(SfxSlotMode::FASTCALL) && !rShell.CanExecuteSlot_Impl(rSlot) && !rShell.IsConditionalFastCall(rReq) )
320         return;
321 
322     if ( GetFrame() )
323     {
324         // Recording may start
325         css::uno::Reference< css::frame::XFrame > xFrame =
326                 GetFrame()->GetFrame().GetFrameInterface();
327 
328         css::uno::Reference< css::beans::XPropertySet > xSet(
329                 xFrame,
330                 css::uno::UNO_QUERY);
331 
332         if ( xSet.is() )
333         {
334             css::uno::Any aProp = xSet->getPropertyValue("DispatchRecorderSupplier");
335             css::uno::Reference< css::frame::XDispatchRecorderSupplier > xSupplier;
336             css::uno::Reference< css::frame::XDispatchRecorder > xRecorder;
337             aProp >>= xSupplier;
338             if(xSupplier.is())
339                 xRecorder = xSupplier->getDispatchRecorder();
340 
341             if ( bRecord && xRecorder.is() && !rSlot.IsMode(SfxSlotMode::NORECORD) )
342                 rReq.Record_Impl( rShell, rSlot, xRecorder, GetFrame() );
343         }
344     }
345     // Get all that is needed, because the slot may not have survived the
346     // Execute if it is a 'pseudo slot' for macros or verbs.
347     bool bAutoUpdate = rSlot.IsMode(SfxSlotMode::AUTOUPDATE);
348 
349     // API-call parentheses and document-lock during the calls
350     {
351         // 'this' must respond in the Destructor
352         bool bThisDispatcherAlive = true;
353         bool *pOldInCallAliveFlag = xImp->pInCallAliveFlag;
354         xImp->pInCallAliveFlag = &bThisDispatcherAlive;
355 
356         SfxExecFunc pFunc = rSlot.GetExecFnc();
357         rShell.CallExec( pFunc, rReq );
358 
359         // If 'this' is still alive
360         if ( bThisDispatcherAlive )
361             xImp->pInCallAliveFlag = pOldInCallAliveFlag;
362         else
363         {
364             if ( pOldInCallAliveFlag )
365             {
366                 // also protect nested stack frames
367                 *pOldInCallAliveFlag = false;
368             }
369 
370             // do nothing after this object is dead
371             return;
372         }
373     }
374 
375     if ( rReq.IsDone() )
376     {
377         SfxBindings *pBindings = GetBindings();
378 
379         // When AutoUpdate update immediately
380         if ( bAutoUpdate && pBindings )
381         {
382             pBindings->Invalidate(rSlot.GetSlotId());
383             pBindings->Update(rSlot.GetSlotId());
384         }
385     }
386 }
387 
Construct_Impl()388 void SfxDispatcher::Construct_Impl()
389 {
390     xImp.reset(new SfxDispatcher_Impl);
391     xImp->bFlushed = true;
392 
393     xImp->bFlushing = false;
394     xImp->bUpdated = false;
395     xImp->bLocked = false;
396     xImp->bActive = false;
397     xImp->bNoUI = false;
398     xImp->bReadOnly = false;
399     xImp->bQuiet = false;
400     xImp->pInCallAliveFlag = nullptr;
401     xImp->nFilterEnabling = SfxSlotFilterState::DISABLED;
402     xImp->nDisableFlags = SfxDisableFlags::NONE;
403 
404     xImp->bInvalidateOnUnlock = false;
405 
406     for (SfxObjectBars_Impl & rObjBar : xImp->aObjBars)
407         rObjBar.eId = ToolbarId::None;
408 
409     xImp->xPoster = new SfxHintPoster(std::bind(&SfxDispatcher::PostMsgHandler, this, std::placeholders::_1));
410 
411     xImp->aIdle.SetPriority(TaskPriority::HIGH_IDLE );
412     xImp->aIdle.SetInvokeHandler( LINK(this, SfxDispatcher, EventHdl_Impl ) );
413     xImp->aIdle.SetDebugName( "sfx::SfxDispatcher_Impl aIdle" );
414 }
415 
SfxDispatcher()416 SfxDispatcher::SfxDispatcher()
417 {
418     Construct_Impl();
419     xImp->pFrame = nullptr;
420 }
421 
422 /** The constructor of the SfxDispatcher class places a stack of empty
423     <SfxShell> pointers. It is not initially locked and is considered flushed.
424 */
SfxDispatcher(SfxViewFrame * pViewFrame)425 SfxDispatcher::SfxDispatcher(SfxViewFrame *pViewFrame)
426 {
427     Construct_Impl();
428     xImp->pFrame = pViewFrame;
429 }
430 
431 /** The destructor of the SfxDispatcher class should not be called when the
432     SfxDispatcher instance is active. It may, however, still be a <SfxShell>
433     pointer on the stack.
434 */
~SfxDispatcher()435 SfxDispatcher::~SfxDispatcher()
436 {
437     SAL_INFO("sfx.control", "Delete Dispatcher " << reinterpret_cast<sal_Int64>(this));
438     DBG_ASSERT( !xImp->bActive, "deleting active Dispatcher" );
439 
440     // So that no timer by Reschedule in PlugComm strikes the LeaveRegistrations
441     xImp->aIdle.Stop();
442     xImp->xPoster->SetEventHdl( std::function<void (std::unique_ptr<SfxRequest>)>() );
443 
444     // Notify the stack variables in Call_Impl
445     if ( xImp->pInCallAliveFlag )
446         *xImp->pInCallAliveFlag = false;
447 
448     // Get bindings and application
449     SfxApplication *pSfxApp = SfxGetpApp();
450     SfxBindings* pBindings = GetBindings();
451 
452     // When not flushed, revive the bindings
453     if (pBindings && !pSfxApp->IsDowning() && !xImp->bFlushed)
454         pBindings->DLEAVEREGISTRATIONS();
455 
456     // may unregister the bindings
457     while ( pBindings )
458     {
459         if ( pBindings->GetDispatcher_Impl() == this)
460             pBindings->SetDispatcher(nullptr);
461         pBindings = pBindings->GetSubBindings_Impl();
462     }
463 }
464 
465 /** With this method, one or more <SfxShell> are popped from the SfxDispatcher.
466     The SfxShell is marked for popping and a timer is set up. Only when the
467     timer has reached the end, the pop is actually performed
468     ( <SfxDispatcher::Flush()> ) and the <SfxBindings> is invalidated.
469     While the timer is running the opposing push and pop commands on one
470     SfxShell cancel each other out.
471 
472     @param rShell the stack to take the SfxShell instance.
473     @param nMode SfxDispatcherPopFlags::POP_UNTIL
474                             Also all 'rShell' of SfxShells are taken from the
475                             stack.
476 
477                  SfxDispatcherPopFlags::POP_DELETE
478                             All SfxShells actually taken from the stack
479                             will be deleted.
480 
481                  SfxDispatcherPopFlags::PUSH (InPlace use only)
482                             The Shell is pushed.
483 */
Pop(SfxShell & rShell,SfxDispatcherPopFlags nMode)484 void SfxDispatcher::Pop(SfxShell& rShell, SfxDispatcherPopFlags nMode)
485 {
486     DBG_ASSERT( rShell.GetInterface(),
487                 "pushing SfxShell without previous RegisterInterface()" );
488 
489     bool bDelete = bool(nMode & SfxDispatcherPopFlags::POP_DELETE);
490     bool bUntil = bool(nMode & SfxDispatcherPopFlags::POP_UNTIL);
491     bool bPush = bool(nMode & SfxDispatcherPopFlags::PUSH);
492 
493     SfxApplication *pSfxApp = SfxGetpApp();
494 
495     SAL_INFO(
496         "sfx.control",
497         "-SfxDispatcher(" << this << (bPush ? ")::Push(" : ")::Pop(")
498             << (rShell.GetInterface()
499                 ? rShell.GetInterface()->GetClassName() : SAL_STREAM(&rShell))
500             << (bDelete ? ") with delete" : ")")
501             << (bUntil ? " (up to)" : ""));
502 
503     // same shell as on top of the to-do stack?
504     if(!xImp->aToDoStack.empty() && xImp->aToDoStack.front().pCluster == &rShell)
505     {
506         // cancel inverse actions
507         if ( xImp->aToDoStack.front().bPush != bPush )
508             xImp->aToDoStack.pop_front();
509         else
510         {
511             DBG_ASSERT( bPush, "SfxInterface pushed more than once" );
512             DBG_ASSERT( !bPush, "SfxInterface popped more than once" );
513         }
514     }
515     else
516     {
517         // Remember Action
518         xImp->aToDoStack.push_front( SfxToDo_Impl(bPush, bDelete, bUntil, rShell) );
519         if (xImp->bFlushed)
520         {
521             SAL_INFO("sfx.control", "Unflushed dispatcher!");
522             xImp->bFlushed = false;
523             xImp->bUpdated = false;
524 
525             // Put bindings to sleep
526             SfxBindings* pBindings = GetBindings();
527             if ( pBindings )
528                 pBindings->DENTERREGISTRATIONS();
529         }
530     }
531 
532     if(!pSfxApp->IsDowning() && !xImp->aToDoStack.empty())
533     {
534         // No immediate update is requested
535         xImp->aIdle.Start();
536     }
537     else
538     {
539         // but to do nothing
540         xImp->aIdle.Stop();
541 
542         // Bindings may wake up again
543         if(xImp->aToDoStack.empty())
544         {
545             SfxBindings* pBindings = GetBindings();
546             if ( pBindings )
547                 pBindings->DLEAVEREGISTRATIONS();
548         }
549     }
550 }
551 
552 
553 /** This handler is called after <SfxDispatcher::Invalidate()> or after
554     changes on the stack (<SfxDispatcher::Push()> and <SfxDispatcher::Pop())
555 
556     It flushes the Stack, if it is dirty, thus it actually executes the
557     pending Push and Pop commands.
558 */
IMPL_LINK_NOARG(SfxDispatcher,EventHdl_Impl,Timer *,void)559 IMPL_LINK_NOARG( SfxDispatcher, EventHdl_Impl, Timer *, void )
560 {
561     Flush();
562     Update_Impl();
563     SfxBindings* pBindings = GetBindings();
564     if ( pBindings )
565         pBindings->StartUpdate_Impl();
566 }
567 
568 /** With this method it can be tested whether the <SfxShell> rShell is on the
569     stack, when it was flushed. This way the SfxDispatcher is not actually
570     flushed.
571 
572     This method is intended among other things to make assertions possible
573     without the side effect of having to flush the SfxDispathcer.
574 */
CheckVirtualStack(const SfxShell & rShell)575 bool SfxDispatcher::CheckVirtualStack(const SfxShell& rShell)
576 {
577     SFX_STACK(SfxDispatcher::CheckVirtualStack);
578 
579     SfxShellStack_Impl aStack( xImp->aStack );
580     for(std::deque<SfxToDo_Impl>::reverse_iterator i = xImp->aToDoStack.rbegin(); i != xImp->aToDoStack.rend(); ++i)
581     {
582         if(i->bPush)
583             aStack.push_back(i->pCluster);
584         else
585         {
586             SfxShell* pPopped(nullptr);
587             do
588             {
589                 DBG_ASSERT( !aStack.empty(), "popping from empty stack" );
590                 pPopped = aStack.back();
591                 aStack.pop_back();
592             }
593             while(i->bUntil && pPopped != i->pCluster);
594             DBG_ASSERT(pPopped == i->pCluster, "popping unpushed SfxInterface");
595         }
596     }
597 
598     bool bReturn = std::find(aStack.begin(), aStack.end(), &rShell) != aStack.end();
599     return bReturn;
600 }
601 
602 /** Determines the position of a given SfxShell in the stack of the dispatcher.
603     If possible this is flushed before.
604 
605     [Return value]
606 
607     sal_uInt16                  == USRT_MAX
608                                 The SfxShell is not on this SfxDispatcher.
609 
610                                 < USHRT_MAX
611                                 Position of the SfxShell on the Dispatcher
612                                 from the top count stating with 0.
613 */
GetShellLevel(const SfxShell & rShell)614 sal_uInt16 SfxDispatcher::GetShellLevel(const SfxShell& rShell)
615 {
616     SFX_STACK(SfxDispatcher::GetShellLevel);
617     Flush();
618 
619     for ( size_t n = 0; n < xImp->aStack.size(); ++n )
620         if ( *( xImp->aStack.rbegin() + n ) == &rShell )
621             return n;
622 
623     return USHRT_MAX;
624 }
625 
626 /** Returns a pointer to the <SfxShell> which is at the position nIdx
627     (from the top, last pushed is 0) on the stack.
628 
629     Thus the SfxDispatcher is not flushed.
630 
631     Is the stack not deep enough a NULL-Pointer is returned.
632 */
GetShell(sal_uInt16 nIdx) const633 SfxShell *SfxDispatcher::GetShell(sal_uInt16 nIdx) const
634 {
635     sal_uInt16 nShellCount = xImp->aStack.size();
636     if ( nIdx < nShellCount )
637         return *(xImp->aStack.rbegin() + nIdx);
638     return nullptr;
639 }
640 
641 /** This method returns a pointer to the <SfxBinding> Instance on which the
642     SfxDispatcher is currently bound. A SfxDispatcher is only bound to
643     the SfxBindings when it is <UI-aktiv>. If it is not UI-active,
644     a NULL-pointer is returned.
645 
646     The returned pointer is only valid in the immediate context of the method
647     call.
648 */
GetBindings() const649 SfxBindings* SfxDispatcher::GetBindings() const
650 {
651     if ( xImp->pFrame )
652         return &xImp->pFrame->GetBindings();
653     else
654         return nullptr;
655 }
656 
657 /** Returns a pointer to the <SfxViewFrame> instance, which belongs to
658     this SfxDispatcher. If it is about the application dispatcher,
659     a NULL-pointer is returned.
660 */
GetFrame() const661 SfxViewFrame* SfxDispatcher::GetFrame() const
662 {
663     return xImp->pFrame;
664 }
665 
666 /** This method controls the activation of a dispatcher.
667 
668     Since the application dispatcher is always active, either as a sub
669     dispatcher of the <SfxViewFrame> dispatcher or as itself, it is never
670     activated as a whole, instead only its individual <SfxShell>s at
671     <SfxDispatcher::Push(SfxShell&)>.
672 
673     When activating a SfxDispatcher all of the SfxShells located on its stack
674     are called with the handler <SfxShell::Activate(bool)>, starting with
675     the lowest.
676 */
DoActivate_Impl(bool bMDI)677 void SfxDispatcher::DoActivate_Impl(bool bMDI)
678 {
679     SFX_STACK(SfxDispatcher::DoActivate);
680     if ( bMDI )
681     {
682         SAL_INFO("sfx.control", "Activate Dispatcher " << reinterpret_cast<sal_Int64>(this));
683         DBG_ASSERT( !xImp->bActive, "Activation error" );
684 
685         xImp->bActive = true;
686         xImp->bUpdated = false;
687         SfxBindings* pBindings = GetBindings();
688         if ( pBindings )
689         {
690             pBindings->SetDispatcher(this);
691             pBindings->SetActiveFrame( xImp->pFrame->GetFrame().GetFrameInterface() );
692         }
693     }
694     else
695     {
696         SAL_INFO("sfx.control", "Non-MDI-Activate Dispatcher " << reinterpret_cast<sal_Int64>(this));
697     }
698 
699     if ( IsAppDispatcher() )
700         return;
701 
702     for ( int i = int(xImp->aStack.size()) - 1; i >= 0; --i )
703         (*(xImp->aStack.rbegin() + i ))->DoActivate_Impl(xImp->pFrame, bMDI);
704 
705     if ( bMDI && xImp->pFrame )
706     {
707         xImp->pFrame->GetFrame().GetWorkWindow_Impl()->HidePopups_Impl( false, 1 );
708     }
709 
710     if(!xImp->aToDoStack.empty())
711     {
712         // No immediate update is requested
713         xImp->aIdle.Start();
714     }
715 }
716 
717 /** This method controls the deactivation of a dispatcher.
718 
719     Since the application dispatcher is always active, either as a sub
720     dispatcher of the <SfxViewFrame> dispatcher or as itself, it is never
721     deactivated as a whole, instead only its individual <SfxShell>s at
722     <SfxDispatcher::Pop(SfxShell&)>.
723 
724     When deactivating a SfxDispatcher all of the SfxShells located on its stack
725     are called with the handler <SfxShell::Deactivate(bool)>, starting with
726     the lowest.
727 */
DoDeactivate_Impl(bool bMDI,SfxViewFrame const * pNew)728 void SfxDispatcher::DoDeactivate_Impl(bool bMDI, SfxViewFrame const * pNew)
729 {
730     SFX_STACK(SfxDispatcher::DoDeactivate);
731 
732     SfxApplication *pSfxApp = SfxGetpApp();
733 
734     if ( bMDI )
735     {
736         SAL_INFO("sfx.control", "Deactivate Dispatcher " << this);
737         DBG_ASSERT( xImp->bActive, "Deactivate error" );
738         xImp->bActive = false;
739 
740         if ( xImp->pFrame && !(xImp->pFrame->GetObjectShell()->IsInPlaceActive() ) )
741         {
742             SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
743             if ( pWorkWin )
744             {
745                 for (size_t n=0; n<xImp->aChildWins.size();)
746                 {
747                     SfxChildWindow *pWin = pWorkWin->GetChildWindow_Impl( static_cast<sal_uInt16>( xImp->aChildWins[n] & 0xFFFF ) );
748                     if (!pWin || pWin->GetAlignment() == SfxChildAlignment::NOALIGNMENT)
749                         xImp->aChildWins.erase(xImp->aChildWins.begin()+n);
750                     else
751                         n++;
752                 }
753             }
754         }
755     }
756     else {
757         SAL_INFO("sfx.control", "Non-MDI-DeActivate Dispatcher " << this);
758     }
759 
760     if ( IsAppDispatcher() && !pSfxApp->IsDowning() )
761         return;
762 
763     for ( size_t i = 0; i < xImp->aStack.size(); ++i )
764         (*(xImp->aStack.rbegin() + i))->DoDeactivate_Impl(xImp->pFrame, bMDI);
765 
766     bool bHidePopups = bMDI && xImp->pFrame;
767     if ( pNew && xImp->pFrame )
768     {
769         css::uno::Reference< css::frame::XFrame > xOldFrame =
770             pNew->GetFrame().GetFrameInterface()->getCreator();
771 
772         css::uno::Reference< css::frame::XFrame > xMyFrame =
773             GetFrame()->GetFrame().GetFrameInterface();
774 
775         if ( xOldFrame == xMyFrame )
776             bHidePopups = false;
777     }
778 
779     if ( bHidePopups )
780     {
781         xImp->pFrame->GetFrame().GetWorkWindow_Impl()->HidePopups_Impl( true, 1 );
782     }
783 
784     Flush();
785 }
786 
787 /** This method searches in SfxDispatcher after <SfxShell> , from the Slot Id
788     nSlot currently being handled. For this, the dispatcher is first flushed.
789 
790     @param nSlot the searchable Slot-Id
791     @param ppShell the SfxShell, which are currently handled the nSlot
792     @param ppSlot the SfxSlot, which are currently handled the nSlot
793 
794     @return int      sal_True
795                      The SfxShell was found, ppShell and ppSlot are valid.
796 
797                      sal_False
798                      The SfxShell was not found, ppShell and ppSlot are invalid.
799 */
GetShellAndSlot_Impl(sal_uInt16 nSlot,SfxShell ** ppShell,const SfxSlot ** ppSlot,bool bOwnShellsOnly,bool bRealSlot)800 bool SfxDispatcher::GetShellAndSlot_Impl(sal_uInt16 nSlot, SfxShell** ppShell,
801         const SfxSlot** ppSlot, bool bOwnShellsOnly, bool bRealSlot)
802 {
803     SFX_STACK(SfxDispatcher::GetShellAndSlot_Impl);
804 
805     Flush();
806     SfxSlotServer aSvr;
807     if ( FindServer_(nSlot, aSvr) )
808     {
809         if ( bOwnShellsOnly && aSvr.GetShellLevel() >= xImp->aStack.size() )
810             return false;
811 
812         *ppShell = GetShell(aSvr.GetShellLevel());
813         *ppSlot = aSvr.GetSlot();
814         if ( nullptr == (*ppSlot)->GetExecFnc() && bRealSlot )
815             *ppSlot = (*ppShell)->GetInterface()->GetRealSlot(*ppSlot);
816         // Check only real slots as enum slots don't have an execute function!
817         return !bRealSlot || !((nullptr == *ppSlot) || (nullptr == (*ppSlot)->GetExecFnc()) );
818     }
819 
820     return false;
821 }
822 
823 /** This method performs a request for a cached <Slot-Server>.
824 
825     @param rShell to the calling <SfxShell>
826     @param rSlot to the calling <SfxSlot>
827     @param rReq function to be performed (Id and optional parameters)
828     @param eCallMode Synchronously, asynchronously or as shown in the slot
829 */
Execute_(SfxShell & rShell,const SfxSlot & rSlot,SfxRequest & rReq,SfxCallMode eCallMode)830 void SfxDispatcher::Execute_(SfxShell& rShell, const SfxSlot& rSlot,
831         SfxRequest& rReq, SfxCallMode eCallMode)
832 {
833     SFX_STACK(SfxDispatcher::Execute_);
834     DBG_ASSERT( !xImp->bFlushing, "recursive call to dispatcher" );
835     DBG_ASSERT( xImp->aToDoStack.empty(), "unprepared InPlace _Execute" );
836 
837     if ( IsLocked() )
838         return;
839 
840     if ( bool(eCallMode & SfxCallMode::ASYNCHRON) ||
841          ( (eCallMode & SfxCallMode::SYNCHRON) == SfxCallMode::SLOT &&
842            rSlot.IsMode(SfxSlotMode::ASYNCHRON) ) )
843     {
844         sal_uInt16 nShellCount = xImp->aStack.size();
845         for ( sal_uInt16 n=0; n<nShellCount; n++ )
846         {
847             if ( &rShell == *(xImp->aStack.rbegin() + n) )
848             {
849                 if ( bool(eCallMode & SfxCallMode::RECORD) )
850                     rReq.AllowRecording( true );
851                 xImp->xPoster->Post(std::make_unique<SfxRequest>(rReq));
852                 return;
853             }
854         }
855     }
856     else
857         Call_Impl( rShell, rSlot, rReq, SfxCallMode::RECORD==(eCallMode&SfxCallMode::RECORD) );
858 }
859 
860 /** Helper function to put from rItem below the Which-ID in the pool of the
861     Item Sets rSet.
862 */
MappedPut_Impl(SfxAllItemSet & rSet,const SfxPoolItem & rItem)863 static void MappedPut_Impl(SfxAllItemSet &rSet, const SfxPoolItem &rItem)
864 {
865     // Put with mapped Which-Id if possible
866     const SfxItemPool *pPool = rSet.GetPool();
867     sal_uInt16 nWhich = rItem.Which();
868     if ( SfxItemPool::IsSlot(nWhich) )
869         nWhich = pPool->GetWhich(nWhich);
870     rSet.Put( rItem, nWhich );
871 }
872 
GetSlot(const OUString & rCommand)873 const SfxSlot* SfxDispatcher::GetSlot( const OUString& rCommand )
874 {
875     // Count the number of Shells on the linked Dispatcher
876     Flush();
877     sal_uInt16 nTotCount = xImp->aStack.size();
878 
879     for ( sal_uInt16 i = 0; i < nTotCount; ++i )
880     {
881         SfxShell *pObjShell = GetShell(i);
882         SfxInterface *pIFace = pObjShell->GetInterface();
883         const SfxSlot *pSlot = pIFace->GetSlot( rCommand );
884         if ( pSlot )
885             return pSlot;
886     }
887 
888     return nullptr;
889 }
890 
Execute(sal_uInt16 nSlot,SfxCallMode nCall,SfxItemSet const * pArgs,SfxItemSet const * pInternalArgs,sal_uInt16 nModi)891 const SfxPoolItem* SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode nCall,
892         SfxItemSet const * pArgs, SfxItemSet const * pInternalArgs, sal_uInt16 nModi)
893 {
894     if ( IsLocked() )
895         return nullptr;
896 
897     SfxShell *pShell = nullptr;
898     const SfxSlot *pSlot = nullptr;
899     if ( GetShellAndSlot_Impl( nSlot,  &pShell, &pSlot, false, true ) )
900     {
901         SfxAllItemSet aSet( pShell->GetPool() );
902         if ( pArgs )
903         {
904             SfxItemIter aIter(*pArgs);
905             for ( const SfxPoolItem *pArg = aIter.GetCurItem();
906                 pArg;
907                 pArg = aIter.NextItem() )
908                 MappedPut_Impl( aSet, *pArg );
909         }
910         SfxRequest aReq(nSlot, nCall, aSet);
911         if (pInternalArgs)
912             aReq.SetInternalArgs_Impl( *pInternalArgs );
913         aReq.SetModifier( nModi );
914 
915         Execute_( *pShell, *pSlot, aReq, nCall );
916         return aReq.GetReturnValue();
917     }
918     return nullptr;
919 }
920 
921 /** Method to execute a <SfxSlot>s over the Slot-Id.
922 
923     @param nSlot the Id of the executing function
924     @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
925     @param pArgs Zero terminated C-Array of Parameters
926     @param pInternalArgs Zero terminated C-Array of Parameters
927 
928     @return const SfxPoolItem* Pointer to the SfxPoolItem valid to the next run
929                             though the Message-Loop, which contains the return
930                             value.
931 
932                             Or a NULL-Pointer, when the function was not
933                             executed (for example canceled by the user).
934 */
Execute(sal_uInt16 nSlot,SfxCallMode eCall,const SfxPoolItem ** pArgs,sal_uInt16 nModi,const SfxPoolItem ** pInternalArgs)935 const SfxPoolItem* SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode eCall,
936         const SfxPoolItem **pArgs, sal_uInt16 nModi, const SfxPoolItem **pInternalArgs)
937 {
938     if ( IsLocked() )
939         return nullptr;
940 
941     SfxShell *pShell = nullptr;
942     const SfxSlot *pSlot = nullptr;
943     if ( GetShellAndSlot_Impl( nSlot,  &pShell, &pSlot, false, true ) )
944     {
945         std::unique_ptr<SfxRequest> pReq;
946         if ( pArgs && *pArgs )
947         {
948             SfxAllItemSet aSet( pShell->GetPool() );
949             for ( const SfxPoolItem **pArg = pArgs; *pArg; ++pArg )
950                 MappedPut_Impl( aSet, **pArg );
951             pReq.reset(new SfxRequest( nSlot, eCall, aSet ));
952         }
953         else
954             pReq.reset(new SfxRequest( nSlot, eCall, pShell->GetPool() ));
955         pReq->SetModifier( nModi );
956         if( pInternalArgs && *pInternalArgs)
957         {
958             SfxAllItemSet aSet( SfxGetpApp()->GetPool() );
959             for ( const SfxPoolItem **pArg = pInternalArgs; *pArg; ++pArg )
960                 aSet.Put( **pArg );
961             pReq->SetInternalArgs_Impl( aSet );
962         }
963         Execute_( *pShell, *pSlot, *pReq, eCall );
964         const SfxPoolItem* pRet = pReq->GetReturnValue();
965         return pRet;
966     }
967     return nullptr;
968 }
969 
970 /** Method to execute a <SfxSlot>s over the Slot-Id.
971 
972     @param nSlot the Id of the executing function
973     @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
974     @param rArgs <SfxItemSet> with the parameters
975 
976     @return const SfxPoolItem* Pointer to the SfxPoolItem valid to the next run
977                             though the Message-Loop, which contains the return
978                             value.
979 
980                             Or a NULL-Pointer, when the function was not
981                             executed (for example canceled by the user).
982 */
Execute(sal_uInt16 nSlot,SfxCallMode eCall,const SfxItemSet & rArgs)983 const SfxPoolItem* SfxDispatcher::Execute(sal_uInt16 nSlot, SfxCallMode eCall,
984         const SfxItemSet &rArgs)
985 {
986     if ( IsLocked() )
987         return nullptr;
988 
989     SfxShell *pShell = nullptr;
990     const SfxSlot *pSlot = nullptr;
991     if ( GetShellAndSlot_Impl( nSlot,  &pShell, &pSlot, false, true ) )
992     {
993         SfxAllItemSet aSet( pShell->GetPool() );
994         SfxItemIter aIter(rArgs);
995         for ( const SfxPoolItem *pArg = aIter.GetCurItem();
996               pArg;
997               pArg = aIter.NextItem() )
998             MappedPut_Impl( aSet, *pArg );
999         SfxRequest aReq( nSlot, eCall, aSet );
1000         aReq.SetModifier( 0 );
1001         Execute_( *pShell, *pSlot, aReq, eCall );
1002         return aReq.GetReturnValue();
1003     }
1004     return nullptr;
1005 }
1006 
1007 /** Method to execute a <SfxSlot>s over the Slot-Id.
1008 
1009     [Note]
1010 
1011     The parameters are copied, can therefore be passed on as the address
1012     of stack objects.
1013 
1014     @param nSlot the Id of the executing function
1015     @param eCall SfxCallMode::SYNCRHON, ..._ASYNCHRON or ..._SLOT
1016     @param args  list of SfxPoolItem arguments
1017 
1018     @return                 Pointer to the SfxPoolItem valid to the next run
1019                             though the Message-Loop, which contains the return
1020                             value.
1021 
1022                             Or a NULL-Pointer, when the function was not
1023                             executed (for example canceled by the user).
1024 
1025     [Example]
1026 
1027     pDispatcher->Execute( SID_OPENDOCUMENT, SfxCallMode::SYNCHRON,
1028         {   &SfxStringItem( SID_FILE_NAME, "\\tmp\\temp.sdd" ),
1029             &SfxStringItem( SID_FILTER_NAME, "StarDraw Presentation" ),
1030             &SfxBoolItem( SID_DOC_READONLY, sal_False ),
1031         });
1032 */
ExecuteList(sal_uInt16 nSlot,SfxCallMode eCall,std::initializer_list<SfxPoolItem const * > args,std::initializer_list<SfxPoolItem const * > internalargs)1033 const SfxPoolItem* SfxDispatcher::ExecuteList(sal_uInt16 nSlot, SfxCallMode eCall,
1034         std::initializer_list<SfxPoolItem const*> args,
1035         std::initializer_list<SfxPoolItem const*> internalargs)
1036 {
1037     if ( IsLocked() )
1038         return nullptr;
1039 
1040     SfxShell *pShell = nullptr;
1041     const SfxSlot *pSlot = nullptr;
1042     if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
1043     {
1044        SfxAllItemSet aSet( pShell->GetPool() );
1045 
1046        for (const SfxPoolItem *pArg : args)
1047        {
1048            assert(pArg);
1049            MappedPut_Impl( aSet, *pArg );
1050        }
1051 
1052        SfxRequest aReq(nSlot, eCall, aSet);
1053 
1054        if (internalargs.begin() != internalargs.end())
1055        {
1056            SfxAllItemSet aInternalSet(SfxGetpApp()->GetPool());
1057            for (const SfxPoolItem *pArg : internalargs)
1058            {
1059                assert(pArg);
1060                aInternalSet.Put(*pArg);
1061            }
1062            aReq.SetInternalArgs_Impl(aInternalSet);
1063        }
1064 
1065        Execute_( *pShell, *pSlot, aReq, eCall );
1066        return aReq.GetReturnValue();
1067     }
1068     return nullptr;
1069 }
1070 
1071 /** Helper method to receive the asynchronously executed <SfxRequest>s.
1072 */
PostMsgHandler(std::unique_ptr<SfxRequest> pReq)1073 void SfxDispatcher::PostMsgHandler(std::unique_ptr<SfxRequest> pReq)
1074 {
1075     DBG_ASSERT( !xImp->bFlushing, "recursive call to dispatcher" );
1076     SFX_STACK(SfxDispatcher::PostMsgHandler);
1077 
1078     // Has also the Pool not yet died?
1079     if ( pReq->IsCancelled() )
1080         return;
1081 
1082     if ( !IsLocked() )
1083     {
1084         Flush();
1085         SfxSlotServer aSvr;
1086         if ( FindServer_(pReq->GetSlot(), aSvr ) ) // HACK(x), whatever that was supposed to mean
1087         {
1088             const SfxSlot *pSlot = aSvr.GetSlot();
1089             SfxShell *pSh = GetShell(aSvr.GetShellLevel());
1090 
1091             // When the pSlot is a "Pseudoslot" for macros or Verbs, it can
1092             // be destroyed in the Call_Impl, thus do not use it anymore!
1093             pReq->SetSynchronCall( false );
1094             Call_Impl( *pSh, *pSlot, *pReq, pReq->AllowsRecording() ); //! why bRecord?
1095         }
1096     }
1097     else
1098     {
1099         if ( xImp->bLocked )
1100             xImp->aReqArr.emplace_back(std::move(pReq));
1101         else
1102             xImp->xPoster->Post(std::move(pReq));
1103     }
1104 }
1105 
SetMenu_Impl()1106 void SfxDispatcher::SetMenu_Impl()
1107 {
1108 #if HAVE_FEATURE_DESKTOP
1109     if ( !xImp->pFrame )
1110         return;
1111 
1112     SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame();
1113     if ( !pTop || pTop->GetBindings().GetDispatcher() != this )
1114         return;
1115 
1116     SfxFrame& rFrame = pTop->GetFrame();
1117     if ( !rFrame.IsMenuBarOn_Impl() )
1118         return;
1119 
1120     css::uno::Reference < css::beans::XPropertySet > xPropSet( rFrame.GetFrameInterface(), css::uno::UNO_QUERY );
1121     if ( xPropSet.is() )
1122     {
1123         css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
1124         css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
1125         aValue >>= xLayoutManager;
1126         if ( xLayoutManager.is() )
1127         {
1128             OUString aMenuBarURL( "private:resource/menubar/menubar" );
1129             if ( !xLayoutManager->isElementVisible( aMenuBarURL ) )
1130                 xLayoutManager->createElement( aMenuBarURL );
1131         }
1132     }
1133 #endif
1134 }
1135 
Update_Impl(bool bForce)1136 void SfxDispatcher::Update_Impl( bool bForce )
1137 {
1138     SFX_STACK(SfxDispatcher::Update_Impl);
1139 
1140     Flush();
1141 
1142     if ( !xImp->pFrame )
1143         return;
1144 
1145     bool bUpdate = bForce;
1146     if ( xImp->pFrame )
1147     {
1148         SfxWorkWindow *pWork = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
1149         SfxDispatcher *pAct = pWork->GetBindings().GetDispatcher_Impl();
1150         if (pAct == this)
1151         {
1152             if ( !bUpdate )
1153                 bUpdate = !xImp->bUpdated;
1154             xImp->bUpdated = true;
1155         }
1156     }
1157 
1158     if ( !bUpdate || xImp->pFrame->GetFrame().IsClosing_Impl() )
1159         return;
1160 
1161     SfxViewFrame* pTop = xImp->pFrame ? xImp->pFrame->GetTopViewFrame() : nullptr;
1162     bool bUIActive = pTop && pTop->GetBindings().GetDispatcher() == this;
1163 
1164     if ( !bUIActive && pTop && GetBindings() == &pTop->GetBindings() )
1165         // keep own tools internally for collecting
1166         GetBindings()->GetDispatcher()->xImp->bUpdated = false;
1167 
1168     css::uno::Reference< css::frame::XFrame > xFrame;
1169     SfxBindings* pBindings = GetBindings();
1170     if (pBindings)
1171     {
1172         pBindings->DENTERREGISTRATIONS();
1173         xFrame = pBindings->GetActiveFrame();
1174     }
1175     css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
1176     css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
1177     if ( xPropSet.is() )
1178     {
1179         try
1180         {
1181             css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
1182             aValue >>= xLayoutManager;
1183         }
1184         catch (const css::uno::Exception&)
1185         {
1186         }
1187     }
1188 
1189     if ( xLayoutManager.is() )
1190         xLayoutManager->lock();
1191 
1192     bool bIsIPActive = xImp->pFrame && xImp->pFrame->GetObjectShell()->IsInPlaceActive();
1193     SfxInPlaceClient *pClient = xImp->pFrame ? xImp->pFrame->GetViewShell()->GetUIActiveClient() : nullptr;
1194     if ( bUIActive && /* !bIsIPActive && */ ( !pClient || !pClient->IsObjectUIActive() ) )
1195         SetMenu_Impl();
1196 
1197     SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
1198     pWorkWin->ResetStatusBar_Impl();
1199 
1200     {
1201         SfxWorkWindow *pWork = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
1202         SfxDispatcher *pAct = pWork->GetBindings().GetDispatcher_Impl();
1203         if (pAct == this)
1204         {
1205             pWork->ResetObjectBars_Impl();
1206             pWork->ResetChildWindows_Impl();
1207         }
1208     }
1209 
1210     bool bIsActive = false;
1211     SfxDispatcher *pActDispat = pWorkWin->GetBindings().GetDispatcher_Impl();
1212     if ( !bIsActive && this == pActDispat )
1213         bIsActive = true;
1214 
1215     Update_Impl_( bUIActive, !bIsIPActive, bIsIPActive, pWorkWin );
1216     if (bUIActive || bIsActive)
1217         pWorkWin->UpdateObjectBars_Impl();
1218 
1219     if ( pBindings )
1220         pBindings->DLEAVEREGISTRATIONS();
1221 
1222     if ( xLayoutManager.is() )
1223         xLayoutManager->unlock();
1224 
1225     if ( SfxViewShell::Current() && SfxViewShell::Current()->GetDispatcher() )
1226     {
1227         const SfxPoolItem *pItem;
1228         SfxViewShell::Current()->GetDispatcher()->QueryState(SID_NOTEBOOKBAR, pItem);
1229     }
1230 }
1231 
Update_Impl_(bool bUIActive,bool bIsMDIApp,bool bIsIPOwner,SfxWorkWindow * pTaskWin)1232 void SfxDispatcher::Update_Impl_( bool bUIActive, bool bIsMDIApp, bool bIsIPOwner, SfxWorkWindow *pTaskWin )
1233 {
1234     SfxWorkWindow *pWorkWin = xImp->pFrame->GetFrame().GetWorkWindow_Impl();
1235     bool bIsActive = false;
1236     SfxDispatcher *pActDispat = pWorkWin->GetBindings().GetDispatcher_Impl();
1237     if ( pActDispat && !bIsActive )
1238     {
1239         if ( this == pActDispat )
1240             bIsActive = true;
1241     }
1242 
1243     for (SfxObjectBars_Impl & rObjBar : xImp->aObjBars)
1244         rObjBar.eId = ToolbarId::None;
1245     xImp->aChildWins.clear();
1246 
1247     // bQuiet: own shells aren't considered for UI and SlotServer
1248     // bNoUI: own Shells aren't considered forms UI
1249     if ( xImp->bQuiet || xImp->bNoUI || (xImp->pFrame && xImp->pFrame->GetObjectShell()->IsPreview()) )
1250         return;
1251 
1252     StatusBarId eStatBarId = StatusBarId::None;
1253 
1254     SfxSlotPool* pSlotPool = &SfxSlotPool::GetSlotPool( GetFrame() );
1255     sal_uInt16 nTotCount = xImp->aStack.size();
1256     for ( sal_uInt16 nShell = nTotCount; nShell > 0; --nShell )
1257     {
1258         SfxShell *pShell = GetShell( nShell-1 );
1259         SfxInterface *pIFace = pShell->GetInterface();
1260 
1261         // don't consider shells if "Hidden" or "Quiet"
1262         bool bReadOnlyShell = IsReadOnlyShell_Impl( nShell-1 );
1263         sal_uInt16 nNo;
1264         for ( nNo = 0; pIFace && nNo<pIFace->GetObjectBarCount(); ++nNo )
1265         {
1266             sal_uInt16 nPos = pIFace->GetObjectBarPos(nNo);
1267             SfxVisibilityFlags nFlags = pIFace->GetObjectBarFlags(nNo);
1268             if ( bReadOnlyShell && !( nFlags & SfxVisibilityFlags::ReadonlyDoc ) )
1269                 continue;
1270 
1271             // check whether toolbar needs activation of a special feature
1272             SfxShellFeature nFeature = pIFace->GetObjectBarFeature(nNo);
1273             if ((nFeature != SfxShellFeature::NONE) && !pShell->HasUIFeature(nFeature))
1274                 continue;
1275 
1276             // check for toolboxes that are exclusively for a viewer
1277             if ( xImp->pFrame)
1278             {
1279                 bool bViewerTbx( nFlags & SfxVisibilityFlags::Viewer );
1280                 SfxObjectShell* pSh = xImp->pFrame->GetObjectShell();
1281                 const SfxBoolItem* pItem = SfxItemSet::GetItem<SfxBoolItem>(pSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false);
1282                 bool bIsViewer = pItem && pItem->GetValue();
1283                 if ( bIsViewer != bViewerTbx )
1284                     continue;
1285             }
1286 
1287             // always register toolbars, allows to switch them on
1288             bool bVisible = pIFace->IsObjectBarVisible(nNo);
1289             if ( !bVisible )
1290                 nFlags = SfxVisibilityFlags::Invisible;
1291 
1292             SfxObjectBars_Impl& rBar = xImp->aObjBars[nPos];
1293             rBar.nPos = nPos;
1294             rBar.nFlags = nFlags;
1295             rBar.eId = pIFace->GetObjectBarId(nNo);
1296 
1297             if ( bUIActive || bIsActive )
1298             {
1299                 pWorkWin->SetObjectBar_Impl(nPos, nFlags, rBar.eId);
1300             }
1301 
1302             if ( !bVisible )
1303                 rBar.eId = ToolbarId::None;
1304         }
1305 
1306         for ( nNo=0; pIFace && nNo<pIFace->GetChildWindowCount(); nNo++ )
1307         {
1308             sal_uInt32 nId = pIFace->GetChildWindowId(nNo);
1309             const SfxSlot *pSlot = pSlotPool->GetSlot( static_cast<sal_uInt16>(nId) );
1310             SAL_WARN_IF( !pSlot, "sfx.control", "Childwindow slot missing: " << nId );
1311             if ( bReadOnlyShell )
1312             {
1313                 // only show ChildWindows if their slot is allowed for readonly documents
1314                 if ( pSlot && !pSlot->IsMode( SfxSlotMode::READONLYDOC ) )
1315                     continue;
1316             }
1317 
1318             SfxShellFeature nFeature = pIFace->GetChildWindowFeature(nNo);
1319             if ((nFeature != SfxShellFeature::NONE) && !pShell->HasUIFeature(nFeature))
1320                 continue;
1321 
1322             // slot decides whether a ChildWindow is shown when document is OLE server or OLE client
1323             SfxVisibilityFlags nMode = SfxVisibilityFlags::Standard;
1324             if( pSlot )
1325             {
1326                 if ( pSlot->IsMode(SfxSlotMode::CONTAINER) )
1327                 {
1328                     if ( pWorkWin->IsVisible_Impl( SfxVisibilityFlags::Client ) )
1329                         nMode |= SfxVisibilityFlags::Client;
1330                 }
1331                 else
1332                 {
1333                     if ( pWorkWin->IsVisible_Impl( SfxVisibilityFlags::Server ) )
1334                         nMode |= SfxVisibilityFlags::Server;
1335                 }
1336             }
1337 
1338             if ( bUIActive || bIsActive )
1339                 pWorkWin->SetChildWindowVisible_Impl( nId, true, nMode );
1340             if ( bUIActive || bIsActive || !pWorkWin->IsFloating( static_cast<sal_uInt16>( nId & 0xFFFF ) ) )
1341                 xImp->aChildWins.push_back( nId );
1342         }
1343 
1344         if ( bIsMDIApp || bIsIPOwner )
1345         {
1346             StatusBarId eId = pIFace ? pIFace->GetStatusBarId() : StatusBarId::None;
1347             if (eId != StatusBarId::None)
1348                 eStatBarId = eId;
1349         }
1350     }
1351 
1352     for ( sal_uInt16 nPos=0; nPos<SFX_OBJECTBAR_MAX; nPos++ )
1353     {
1354         SfxObjectBars_Impl& rFixed = xImp->aFixedObjBars[nPos];
1355         if (rFixed.eId != ToolbarId::None)
1356         {
1357             SfxObjectBars_Impl& rBar = xImp->aObjBars[nPos];
1358             rBar = rFixed;
1359             pWorkWin->SetObjectBar_Impl(rFixed.nPos, rFixed.nFlags,
1360                 rFixed.eId);
1361         }
1362     }
1363 
1364     if ( !pTaskWin || ( !bIsMDIApp && !bIsIPOwner ) )
1365         return;
1366 
1367     bool bIsTaskActive = false;
1368 
1369     SfxDispatcher *pActDispatcher = pTaskWin->GetBindings().GetDispatcher_Impl();
1370     if ( pActDispatcher && !bIsTaskActive )
1371     {
1372         if ( this == pActDispatcher )
1373             bIsTaskActive = true;
1374     }
1375 
1376     if (bIsTaskActive && eStatBarId != StatusBarId::None && xImp->pFrame)
1377     {
1378         // internal frames also may control statusbar
1379         xImp->pFrame->GetFrame().GetWorkWindow_Impl()->SetStatusBar_Impl(eStatBarId);
1380     }
1381 }
1382 
1383 /** Helper method to execute the outstanding push and pop commands.
1384 */
FlushImpl()1385 void SfxDispatcher::FlushImpl()
1386 {
1387     SFX_STACK(SfxDispatcher::FlushImpl);
1388 
1389     SAL_INFO("sfx.control", "Flushing dispatcher!");
1390 
1391     xImp->aIdle.Stop();
1392 
1393     xImp->bFlushing = !xImp->bFlushing;
1394     if ( !xImp->bFlushing )
1395     {
1396         xImp->bFlushing = true;
1397         return;
1398     }
1399 
1400     SfxApplication *pSfxApp = SfxGetpApp();
1401 
1402     // Re-build the true stack in the first round
1403     std::deque<SfxToDo_Impl> aToDoCopy;
1404     bool bModify = false;
1405     for(std::deque<SfxToDo_Impl>::reverse_iterator i = xImp->aToDoStack.rbegin(); i != xImp->aToDoStack.rend(); ++i)
1406     {
1407         bModify = true;
1408 
1409         if(i->bPush)
1410         {
1411             // Actually push
1412             DBG_ASSERT( std::find(xImp->aStack.begin(), xImp->aStack.end(), i->pCluster) == xImp->aStack.end(),
1413                        "pushed SfxShell already on stack" );
1414             xImp->aStack.push_back(i->pCluster);
1415             i->pCluster->SetDisableFlags(xImp->nDisableFlags);
1416 
1417             // Mark the moved shell
1418             aToDoCopy.push_front(*i);
1419         }
1420         else
1421         {
1422             // Actually pop
1423             SfxShell* pPopped = nullptr;
1424             bool bFound = false;
1425             do
1426             {
1427                 DBG_ASSERT( !xImp->aStack.empty(), "popping from empty stack" );
1428                 pPopped = xImp->aStack.back();
1429                 xImp->aStack.pop_back();
1430                 pPopped->SetDisableFlags( SfxDisableFlags::NONE );
1431                 bFound = (pPopped == i->pCluster);
1432 
1433                 // Mark the moved Shell
1434                 aToDoCopy.push_front(SfxToDo_Impl(false, i->bDelete, false, *pPopped));
1435             }
1436             while(i->bUntil && !bFound);
1437             DBG_ASSERT( bFound, "wrong SfxShell popped" );
1438         }
1439     }
1440     xImp->aToDoStack.clear();
1441 
1442     // Invalidate bindings, if possible
1443     if ( !pSfxApp->IsDowning() )
1444     {
1445         InvalidateBindings_Impl( bModify );
1446     }
1447 
1448     xImp->bFlushing = false;
1449     xImp->bUpdated = false; // not only when bModify, if Doc/Template-Config
1450     xImp->bFlushed = true;
1451     SAL_INFO("sfx.control", "Successfully flushed dispatcher!");
1452 
1453     //fdo#70703 FlushImpl may call back into itself so use aToDoCopyStack to talk
1454     //to outer levels of ourself. If DoActivate_Impl/DoDeactivate_Impl deletes
1455     //an entry, then they will walk back up aToDoCopyStack and set outer
1456     //levels's entries to bDeleted
1457     xImp->aToDoCopyStack.push_back(aToDoCopy);
1458     std::deque<SfxToDo_Impl>& rToDoCopy = xImp->aToDoCopyStack.back();
1459     // Activate the Shells and possible delete them in the 2nd round
1460     for(std::deque<SfxToDo_Impl>::reverse_iterator i = rToDoCopy.rbegin(); i != rToDoCopy.rend(); ++i)
1461     {
1462         if (i->bDeleted)
1463             continue;
1464         if (!xImp->bActive)
1465             continue;
1466         if (i->bPush)
1467             i->pCluster->DoActivate_Impl(xImp->pFrame, true);
1468         else
1469             i->pCluster->DoDeactivate_Impl(xImp->pFrame, true);
1470     }
1471 
1472     aToDoCopy = xImp->aToDoCopyStack.back();
1473     xImp->aToDoCopyStack.pop_back();
1474 
1475     for(std::deque<SfxToDo_Impl>::reverse_iterator i = aToDoCopy.rbegin(); i != aToDoCopy.rend(); ++i)
1476     {
1477         if (i->bDelete && !i->bDeleted)
1478         {
1479             if (!xImp->aToDoCopyStack.empty())
1480             {
1481                 //fdo#70703 if there is an outer FlushImpl then inform it that
1482                 //we have deleted this cluster
1483                 for (auto & elem : xImp->aToDoCopyStack)
1484                 {
1485                     for (auto & subelem : elem)
1486                     {
1487                         if (subelem.pCluster == i->pCluster)
1488                             subelem.bDeleted = true;
1489                     }
1490                 }
1491             }
1492             delete i->pCluster;
1493         }
1494     }
1495     bool bAwakeBindings = !aToDoCopy.empty();
1496     if( bAwakeBindings )
1497         aToDoCopy.clear();
1498 
1499     // If more changes have occurred on the stack when
1500     // Activate/Deactivate/Delete:
1501     if (!xImp->bFlushed)
1502         // If Push/Pop has been called by someone, then also EnterReg was called!
1503         FlushImpl();
1504 
1505     if( bAwakeBindings && GetBindings() )
1506         GetBindings()->DLEAVEREGISTRATIONS();
1507 
1508     for (SfxObjectBars_Impl & rFixedObjBar : xImp->aFixedObjBars)
1509         rFixedObjBar.eId = ToolbarId::None;
1510 
1511     SAL_INFO("sfx.control", "SfxDispatcher(" << this << ")::Flush() done");
1512 }
1513 
1514 /** With this method a filter set, the target slots can be enabled or disabled.
1515     The passed array must be retained until the destructor or the next
1516     <SetSlotFilter()>, it is not deleted from the dispatcher, so it can thus be
1517     static.
1518 
1519     In read-only documents the quasi ReadOnlyDoc Flag of slots can be
1520     overturned by the use of 'bEnable == 2', so this will be displayed again.
1521     On the other slots it has no effect.
1522 
1523     // HACK(here should be used an enum) ???
1524     @param nEnable  1==true: only enable specified slots, disable all other
1525                     0==false: disable specified slots, first enable all other
1526     @param nCount Number of SIDs in the following Array
1527     @param pSIDs sorted Array of 'nCount' SIDs
1528 
1529     [Example]
1530 
1531     Targeted disabling of Slots 1, 2 and 3:
1532 
1533         static sal_uInt16 const pSIDs[] = { 1, 2, 3 };
1534         pDisp->SetSlotFilter( sal_False, sizeof(pSIDs)/sizeof(sal_uInt16), pSIDs );
1535 
1536     only permit Slots 5, 6 and 7:
1537 
1538         static sal_uInt16 const pSIDs[] = { 5, 6, 7 };
1539         pDisp->SetSlotFilter( sal_True, sizeof(pSIDs)/sizeof(sal_uInt16), pSIDs );
1540 
1541     Turn-off Filter:
1542 
1543         pDisp->SetSlotFilter();
1544 */
SetSlotFilter(SfxSlotFilterState nEnable,o3tl::span<sal_uInt16 const> pSIDs)1545 void SfxDispatcher::SetSlotFilter(SfxSlotFilterState nEnable,
1546         o3tl::span<sal_uInt16 const> pSIDs)
1547 {
1548 #ifdef DBG_UTIL
1549     // Check Array
1550     for ( std::size_t n = 1; n < pSIDs.size(); ++n )
1551         DBG_ASSERT( pSIDs[n] > pSIDs[n-1], "SetSlotFilter: SIDs not sorted" );
1552 #endif
1553 
1554     xImp->nFilterEnabling = nEnable;
1555     xImp->pFilterSIDs = pSIDs;
1556 
1557     GetBindings()->InvalidateAll(true);
1558 }
1559 
1560 extern "C" {
1561 
SfxCompareSIDs_Impl(const void * pSmaller,const void * pBigger)1562 static int SfxCompareSIDs_Impl(const void* pSmaller, const void* pBigger)
1563 {
1564     return static_cast<long>(*static_cast<sal_uInt16 const *>(pSmaller)) - static_cast<long>(*static_cast<sal_uInt16 const *>(pBigger));
1565 }
1566 
1567 }
1568 
1569 /** Searches for 'nSID' in the Filter set by <SetSlotFilter()> and
1570     returns sal_True, if the SIDis allowed, or sal_False, if it is
1571     disabled by the Filter.
1572 
1573     @return             0       =>      disabled
1574                         1       =>      enabled
1575                         2       =>      enabled even if ReadOnlyDoc
1576 */
IsSlotEnabledByFilter_Impl(sal_uInt16 nSID) const1577 SfxSlotFilterState SfxDispatcher::IsSlotEnabledByFilter_Impl( sal_uInt16 nSID ) const
1578 {
1579     // no filter?
1580     if ( xImp->pFilterSIDs.empty() )
1581         // => all SIDs allowed
1582         return SfxSlotFilterState::ENABLED;
1583 
1584     // search
1585     bool bFound = nullptr != bsearch( &nSID, xImp->pFilterSIDs.data(), xImp->pFilterSIDs.size(),
1586                                 sizeof(sal_uInt16), SfxCompareSIDs_Impl );
1587 
1588     // even if ReadOnlyDoc
1589     if ( SfxSlotFilterState::ENABLED_READONLY == xImp->nFilterEnabling )
1590         return bFound ? SfxSlotFilterState::ENABLED_READONLY : SfxSlotFilterState::ENABLED;
1591     // Otherwise after Negative/Positive Filter
1592     else if ( SfxSlotFilterState::ENABLED == xImp->nFilterEnabling )
1593         return bFound ? SfxSlotFilterState::ENABLED : SfxSlotFilterState::DISABLED;
1594     else
1595         return bFound ? SfxSlotFilterState::DISABLED : SfxSlotFilterState::ENABLED;
1596 }
1597 
1598 /** This helper method searches for the <Slot-Server> which currently serves
1599     the nSlot. As the result, rServe is filled accordingly.
1600 
1601     If known the SfxInterface which is currently served by nSlot can be
1602     passed along.
1603 
1604     The SfxDispatcher is flushed while searching for nSlot.
1605 
1606     @param nSlot Slot-Id to search for
1607     @param rServer <SfxSlotServer>-Instance to fill
1608 
1609     @return         true
1610                     The Slot was found, rServer is valid.
1611 
1612                     false
1613                     The Slot is currently not served, rServer is invalid.
1614 */
FindServer_(sal_uInt16 nSlot,SfxSlotServer & rServer)1615 bool SfxDispatcher::FindServer_(sal_uInt16 nSlot, SfxSlotServer& rServer)
1616 {
1617     SFX_STACK(SfxDispatcher::FindServer_);
1618 
1619     // Dispatcher locked? (nevertheless let SID_HELP_PI through)
1620     if ( IsLocked() )
1621     {
1622         xImp->bInvalidateOnUnlock = true;
1623         return false;
1624     }
1625 
1626     // Count the number of Shells in the linked dispatchers.
1627     Flush();
1628     sal_uInt16 nTotCount = xImp->aStack.size();
1629 
1630     // Verb-Slot?
1631     if (nSlot >= SID_VERB_START && nSlot <= SID_VERB_END)
1632     {
1633         for ( sal_uInt16 nShell = 0;; ++nShell )
1634         {
1635             SfxShell *pSh = GetShell(nShell);
1636             if ( pSh == nullptr )
1637                 return false;
1638             if ( dynamic_cast< const SfxViewShell *>( pSh ) !=  nullptr )
1639             {
1640                 const SfxSlot* pSlot = pSh->GetVerbSlot_Impl(nSlot);
1641                 if ( pSlot )
1642                 {
1643                     rServer.SetShellLevel(nShell);
1644                     rServer.SetSlot( pSlot );
1645                     return true;
1646                 }
1647             }
1648         }
1649     }
1650 
1651     // SID check against set filter
1652     SfxSlotFilterState nSlotEnableMode = SfxSlotFilterState::DISABLED;
1653     if ( xImp->pFrame )
1654     {
1655         nSlotEnableMode = IsSlotEnabledByFilter_Impl( nSlot );
1656         if ( SfxSlotFilterState::DISABLED == nSlotEnableMode )
1657             return false;
1658     }
1659 
1660     // In Quiet-Mode only Parent-Dispatcher
1661     if ( xImp->bQuiet )
1662     {
1663         return false;
1664     }
1665 
1666     bool bReadOnly = ( SfxSlotFilterState::ENABLED_READONLY != nSlotEnableMode && xImp->bReadOnly );
1667 
1668     // search through all the shells of the chained dispatchers
1669     // from top to bottom
1670     sal_uInt16 nFirstShell = 0;
1671     for ( sal_uInt16 i = nFirstShell; i < nTotCount; ++i )
1672     {
1673         SfxShell *pObjShell = GetShell(i);
1674         SfxInterface *pIFace = pObjShell->GetInterface();
1675         const SfxSlot *pSlot = pIFace->GetSlot(nSlot);
1676 
1677         if ( pSlot && pSlot->nDisableFlags != SfxDisableFlags::NONE &&
1678              ( static_cast<int>(pSlot->nDisableFlags) & static_cast<int>(pObjShell->GetDisableFlags()) ) != 0 )
1679             return false;
1680 
1681         if ( pSlot && !( pSlot->nFlags & SfxSlotMode::READONLYDOC ) && bReadOnly )
1682             return false;
1683 
1684         if ( pSlot )
1685         {
1686             // Slot belongs to Container?
1687             bool bIsContainerSlot = pSlot->IsMode(SfxSlotMode::CONTAINER);
1688             bool bIsInPlace = xImp->pFrame && xImp->pFrame->GetObjectShell()->IsInPlaceActive();
1689 
1690             // Shell belongs to Server?
1691             // AppDispatcher or IPFrame-Dispatcher
1692             bool bIsServerShell = !xImp->pFrame || bIsInPlace;
1693 
1694             // Of course ShellServer-Slots are also executable even when it is
1695             // executed on a container dispatcher without an IPClient.
1696             if ( !bIsServerShell )
1697             {
1698                 SfxViewShell *pViewSh = xImp->pFrame->GetViewShell();
1699                 bIsServerShell = !pViewSh || !pViewSh->GetUIActiveClient();
1700             }
1701 
1702             // Shell belongs to Container?
1703             // AppDispatcher or no IPFrameDispatcher
1704             bool bIsContainerShell = !xImp->pFrame || !bIsInPlace;
1705             // Shell and Slot match
1706             if ( !( ( bIsContainerSlot && bIsContainerShell ) ||
1707                     ( !bIsContainerSlot && bIsServerShell ) ) )
1708                 pSlot = nullptr;
1709         }
1710 
1711         if ( pSlot )
1712         {
1713             rServer.SetSlot(pSlot);
1714             rServer.SetShellLevel(i);
1715             return true;
1716         }
1717     }
1718 
1719     return false;
1720 }
1721 
1722 /** Helper method to obtain the status of the <Slot-Server>s rSvr.
1723     The required slots IDs (partly converted to Which-IDs of the pool)
1724     must be present in rstate.
1725 
1726     The SfxDispatcher is flushed before the query.
1727 
1728     @param rSvr Slot-Server to query
1729     @param rState SfxItemSet to be filled
1730     @param pRealSlot The actual Slot if possible
1731 */
FillState_(const SfxSlotServer & rSvr,SfxItemSet & rState,const SfxSlot * pRealSlot)1732 bool SfxDispatcher::FillState_(const SfxSlotServer& rSvr, SfxItemSet& rState,
1733         const SfxSlot* pRealSlot)
1734 {
1735     SFX_STACK(SfxDispatcher::FillState_);
1736 
1737     const SfxSlot *pSlot = rSvr.GetSlot();
1738     if ( pSlot && IsLocked() )
1739     {
1740         xImp->bInvalidateOnUnlock = true;
1741         return false;
1742     }
1743 
1744     if ( pSlot )
1745     {
1746         DBG_ASSERT(xImp->bFlushed,
1747                 "Dispatcher not flushed after retrieving slot servers!");
1748         if (!xImp->bFlushed)
1749             return false;
1750 
1751         // Determine the object and call the Message of this object
1752         SfxShell *pSh = GetShell(rSvr.GetShellLevel());
1753         DBG_ASSERT(pSh, "ObjectShell not found");
1754 
1755         SfxStateFunc pFunc;
1756 
1757         if (pRealSlot)
1758             pFunc = pRealSlot->GetStateFnc();
1759         else
1760             pFunc = pSlot->GetStateFnc();
1761 
1762         pSh->CallState( pFunc, rState );
1763 #ifdef DBG_UTIL
1764         // To examine the conformity of IDL (SlotMap) and current Items
1765         if ( rState.Count() )
1766         {
1767             SfxInterface *pIF = pSh->GetInterface();
1768             SfxItemIter aIter( rState );
1769             for ( const SfxPoolItem *pItem = aIter.GetCurItem();
1770                   pItem;
1771                   pItem = aIter.NextItem() )
1772             {
1773                 if ( !IsInvalidItem(pItem) && !pItem->IsVoidItem() )
1774                 {
1775                     sal_uInt16 nSlotId = rState.GetPool()->GetSlotId(pItem->Which());
1776                     SAL_INFO_IF(
1777                         typeid(pItem) != *pIF->GetSlot(nSlotId)->GetType()->Type(),
1778                         "sfx.control",
1779                         "item-type unequal to IDL (=> no BASIC) with SID: "
1780                             << nSlotId << " in " << pIF->GetClassName());
1781                 }
1782             }
1783         }
1784 #endif
1785 
1786         return true;
1787     }
1788 
1789     return false;
1790 }
1791 
ExecutePopup(vcl::Window * pWin,const Point * pPos)1792 void SfxDispatcher::ExecutePopup( vcl::Window *pWin, const Point *pPos )
1793 {
1794     SfxDispatcher &rDisp = *SfxGetpApp()->GetDispatcher_Impl();
1795     sal_uInt16 nShLevel = 0;
1796     SfxShell *pSh;
1797 
1798     if ( rDisp.xImp->bQuiet )
1799         nShLevel = rDisp.xImp->aStack.size();
1800 
1801     for ( pSh = rDisp.GetShell(nShLevel); pSh; ++nShLevel, pSh = rDisp.GetShell(nShLevel) )
1802     {
1803         const OUString& rResName = pSh->GetInterface()->GetPopupMenuName();
1804         if ( !rResName.isEmpty() )
1805         {
1806             rDisp.ExecutePopup( rResName, pWin, pPos );
1807             return;
1808         }
1809     }
1810 }
1811 
ExecutePopup(const OUString & rResName,vcl::Window * pWin,const Point * pPos)1812 void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, const Point* pPos )
1813 {
1814     css::uno::Sequence< css::uno::Any > aArgs( 3 );
1815     aArgs[0] <<= comphelper::makePropertyValue( "Value", rResName );
1816     aArgs[1] <<= comphelper::makePropertyValue( "Frame", GetFrame()->GetFrame().GetFrameInterface() );
1817     aArgs[2] <<= comphelper::makePropertyValue( "IsContextMenu", true );
1818 
1819     css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
1820     css::uno::Reference< css::frame::XPopupMenuController > xPopupController(
1821         xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
1822         "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext ), css::uno::UNO_QUERY );
1823 
1824     css::uno::Reference< css::awt::XPopupMenu > xPopupMenu( xContext->getServiceManager()->createInstanceWithContext(
1825         "com.sun.star.awt.PopupMenu", xContext ), css::uno::UNO_QUERY );
1826 
1827     if ( !xPopupController.is() || !xPopupMenu.is() )
1828         return;
1829 
1830     vcl::Window* pWindow = pWin ? pWin : xImp->pFrame->GetFrame().GetWorkWindow_Impl()->GetWindow();
1831     Point aPos = pPos ? *pPos : pWindow->GetPointerPosPixel();
1832 
1833     css::ui::ContextMenuExecuteEvent aEvent;
1834     aEvent.SourceWindow = VCLUnoHelper::GetInterface( pWindow );
1835     aEvent.ExecutePosition.X = aPos.X();
1836     aEvent.ExecutePosition.Y = aPos.Y();
1837 
1838     xPopupController->setPopupMenu( xPopupMenu );
1839     VCLXMenu* pAwtMenu = comphelper::getUnoTunnelImplementation<VCLXMenu>( xPopupMenu );
1840     PopupMenu* pVCLMenu = static_cast< PopupMenu* >( pAwtMenu->GetMenu() );
1841     if (comphelper::LibreOfficeKit::isActive())
1842     {
1843         boost::property_tree::ptree aMenu = fillPopupMenu(pVCLMenu);
1844         boost::property_tree::ptree aRoot;
1845         aRoot.add_child("menu", aMenu);
1846 
1847         std::stringstream aStream;
1848         boost::property_tree::write_json(aStream, aRoot, true);
1849         if (SfxViewShell* pViewShell = xImp->pFrame->GetViewShell())
1850             pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CONTEXT_MENU, aStream.str().c_str());
1851     }
1852     else
1853     {
1854         OUString aMenuURL = "private:resource/popupmenu/" + rResName;
1855         if (pVCLMenu && GetFrame()->GetViewShell()->TryContextMenuInterception(*pVCLMenu, aMenuURL, aEvent))
1856         {
1857             pVCLMenu->Execute(pWindow, aPos);
1858         }
1859     }
1860 
1861     css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY );
1862     if ( xComponent.is() )
1863         xComponent->dispose();
1864 }
1865 
1866 /** With this method the SfxDispatcher can be locked and released. A locked
1867     SfxDispatcher does not perform <SfxRequest>s and does no longer provide
1868     status information. It behaves as if all the slots were disabled.
1869 */
Lock(bool bLock)1870 void SfxDispatcher::Lock( bool bLock )
1871 {
1872     SfxBindings* pBindings = GetBindings();
1873     if ( !bLock && xImp->bLocked && xImp->bInvalidateOnUnlock )
1874     {
1875         if ( pBindings )
1876             pBindings->InvalidateAll(true);
1877         xImp->bInvalidateOnUnlock = false;
1878     }
1879     else if ( pBindings )
1880         pBindings->InvalidateAll(false);
1881     xImp->bLocked = bLock;
1882     if ( !bLock )
1883     {
1884         for(size_t i = 0; i < xImp->aReqArr.size(); ++i)
1885             xImp->xPoster->Post(std::move(xImp->aReqArr[i]));
1886         xImp->aReqArr.clear();
1887     }
1888 }
1889 
GetObjectBarId(sal_uInt16 nPos) const1890 ToolbarId SfxDispatcher::GetObjectBarId( sal_uInt16 nPos ) const
1891 {
1892     return xImp->aObjBars[nPos].eId;
1893 }
1894 
HideUI(bool bHide)1895 void SfxDispatcher::HideUI( bool bHide )
1896 {
1897     bool bWasHidden = xImp->bNoUI;
1898     xImp->bNoUI = bHide;
1899     if ( xImp->pFrame )
1900     {
1901         SfxViewFrame* pTop = xImp->pFrame->GetTopViewFrame();
1902         if ( pTop && pTop->GetBindings().GetDispatcher() == this )
1903         {
1904             SfxFrame& rFrame = pTop->GetFrame();
1905             if ( rFrame.IsMenuBarOn_Impl() )
1906             {
1907                 css::uno::Reference < css::beans::XPropertySet > xPropSet( rFrame.GetFrameInterface(), css::uno::UNO_QUERY );
1908                 if ( xPropSet.is() )
1909                 {
1910                     css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
1911                     css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
1912                     aValue >>= xLayoutManager;
1913                     if ( xLayoutManager.is() )
1914                         xLayoutManager->setVisible( !bHide );
1915                 }
1916             }
1917         }
1918     }
1919 
1920     if ( bHide != bWasHidden )
1921         Update_Impl( true );
1922 }
1923 
SetReadOnly_Impl(bool bOn)1924 void SfxDispatcher::SetReadOnly_Impl( bool bOn )
1925 {
1926     xImp->bReadOnly = bOn;
1927 }
1928 
GetReadOnly_Impl() const1929 bool SfxDispatcher::GetReadOnly_Impl() const
1930 {
1931     return xImp->bReadOnly;
1932 }
1933 
1934 /** With 'bOn' the Dispatcher is quasi dead and transfers everything to the
1935     Parent-Dispatcher.
1936 */
SetQuietMode_Impl(bool bOn)1937 void SfxDispatcher::SetQuietMode_Impl( bool bOn )
1938 {
1939     xImp->bQuiet = bOn;
1940     SfxBindings* pBindings = GetBindings();
1941     if ( pBindings )
1942         pBindings->InvalidateAll(true);
1943 }
1944 
QueryState(sal_uInt16 nSlot,const SfxPoolItem * & rpState)1945 SfxItemState SfxDispatcher::QueryState( sal_uInt16 nSlot, const SfxPoolItem* &rpState )
1946 {
1947     SfxShell *pShell = nullptr;
1948     const SfxSlot *pSlot = nullptr;
1949     if ( GetShellAndSlot_Impl( nSlot, &pShell, &pSlot, false, true ) )
1950     {
1951         rpState = pShell->GetSlotState(nSlot);
1952         if ( !rpState )
1953             return SfxItemState::DISABLED;
1954         else
1955             return SfxItemState::DEFAULT;
1956     }
1957 
1958     return SfxItemState::DISABLED;
1959 }
1960 
QueryState(sal_uInt16 nSID,css::uno::Any & rAny)1961 SfxItemState SfxDispatcher::QueryState( sal_uInt16 nSID, css::uno::Any& rAny )
1962 {
1963     SfxShell *pShell = nullptr;
1964     const SfxSlot *pSlot = nullptr;
1965     if ( GetShellAndSlot_Impl( nSID, &pShell, &pSlot, false, true ) )
1966     {
1967         const SfxPoolItem* pItem = pShell->GetSlotState( nSID );
1968         if ( !pItem )
1969             return SfxItemState::DISABLED;
1970         else
1971         {
1972             css::uno::Any aState;
1973             if ( !pItem->IsVoidItem() )
1974             {
1975                 sal_uInt16 nSubId( 0 );
1976                 SfxItemPool& rPool = pShell->GetPool();
1977                 sal_uInt16 nWhich = rPool.GetWhich( nSID );
1978                 if ( rPool.GetMetric( nWhich ) == MapUnit::MapTwip )
1979                     nSubId |= CONVERT_TWIPS;
1980                 pItem->QueryValue( aState, static_cast<sal_uInt8>(nSubId) );
1981             }
1982             rAny = aState;
1983 
1984             return SfxItemState::DEFAULT;
1985         }
1986     }
1987 
1988     return SfxItemState::DISABLED;
1989 }
1990 
IsReadOnlyShell_Impl(sal_uInt16 nShell) const1991 bool SfxDispatcher::IsReadOnlyShell_Impl( sal_uInt16 nShell ) const
1992 {
1993     sal_uInt16 nShellCount = xImp->aStack.size();
1994     if ( nShell < nShellCount )
1995     {
1996         SfxShell* pShell = *( xImp->aStack.rbegin() + nShell );
1997         if( dynamic_cast< const SfxModule *>( pShell ) != nullptr || dynamic_cast< const SfxApplication *>( pShell ) != nullptr || dynamic_cast< const SfxViewFrame *>( pShell ) !=  nullptr )
1998             return false;
1999         else
2000             return xImp->bReadOnly;
2001     }
2002     return true;
2003 }
2004 
RemoveShell_Impl(SfxShell & rShell)2005 void SfxDispatcher::RemoveShell_Impl( SfxShell& rShell )
2006 {
2007     Flush();
2008 
2009     sal_uInt16 nCount = xImp->aStack.size();
2010     for ( sal_uInt16 n=0; n<nCount; ++n )
2011     {
2012         if ( xImp->aStack[n] == &rShell )
2013         {
2014             xImp->aStack.erase( xImp->aStack.begin() + n );
2015             rShell.SetDisableFlags( SfxDisableFlags::NONE );
2016             rShell.DoDeactivate_Impl(xImp->pFrame, true);
2017             break;
2018         }
2019     }
2020 
2021     if ( !SfxGetpApp()->IsDowning() )
2022     {
2023         xImp->bUpdated = false;
2024         InvalidateBindings_Impl(true);
2025     }
2026 }
2027 
InvalidateBindings_Impl(bool bModify)2028 void SfxDispatcher::InvalidateBindings_Impl( bool bModify )
2029 {
2030     // App-Dispatcher?
2031     if ( IsAppDispatcher() )
2032     {
2033         for ( SfxViewFrame *pFrame = SfxViewFrame::GetFirst();
2034                 pFrame;
2035                 pFrame = SfxViewFrame::GetNext( *pFrame ) )
2036             pFrame->GetBindings().InvalidateAll(bModify);
2037     }
2038     else
2039     {
2040         SfxDispatcher *pDisp = GetBindings()->GetDispatcher_Impl();
2041         if ( pDisp == this )
2042         {
2043             GetBindings()->InvalidateAll( bModify );
2044         }
2045     }
2046 }
2047 
IsUpdated_Impl() const2048 bool SfxDispatcher::IsUpdated_Impl() const
2049 {
2050     return xImp->bUpdated;
2051 }
2052 
SetDisableFlags(SfxDisableFlags nFlags)2053 void SfxDispatcher::SetDisableFlags( SfxDisableFlags nFlags )
2054 {
2055     xImp->nDisableFlags = nFlags;
2056     for ( SfxShellStack_Impl::reverse_iterator it = xImp->aStack.rbegin(); it != xImp->aStack.rend(); ++it )
2057         (*it)->SetDisableFlags( nFlags );
2058 }
2059 
GetDisableFlags() const2060 SfxDisableFlags SfxDispatcher::GetDisableFlags() const
2061 {
2062     return xImp->nDisableFlags;
2063 }
2064 
GetModule() const2065 SfxModule* SfxDispatcher::GetModule() const
2066 {
2067     for ( sal_uInt16 nShell = 0;; ++nShell )
2068     {
2069         SfxShell *pSh = GetShell(nShell);
2070         if ( pSh == nullptr )
2071             return nullptr;
2072         if ( dynamic_cast< const SfxModule *>( pSh ) !=  nullptr )
2073             return static_cast<SfxModule*>(pSh);
2074     }
2075 }
2076 
2077 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2078