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