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