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