/src/libreoffice/sfx2/source/sidebar/SidebarController.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 | | #include <sfx2/sidebar/SidebarController.hxx> |
20 | | |
21 | | #include <boost/property_tree/json_parser.hpp> |
22 | | |
23 | | #include <sfx2/sidebar/Deck.hxx> |
24 | | #include <sidebar/DeckDescriptor.hxx> |
25 | | #include <sidebar/DeckTitleBar.hxx> |
26 | | #include <sfx2/sidebar/Panel.hxx> |
27 | | #include <sidebar/PanelDescriptor.hxx> |
28 | | #include <sidebar/PanelTitleBar.hxx> |
29 | | #include <sfx2/sidebar/TabBar.hxx> |
30 | | #include <sfx2/sidebar/Theme.hxx> |
31 | | #include <sfx2/sidebar/SidebarChildWindow.hxx> |
32 | | #include <sidebar/Tools.hxx> |
33 | | #include <sfx2/sidebar/SidebarDockingWindow.hxx> |
34 | | #include <com/sun/star/ui/XSidebarProvider.hpp> |
35 | | #include <com/sun/star/frame/XController2.hpp> |
36 | | #include <sfx2/sidebar/Context.hxx> |
37 | | #include <sfx2/viewfrm.hxx> |
38 | | #include <sfx2/viewsh.hxx> |
39 | | |
40 | | |
41 | | #include <framework/ContextChangeEventMultiplexerTunnel.hxx> |
42 | | #include <vcl/EnumContext.hxx> |
43 | | #include <vcl/uitest/logger.hxx> |
44 | | #include <vcl/uitest/eventdescription.hxx> |
45 | | #include <vcl/svapp.hxx> |
46 | | #include <vcl/vclevent.hxx> |
47 | | #include <vcl/weld/Image.hxx> |
48 | | #include <vcl/weld/Menu.hxx> |
49 | | #include <splitwin.hxx> |
50 | | #include <comphelper/diagnose_ex.hxx> |
51 | | #include <tools/json_writer.hxx> |
52 | | #include <tools/link.hxx> |
53 | | #include <toolkit/helper/vclunohelper.hxx> |
54 | | #include <comphelper/processfactory.hxx> |
55 | | #include <comphelper/namedvaluecollection.hxx> |
56 | | #include <comphelper/lok.hxx> |
57 | | #include <sal/log.hxx> |
58 | | #include <officecfg/Office/UI/Sidebar.hxx> |
59 | | #include <LibreOfficeKit/LibreOfficeKitEnums.h> |
60 | | #include <o3tl/string_view.hxx> |
61 | | |
62 | | #include <com/sun/star/awt/XWindowPeer.hpp> |
63 | | #include <com/sun/star/frame/XDispatch.hpp> |
64 | | #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp> |
65 | | #include <com/sun/star/ui/ContextChangeEventObject.hpp> |
66 | | #include <com/sun/star/ui/theUIElementFactoryManager.hpp> |
67 | | #include <com/sun/star/util/URL.hpp> |
68 | | #include <com/sun/star/rendering/XSpriteCanvas.hpp> |
69 | | |
70 | | #include <bitmaps.hlst> |
71 | | |
72 | | using namespace css; |
73 | | using namespace css::uno; |
74 | | |
75 | | namespace |
76 | | { |
77 | | constexpr OUString gsReadOnlyCommandName = u".uno:EditDoc"_ustr; |
78 | | const sal_Int32 gnWidthCloseThreshold (70); |
79 | | const sal_Int32 gnWidthOpenThreshold (40); |
80 | | |
81 | | std::string UnoNameFromDeckId(std::u16string_view rsDeckId, const sfx2::sidebar::Context& context) |
82 | 0 | { |
83 | 0 | if (rsDeckId == u"SdCustomAnimationDeck") |
84 | 0 | return ".uno:CustomAnimation"; |
85 | | |
86 | 0 | if (rsDeckId == u"PropertyDeck") |
87 | 0 | return vcl::EnumContext::Application::Impress == vcl::EnumContext::GetApplicationEnum(context.msApplication) ? ".uno:ModifyPage" : ".uno:Sidebar"; |
88 | | |
89 | 0 | if (rsDeckId == u"SdLayoutsDeck") |
90 | 0 | return ".uno:ModifyPage"; |
91 | | |
92 | 0 | if (rsDeckId == u"SdSlideTransitionDeck") |
93 | 0 | return ".uno:SlideChangeWindow"; |
94 | | |
95 | 0 | if (rsDeckId == u"SdAllMasterPagesDeck") |
96 | 0 | return ".uno:MasterSlidesPanel"; |
97 | | |
98 | 0 | if (rsDeckId == u"SdMasterPagesDeck") |
99 | 0 | return ".uno:MasterSlidesPanel"; |
100 | | |
101 | 0 | if (rsDeckId == u"GalleryDeck") |
102 | 0 | return ".uno:Gallery"; |
103 | | |
104 | 0 | OString sUno = ".uno:SidebarDeck." + OUStringToOString(rsDeckId, RTL_TEXTENCODING_ASCII_US); |
105 | 0 | return std::string(sUno); |
106 | 0 | } |
107 | | } |
108 | | |
109 | | namespace sfx2::sidebar { |
110 | | |
111 | | namespace { |
112 | | |
113 | | /** When in doubt, show this deck. |
114 | | */ |
115 | | constexpr OUString gsDefaultDeckId(u"PropertyDeck"_ustr); |
116 | | } |
117 | | |
118 | | SidebarController::SidebarController ( |
119 | | SidebarDockingWindow* pParentWindow, |
120 | | const SfxViewFrame* pViewFrame) |
121 | 0 | : mpParentWindow(pParentWindow), |
122 | 0 | mpViewFrame(pViewFrame), |
123 | 0 | mxFrame(pViewFrame->GetFrame().GetFrameInterface()), |
124 | 0 | mpTabBar(VclPtr<TabBar>::Create( |
125 | 0 | mpParentWindow, |
126 | 0 | mxFrame, |
127 | 0 | [this](const OUString& rsDeckId) { return this->OpenThenToggleDeck(rsDeckId); }, |
128 | 0 | [this](weld::Menu& rMainMenu, weld::Menu& rSubMenu) { return this->ConnectMenuActivateHandlers(rMainMenu, rSubMenu); }, |
129 | 0 | *this)), |
130 | 0 | maCurrentContext(OUString(), OUString()), |
131 | 0 | maRequestedContext(OUString(), OUString()), |
132 | 0 | mnRequestedForceFlags(SwitchFlag_NoForce), |
133 | 0 | mbMinimumSidebarWidth(officecfg::Office::UI::Sidebar::General::MinimumWidth::get()), |
134 | 0 | msCurrentDeckId(gsDefaultDeckId), |
135 | 0 | maPropertyChangeForwarder(mpViewFrame, [this](){ return this->BroadcastPropertyChange(); }), |
136 | 0 | maContextChangeUpdate(mpViewFrame, [this](){ return this->UpdateConfigurations(); }), |
137 | 0 | mbFloatingDeckClosed(!pParentWindow->IsFloatingMode()), |
138 | 0 | mnSavedSidebarWidth(pParentWindow->GetSizePixel().Width()), |
139 | 0 | maFocusManager([this](const Panel& rPanel){ return this->ShowPanel(rPanel); }), |
140 | 0 | mbIsDocumentReadOnly(false), |
141 | 0 | mpSplitWindow(nullptr), |
142 | 0 | mnWidthOnSplitterButtonDown(0) |
143 | 0 | { |
144 | 0 | mnMaximumSidebarWidth = officecfg::Office::UI::Sidebar::General::MaximumWidth::get() * mpTabBar->GetDPIScaleFactor(); |
145 | | // Decks and panel collections for this sidebar |
146 | 0 | mpResourceManager = std::make_unique<ResourceManager>(); |
147 | 0 | } Unexecuted instantiation: sfx2::sidebar::SidebarController::SidebarController(sfx2::sidebar::SidebarDockingWindow*, SfxViewFrame const*) Unexecuted instantiation: sfx2::sidebar::SidebarController::SidebarController(sfx2::sidebar::SidebarDockingWindow*, SfxViewFrame const*) |
148 | | |
149 | | rtl::Reference<SidebarController> SidebarController::create(SidebarDockingWindow* pParentWindow, |
150 | | const SfxViewFrame* pViewFrame) |
151 | 0 | { |
152 | 0 | rtl::Reference<SidebarController> instance(new SidebarController(pParentWindow, pViewFrame)); |
153 | |
|
154 | 0 | const css::uno::Reference<css::frame::XFrame>& rxFrame = pViewFrame->GetFrame().GetFrameInterface(); |
155 | 0 | instance->registerSidebarForFrame(rxFrame->getController()); |
156 | 0 | rxFrame->addFrameActionListener(instance); |
157 | | // Listen for window events. |
158 | 0 | instance->mpParentWindow->AddEventListener(LINK(instance.get(), SidebarController, WindowEventHandler)); |
159 | | |
160 | | // Listen for theme property changes. |
161 | 0 | instance->mxThemePropertySet = Theme::GetPropertySet(); |
162 | 0 | instance->mxThemePropertySet->addPropertyChangeListener( |
163 | 0 | u""_ustr, |
164 | 0 | static_cast<css::beans::XPropertyChangeListener*>(instance.get())); |
165 | | |
166 | | // Get the dispatch object as preparation to listen for changes of |
167 | | // the read-only state. |
168 | 0 | const util::URL aURL (Tools::GetURL(gsReadOnlyCommandName)); |
169 | 0 | instance->mxReadOnlyModeDispatch = Tools::GetDispatch(rxFrame, aURL); |
170 | 0 | if (instance->mxReadOnlyModeDispatch.is()) |
171 | 0 | instance->mxReadOnlyModeDispatch->addStatusListener(instance, aURL); |
172 | | |
173 | | //first UpdateConfigurations call will SwitchToDeck |
174 | |
|
175 | 0 | return instance; |
176 | 0 | } |
177 | | |
178 | | SidebarController::~SidebarController() |
179 | 0 | { |
180 | 0 | } |
181 | | |
182 | | SidebarController* SidebarController::GetSidebarControllerForFrame ( |
183 | | const css::uno::Reference<css::frame::XFrame>& rxFrame) |
184 | 0 | { |
185 | 0 | uno::Reference<frame::XController> const xController(rxFrame->getController()); |
186 | 0 | if (!xController.is()) // this may happen during dispose of Draw controller but perhaps it's a bug |
187 | 0 | { |
188 | 0 | SAL_WARN("sfx.sidebar", "GetSidebarControllerForFrame: frame has no XController"); |
189 | 0 | return nullptr; |
190 | 0 | } |
191 | 0 | uno::Reference<ui::XContextChangeEventListener> const xListener( |
192 | 0 | framework::GetFirstListenerWith( |
193 | 0 | ::comphelper::getProcessComponentContext(), |
194 | 0 | xController, |
195 | 0 | [] (uno::Reference<uno::XInterface> const& xRef) |
196 | 0 | { return nullptr != dynamic_cast<SidebarController*>(xRef.get()); } |
197 | 0 | )); |
198 | |
|
199 | 0 | return dynamic_cast<SidebarController*>(xListener.get()); |
200 | 0 | } |
201 | | |
202 | | void SidebarController::registerSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController) |
203 | 0 | { |
204 | | // Listen for context change events. |
205 | 0 | css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( |
206 | 0 | css::ui::ContextChangeEventMultiplexer::get( |
207 | 0 | ::comphelper::getProcessComponentContext())); |
208 | 0 | xMultiplexer->addContextChangeEventListener( |
209 | 0 | static_cast<css::ui::XContextChangeEventListener*>(this), |
210 | 0 | xController); |
211 | 0 | } |
212 | | |
213 | | void SidebarController::unregisterSidebarForFrame(const css::uno::Reference<css::frame::XController>& xController) |
214 | 0 | { |
215 | 0 | saveDeckState(); |
216 | 0 | disposeDecks(); |
217 | |
|
218 | 0 | css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer ( |
219 | 0 | css::ui::ContextChangeEventMultiplexer::get( |
220 | 0 | ::comphelper::getProcessComponentContext())); |
221 | 0 | xMultiplexer->removeContextChangeEventListener( |
222 | 0 | static_cast<css::ui::XContextChangeEventListener*>(this), |
223 | 0 | xController); |
224 | 0 | } |
225 | | |
226 | | void SidebarController::disposeDecks() |
227 | 0 | { |
228 | 0 | SolarMutexGuard aSolarMutexGuard; |
229 | |
|
230 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
231 | 0 | { |
232 | 0 | if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell()) |
233 | 0 | { |
234 | 0 | const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext()); |
235 | 0 | if (!hide.empty()) |
236 | 0 | { |
237 | | // Be consistent with SwitchToDeck(), so both places emit JSON. |
238 | 0 | boost::property_tree::ptree aTree; |
239 | 0 | aTree.put("commandName", hide); |
240 | 0 | aTree.put("state", "false"); |
241 | 0 | std::stringstream aStream; |
242 | 0 | boost::property_tree::write_json(aStream, aTree); |
243 | 0 | pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, |
244 | 0 | OString(aStream.str())); |
245 | 0 | } |
246 | 0 | } |
247 | |
|
248 | 0 | if (mpParentWindow) |
249 | 0 | mpParentWindow->ReleaseLOKNotifier(); |
250 | 0 | } |
251 | |
|
252 | 0 | mpCurrentDeck.reset(); |
253 | 0 | maFocusManager.Clear(); |
254 | 0 | mpResourceManager->disposeDecks(); |
255 | 0 | } |
256 | | |
257 | | namespace |
258 | | { |
259 | | class CloseIndicator final : public InterimItemWindow |
260 | | { |
261 | | public: |
262 | | CloseIndicator(vcl::Window* pParent) |
263 | 0 | : InterimItemWindow(pParent, u"svt/ui/fixedimagecontrol.ui"_ustr, u"FixedImageControl"_ustr) |
264 | 0 | , m_xWidget(m_xBuilder->weld_image(u"image"_ustr)) |
265 | 0 | { |
266 | 0 | InitControlBase(m_xWidget.get()); |
267 | |
|
268 | 0 | m_xWidget->set_from_icon_name(SIDEBAR_CLOSE_INDICATOR); |
269 | |
|
270 | 0 | SetSizePixel(get_preferred_size()); |
271 | |
|
272 | 0 | SetBackground(Theme::GetColor(Theme::Color_DeckBackground)); |
273 | 0 | } |
274 | | |
275 | | virtual ~CloseIndicator() override |
276 | 0 | { |
277 | 0 | disposeOnce(); |
278 | 0 | } |
279 | | |
280 | | virtual void dispose() override |
281 | 0 | { |
282 | 0 | m_xWidget.reset(); |
283 | 0 | InterimItemWindow::dispose(); |
284 | 0 | } |
285 | | |
286 | | private: |
287 | | std::unique_ptr<weld::Image> m_xWidget; |
288 | | }; |
289 | | } |
290 | | |
291 | | void SidebarController::disposing(std::unique_lock<std::mutex>&) |
292 | 0 | { |
293 | 0 | SolarMutexGuard aSolarMutexGuard; |
294 | |
|
295 | 0 | mpCloseIndicator.disposeAndClear(); |
296 | |
|
297 | 0 | maFocusManager.Clear(); |
298 | 0 | mpTabBar.disposeAndClear(); |
299 | |
|
300 | 0 | saveDeckState(); |
301 | | |
302 | | // clear decks |
303 | 0 | ResourceManager::DeckContextDescriptorContainer aDecks; |
304 | |
|
305 | 0 | mpResourceManager->GetMatchingDecks ( |
306 | 0 | aDecks, |
307 | 0 | GetCurrentContext(), |
308 | 0 | IsDocumentReadOnly(), |
309 | 0 | mxFrame->getController()); |
310 | |
|
311 | 0 | for (const auto& rDeck : aDecks) |
312 | 0 | { |
313 | 0 | std::shared_ptr<DeckDescriptor> deckDesc = mpResourceManager->GetDeckDescriptor(rDeck.msId); |
314 | |
|
315 | 0 | VclPtr<Deck> aDeck = deckDesc->mpDeck; |
316 | 0 | if (aDeck) |
317 | 0 | aDeck.disposeAndClear(); |
318 | 0 | } |
319 | |
|
320 | 0 | maContextChangeUpdate.CancelRequest(); |
321 | |
|
322 | 0 | if (mxReadOnlyModeDispatch.is()) |
323 | 0 | mxReadOnlyModeDispatch->removeStatusListener(this, Tools::GetURL(gsReadOnlyCommandName)); |
324 | |
|
325 | 0 | if (mxThemePropertySet.is()) |
326 | 0 | mxThemePropertySet->removePropertyChangeListener( |
327 | 0 | u""_ustr, |
328 | 0 | static_cast<css::beans::XPropertyChangeListener*>(this)); |
329 | |
|
330 | 0 | if (mpParentWindow != nullptr) |
331 | 0 | { |
332 | 0 | mpParentWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); |
333 | 0 | mpParentWindow = nullptr; |
334 | 0 | } |
335 | |
|
336 | 0 | if (mpSplitWindow != nullptr) |
337 | 0 | { |
338 | 0 | mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); |
339 | 0 | mpSplitWindow = nullptr; |
340 | 0 | } |
341 | |
|
342 | 0 | mxFrame->removeFrameActionListener(this); |
343 | |
|
344 | 0 | uno::Reference<css::frame::XController> xController = mxFrame->getController(); |
345 | 0 | if (!xController.is()) |
346 | 0 | xController = mxCurrentController; |
347 | |
|
348 | 0 | unregisterSidebarForFrame(xController); |
349 | 0 | } |
350 | | |
351 | | void SAL_CALL SidebarController::notifyContextChangeEvent (const css::ui::ContextChangeEventObject& rEvent) |
352 | 0 | { |
353 | 0 | SolarMutexGuard aSolarMutexGuard; |
354 | | |
355 | | // Update to the requested new context asynchronously to avoid |
356 | | // subtle errors caused by SFX2 which in rare cases can not |
357 | | // properly handle a synchronous update. |
358 | |
|
359 | 0 | maRequestedContext = Context( |
360 | 0 | rEvent.ApplicationName, |
361 | 0 | rEvent.ContextName); |
362 | |
|
363 | 0 | if (maRequestedContext != maCurrentContext) |
364 | 0 | { |
365 | 0 | mxCurrentController.set(rEvent.Source, css::uno::UNO_QUERY); |
366 | 0 | maContextChangeUpdate.RequestCall(); // async call, not a prob |
367 | | // calling with held |
368 | | // solarmutex |
369 | |
|
370 | 0 | bool bSwitchedApp = maRequestedContext.msApplication != maCurrentContext.msApplication; |
371 | | // Happens on reattach of sidebar to frame or context change |
372 | | // LOK performance impact: prevents to switch sidebar on every keypress in multi user case |
373 | | // Allow when enters embedded OLE (eg. Math formula editor second time) |
374 | 0 | if (!comphelper::LibreOfficeKit::isActive() || bSwitchedApp) |
375 | 0 | UpdateConfigurations(); |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | | void SAL_CALL SidebarController::disposing (const css::lang::EventObject& ) |
380 | 0 | { |
381 | 0 | dispose(); |
382 | 0 | } |
383 | | |
384 | | void SAL_CALL SidebarController::propertyChange (const css::beans::PropertyChangeEvent& ) |
385 | 0 | { |
386 | 0 | SolarMutexGuard aSolarMutexGuard; |
387 | |
|
388 | 0 | maPropertyChangeForwarder.RequestCall(); // async call, not a prob |
389 | | // to call with held |
390 | | // solarmutex |
391 | 0 | } |
392 | | |
393 | | void SAL_CALL SidebarController::statusChanged (const css::frame::FeatureStateEvent& rEvent) |
394 | 0 | { |
395 | 0 | SolarMutexGuard aSolarMutexGuard; |
396 | |
|
397 | 0 | bool bIsReadWrite (true); |
398 | 0 | if (rEvent.IsEnabled) |
399 | 0 | rEvent.State >>= bIsReadWrite; |
400 | |
|
401 | 0 | if (mbIsDocumentReadOnly != !bIsReadWrite) |
402 | 0 | { |
403 | 0 | mbIsDocumentReadOnly = !bIsReadWrite; |
404 | | |
405 | | // Force the current deck to update its panel list. |
406 | 0 | if ( ! mbIsDocumentReadOnly) |
407 | 0 | SwitchToDefaultDeck(); |
408 | |
|
409 | 0 | mnRequestedForceFlags |= SwitchFlag_ForceSwitch; |
410 | 0 | maContextChangeUpdate.RequestCall(); // async call, ok to call |
411 | | // with held solarmutex |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | | void SAL_CALL SidebarController::requestLayout() |
416 | 0 | { |
417 | 0 | SolarMutexGuard aSolarMutexGuard; |
418 | |
|
419 | 0 | sal_Int32 nMinimalWidth = 0; |
420 | 0 | if (mpCurrentDeck && !mpCurrentDeck->isDisposed()) |
421 | 0 | { |
422 | 0 | mpCurrentDeck->RequestLayout(); |
423 | 0 | nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0; |
424 | 0 | } |
425 | 0 | RestrictWidth(nMinimalWidth); |
426 | 0 | } |
427 | | |
428 | | void SidebarController::BroadcastPropertyChange() |
429 | 0 | { |
430 | 0 | mpParentWindow->Invalidate(InvalidateFlags::Children); |
431 | 0 | } |
432 | | |
433 | | void SidebarController::NotifyResize() |
434 | 0 | { |
435 | 0 | if (!mpTabBar) |
436 | 0 | { |
437 | 0 | OSL_ASSERT(mpTabBar!=nullptr); |
438 | 0 | return; |
439 | 0 | } |
440 | | |
441 | 0 | const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth(); |
442 | |
|
443 | 0 | const sal_Int32 nWidth(mpParentWindow->GetSizePixel().Width()); |
444 | 0 | const sal_Int32 nHeight(mpParentWindow->GetSizePixel().Height()); |
445 | |
|
446 | 0 | mbIsDeckOpen = (nWidth > nTabBarDefaultWidth); |
447 | |
|
448 | 0 | if (mnSavedSidebarWidth <= 0) |
449 | 0 | mnSavedSidebarWidth = nWidth; |
450 | |
|
451 | 0 | bool bIsDeckVisible; |
452 | 0 | const bool bIsOpening (nWidth > mnWidthOnSplitterButtonDown); |
453 | 0 | if (bIsOpening) |
454 | 0 | bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthOpenThreshold; |
455 | 0 | else |
456 | 0 | bIsDeckVisible = nWidth >= nTabBarDefaultWidth + gnWidthCloseThreshold; |
457 | 0 | mbIsDeckRequestedOpen = bIsDeckVisible; |
458 | 0 | UpdateCloseIndicator(!bIsDeckVisible); |
459 | |
|
460 | 0 | if (mpCurrentDeck && !mpCurrentDeck->isDisposed()) |
461 | 0 | { |
462 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
463 | 0 | WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right; |
464 | 0 | tools::Long nDeckX, nTabX; |
465 | 0 | if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen |
466 | 0 | { |
467 | 0 | nDeckX = nTabBarDefaultWidth; |
468 | 0 | nTabX = 0; |
469 | 0 | } |
470 | 0 | else // attach the Sidebar towards the right-side of screen |
471 | 0 | { |
472 | 0 | nDeckX = 0; |
473 | 0 | nTabX = nWidth - nTabBarDefaultWidth; |
474 | 0 | } |
475 | | |
476 | | // Place the deck first. |
477 | 0 | if (bIsDeckVisible) |
478 | 0 | { |
479 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
480 | 0 | { |
481 | | // We want to let the layouter use up as much of the |
482 | | // height as necessary to make sure no scrollbar is |
483 | | // visible. This only works when there are no greedy |
484 | | // panes that fill up all available area. So we only |
485 | | // use this for the PropertyDeck, which has no such |
486 | | // panes, while most other do. This is fine, since |
487 | | // it's the PropertyDeck that really has many panes |
488 | | // that can collapse or expand. For others, limit |
489 | | // the height to something sensible. |
490 | 0 | const sal_Int32 nExtHeight = (msCurrentDeckId == "PropertyDeck" ? 2000 : 600); |
491 | | // No TabBar in LOK (use nWidth in full). |
492 | 0 | mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth, nExtHeight); |
493 | 0 | } |
494 | 0 | else |
495 | 0 | mpCurrentDeck->setPosSizePixel(nDeckX, 0, nWidth - nTabBarDefaultWidth, nHeight); |
496 | 0 | mpCurrentDeck->Show(); |
497 | 0 | mpCurrentDeck->RequestLayout(); |
498 | 0 | mpTabBar->HighlightDeck(mpCurrentDeck->GetId()); |
499 | 0 | } |
500 | 0 | else |
501 | 0 | mpCurrentDeck->Hide(); |
502 | | |
503 | | // Now place the tab bar. |
504 | 0 | mpTabBar->setPosSizePixel(nTabX, 0, nTabBarDefaultWidth, nHeight); |
505 | 0 | if (!comphelper::LibreOfficeKit::isActive()) |
506 | 0 | mpTabBar->Show(); // Don't show TabBar in LOK. |
507 | 0 | } |
508 | | |
509 | | // Determine if the closer of the deck can be shown. |
510 | 0 | sal_Int32 nMinimalWidth = 0; |
511 | 0 | if (mpCurrentDeck && !mpCurrentDeck->isDisposed()) |
512 | 0 | { |
513 | 0 | DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar(); |
514 | 0 | if (pTitleBar && pTitleBar->GetVisible()) |
515 | 0 | pTitleBar->SetCloserVisible(CanModifyChildWindowWidth()); |
516 | 0 | nMinimalWidth = mbMinimumSidebarWidth ? mpCurrentDeck->GetMinimalWidth() : 0; |
517 | 0 | } |
518 | |
|
519 | 0 | RestrictWidth(nMinimalWidth); |
520 | 0 | } |
521 | | |
522 | | void SidebarController::ProcessNewWidth (const sal_Int32 nNewWidth) |
523 | 0 | { |
524 | 0 | if ( ! mbIsDeckRequestedOpen.has_value()) |
525 | 0 | return; |
526 | | |
527 | 0 | if (*mbIsDeckRequestedOpen) |
528 | 0 | { |
529 | | // Deck became large enough to be shown. Show it. |
530 | 0 | mnSavedSidebarWidth = nNewWidth; |
531 | | // Store nNewWidth to mnWidthOnSplitterButtonDown when dragging sidebar Splitter |
532 | 0 | mnWidthOnSplitterButtonDown = nNewWidth; |
533 | 0 | if (!*mbIsDeckOpen) |
534 | 0 | RequestOpenDeck(); |
535 | 0 | } |
536 | 0 | else |
537 | 0 | { |
538 | | // Deck became too small. Close it completely. |
539 | | // If window is wider than the tab bar then mark the deck as being visible, even when it is not. |
540 | | // This is to trigger an adjustment of the width to the width of the tab bar. |
541 | 0 | mbIsDeckOpen = true; |
542 | 0 | RequestCloseDeck(); |
543 | |
|
544 | 0 | if (mnWidthOnSplitterButtonDown > TabBar::GetDefaultWidth()) |
545 | 0 | mnSavedSidebarWidth = mnWidthOnSplitterButtonDown; |
546 | 0 | } |
547 | 0 | } |
548 | | |
549 | | void SidebarController::SyncUpdate() |
550 | 0 | { |
551 | 0 | maPropertyChangeForwarder.Sync(); |
552 | 0 | maContextChangeUpdate.Sync(); |
553 | 0 | } |
554 | | |
555 | | void SidebarController::UpdateConfigurations() |
556 | 0 | { |
557 | 0 | if (maCurrentContext == maRequestedContext |
558 | 0 | && mnRequestedForceFlags == SwitchFlag_NoForce) |
559 | 0 | return; |
560 | | |
561 | 0 | bool bIsLOK = comphelper::LibreOfficeKit::isActive(); |
562 | |
|
563 | 0 | if (!bIsLOK && maCurrentContext.msApplication != "none" && |
564 | 0 | !maCurrentContext.msApplication.isEmpty()) |
565 | 0 | { |
566 | 0 | mpResourceManager->SaveDecksSettings(maCurrentContext); |
567 | 0 | mpResourceManager->SetLastActiveDeck(maCurrentContext, msCurrentDeckId); |
568 | 0 | } |
569 | | |
570 | | // get last active deck for this application on first update |
571 | 0 | if (!maRequestedContext.msApplication.isEmpty() && |
572 | 0 | (maCurrentContext.msApplication != maRequestedContext.msApplication)) |
573 | 0 | { |
574 | 0 | if (bIsLOK) |
575 | 0 | { |
576 | | // LOK has no last-used memory |
577 | 0 | const auto& rOverrides = mpResourceManager->GetDeckOverrides(); |
578 | 0 | const auto aOverride = rOverrides.find(maRequestedContext.msApplication); |
579 | 0 | if (aOverride != rOverrides.end()) |
580 | 0 | msCurrentDeckId = aOverride->second; |
581 | 0 | } |
582 | 0 | else |
583 | 0 | { |
584 | 0 | OUString sLastActiveDeck = mpResourceManager->GetLastActiveDeck( maRequestedContext ); |
585 | 0 | if (!sLastActiveDeck.isEmpty()) |
586 | 0 | msCurrentDeckId = sLastActiveDeck; |
587 | 0 | } |
588 | 0 | } |
589 | |
|
590 | 0 | maCurrentContext = maRequestedContext; |
591 | |
|
592 | 0 | mpResourceManager->InitDeckContext(GetCurrentContext()); |
593 | | |
594 | | // Find the set of decks that could be displayed for the new context. |
595 | 0 | ResourceManager::DeckContextDescriptorContainer aDecks; |
596 | |
|
597 | 0 | css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController(); |
598 | |
|
599 | 0 | mpResourceManager->GetMatchingDecks ( |
600 | 0 | aDecks, |
601 | 0 | maCurrentContext, |
602 | 0 | mbIsDocumentReadOnly, |
603 | 0 | xController); |
604 | |
|
605 | 0 | maFocusManager.Clear(); |
606 | | |
607 | | // Notify the tab bar about the updated set of decks. |
608 | 0 | mpTabBar->SetDecks(aDecks); |
609 | | |
610 | | // Find the new deck. By default that is the same as the old |
611 | | // one. If that is not set or not enabled, then choose the |
612 | | // first enabled deck (which is PropertyDeck). |
613 | 0 | OUString sNewDeckId; |
614 | 0 | for (const auto& rDeck : aDecks) |
615 | 0 | { |
616 | 0 | if (rDeck.mbIsEnabled) |
617 | 0 | { |
618 | 0 | if (rDeck.msId == msCurrentDeckId) |
619 | 0 | { |
620 | 0 | sNewDeckId = msCurrentDeckId; |
621 | 0 | break; |
622 | 0 | } |
623 | 0 | else if (sNewDeckId.getLength() == 0) |
624 | 0 | sNewDeckId = rDeck.msId; |
625 | 0 | } |
626 | 0 | } |
627 | |
|
628 | 0 | if (sNewDeckId.getLength() == 0) |
629 | 0 | { |
630 | | // We did not find a valid deck. |
631 | 0 | RequestCloseDeck(); |
632 | 0 | return; |
633 | 0 | } |
634 | | |
635 | 0 | std::shared_ptr<DeckDescriptor> xDescriptor = mpResourceManager->GetDeckDescriptor(sNewDeckId); |
636 | |
|
637 | 0 | if (xDescriptor) |
638 | 0 | { |
639 | 0 | SwitchToDeck(*xDescriptor, maCurrentContext); |
640 | 0 | } |
641 | 0 | } |
642 | | |
643 | | namespace { |
644 | | |
645 | | void collectUIInformation(const OUString& rDeckId) |
646 | 0 | { |
647 | 0 | EventDescription aDescription; |
648 | 0 | aDescription.aAction = "SIDEBAR"; |
649 | 0 | aDescription.aParent = "MainWindow"; |
650 | 0 | aDescription.aParameters = {{"PANEL", rDeckId}}; |
651 | 0 | aDescription.aKeyWord = "CurrentApp"; |
652 | |
|
653 | 0 | UITestLogger::getInstance().logEvent(aDescription); |
654 | 0 | } |
655 | | |
656 | | } |
657 | | |
658 | 0 | bool SidebarController::IsDocked() const { return !mpParentWindow->IsFloatingMode(); } |
659 | | |
660 | | void SidebarController::OpenThenToggleDeck ( |
661 | | const OUString& rsDeckId) |
662 | 0 | { |
663 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
664 | 0 | if ( pSplitWindow && !pSplitWindow->IsFadeIn() ) |
665 | | // tdf#83546 Collapsed sidebar should expand first |
666 | 0 | pSplitWindow->FadeIn(); |
667 | 0 | else if ( IsDeckVisible( rsDeckId ) ) |
668 | 0 | { |
669 | 0 | if( !WasFloatingDeckClosed() ) |
670 | 0 | { |
671 | | // tdf#88241 Summoning an undocked sidebar a second time should close sidebar |
672 | 0 | mpParentWindow->Close(); |
673 | 0 | return; |
674 | 0 | } |
675 | 0 | else |
676 | 0 | { |
677 | | // tdf#67627 Clicking a second time on a Deck icon will close the Deck |
678 | 0 | RequestCloseDeck(); |
679 | 0 | return; |
680 | 0 | } |
681 | 0 | } |
682 | 0 | RequestOpenDeck(); |
683 | | // before SwitchToDeck which may cause the rsDeckId string to be released |
684 | 0 | collectUIInformation(rsDeckId); |
685 | 0 | SwitchToDeck(rsDeckId); |
686 | | |
687 | | // Make sure the sidebar is wide enough to fit the requested content |
688 | 0 | if (mpCurrentDeck && mpTabBar) |
689 | 0 | { |
690 | 0 | sal_Int32 nRequestedWidth = mpCurrentDeck->GetMinimalWidth() + TabBar::GetDefaultWidth(); |
691 | | // if sidebar was dragged |
692 | 0 | if(mnWidthOnSplitterButtonDown > 0 && mnWidthOnSplitterButtonDown > nRequestedWidth){ |
693 | 0 | SetChildWindowWidth(mnWidthOnSplitterButtonDown); |
694 | 0 | }else{ |
695 | | // tdf#150639 The mnWidthOnSplitterButtonDown is initialized to 0 at program start. |
696 | | // This makes every call to take the else case until the user manually changes the |
697 | | // width, but some decks such as Master Slides have the mnMinimalWidth too low which |
698 | | // makes them too narrow for the content they should display to the user. |
699 | 0 | SetChildWindowWidth(nRequestedWidth > mnSavedSidebarWidth ? nRequestedWidth |
700 | 0 | : mnSavedSidebarWidth); |
701 | 0 | } |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | | void SidebarController::OpenThenSwitchToDeck ( |
706 | | std::u16string_view rsDeckId) |
707 | 0 | { |
708 | 0 | RequestOpenDeck(); |
709 | 0 | SwitchToDeck(rsDeckId); |
710 | |
|
711 | 0 | } |
712 | | |
713 | | void SidebarController::SwitchToDefaultDeck() |
714 | 0 | { |
715 | 0 | SwitchToDeck(gsDefaultDeckId); |
716 | 0 | } |
717 | | |
718 | | void SidebarController::SwitchToDeck ( |
719 | | std::u16string_view rsDeckId) |
720 | 0 | { |
721 | 0 | if ( msCurrentDeckId != rsDeckId |
722 | 0 | || ! mbIsDeckOpen.has_value() |
723 | 0 | || mnRequestedForceFlags!=SwitchFlag_NoForce) |
724 | 0 | { |
725 | 0 | std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rsDeckId); |
726 | |
|
727 | 0 | if (xDeckDescriptor) |
728 | 0 | { |
729 | 0 | SwitchToDeck(*xDeckDescriptor, maCurrentContext); |
730 | 0 | } |
731 | 0 | } |
732 | 0 | } |
733 | | |
734 | 0 | void SidebarController::CreateDeck(std::u16string_view rDeckId) { |
735 | 0 | CreateDeck(rDeckId, maCurrentContext); |
736 | 0 | } |
737 | | |
738 | | void SidebarController::CreateDeck(std::u16string_view rDeckId, const Context& rContext, bool bForceCreate) |
739 | 0 | { |
740 | 0 | std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId); |
741 | |
|
742 | 0 | if (!xDeckDescriptor) |
743 | 0 | return; |
744 | | |
745 | 0 | VclPtr<Deck> aDeck = xDeckDescriptor->mpDeck; |
746 | 0 | if (!aDeck || bForceCreate) |
747 | 0 | { |
748 | 0 | if (aDeck) |
749 | 0 | aDeck.disposeAndClear(); |
750 | |
|
751 | 0 | aDeck = VclPtr<Deck>::Create( |
752 | 0 | *xDeckDescriptor, |
753 | 0 | mpParentWindow, |
754 | 0 | [this]() { return this->RequestCloseDeck(); }); |
755 | 0 | } |
756 | 0 | xDeckDescriptor->mpDeck = std::move(aDeck); |
757 | 0 | CreatePanels(rDeckId, rContext); |
758 | 0 | } |
759 | | |
760 | | void SidebarController::CreatePanels(std::u16string_view rDeckId, const Context& rContext) |
761 | 0 | { |
762 | 0 | std::shared_ptr<DeckDescriptor> xDeckDescriptor = mpResourceManager->GetDeckDescriptor(rDeckId); |
763 | | |
764 | | // init panels bounded to that deck, do not wait them being displayed as may be accessed through API |
765 | |
|
766 | 0 | VclPtr<Deck> pDeck = xDeckDescriptor->mpDeck; |
767 | |
|
768 | 0 | ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors; |
769 | |
|
770 | 0 | css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController(); |
771 | |
|
772 | 0 | mpResourceManager->GetMatchingPanels( |
773 | 0 | aPanelContextDescriptors, |
774 | 0 | rContext, |
775 | 0 | rDeckId, |
776 | 0 | xController); |
777 | | |
778 | | // Update the panel list. |
779 | 0 | const sal_Int32 nNewPanelCount (aPanelContextDescriptors.size()); |
780 | 0 | SharedPanelContainer aNewPanels; |
781 | 0 | sal_Int32 nWriteIndex (0); |
782 | |
|
783 | 0 | aNewPanels.resize(nNewPanelCount); |
784 | |
|
785 | 0 | for (sal_Int32 nReadIndex=0; nReadIndex<nNewPanelCount; ++nReadIndex) |
786 | 0 | { |
787 | 0 | const ResourceManager::PanelContextDescriptor& rPanelContexDescriptor ( |
788 | 0 | aPanelContextDescriptors[nReadIndex]); |
789 | | |
790 | | // Determine if the panel can be displayed. |
791 | 0 | const bool bIsPanelVisible (!mbIsDocumentReadOnly || rPanelContexDescriptor.mbShowForReadOnlyDocuments); |
792 | 0 | if ( ! bIsPanelVisible) |
793 | 0 | continue; |
794 | | |
795 | 0 | auto xOldPanel(pDeck->GetPanel(rPanelContexDescriptor.msId)); |
796 | 0 | if (xOldPanel) |
797 | 0 | { |
798 | 0 | xOldPanel->SetLurkMode(false); |
799 | 0 | aNewPanels[nWriteIndex] = xOldPanel; |
800 | 0 | xOldPanel->SetExpanded(rPanelContexDescriptor.mbIsInitiallyVisible); |
801 | 0 | ++nWriteIndex; |
802 | 0 | } |
803 | 0 | else |
804 | 0 | { |
805 | 0 | auto aPanel = CreatePanel(rPanelContexDescriptor.msId, |
806 | 0 | pDeck->GetPanelParentWindow(), |
807 | 0 | rPanelContexDescriptor.mbIsInitiallyVisible, |
808 | 0 | rContext, |
809 | 0 | pDeck); |
810 | 0 | if (aPanel) |
811 | 0 | { |
812 | 0 | aNewPanels[nWriteIndex] = std::move(aPanel); |
813 | | |
814 | | // Depending on the context we have to change the command |
815 | | // for the "more options" dialog. |
816 | 0 | PanelTitleBar* pTitleBar = aNewPanels[nWriteIndex]->GetTitleBar(); |
817 | 0 | if (pTitleBar) |
818 | 0 | { |
819 | 0 | pTitleBar->SetMoreOptionsCommand( |
820 | 0 | rPanelContexDescriptor.msMenuCommand, |
821 | 0 | mxFrame, xController); |
822 | 0 | } |
823 | 0 | ++nWriteIndex; |
824 | 0 | } |
825 | 0 | } |
826 | 0 | } |
827 | | |
828 | | // mpCurrentPanels - may miss stuff (?) |
829 | 0 | aNewPanels.resize(nWriteIndex); |
830 | 0 | pDeck->ResetPanels(std::move(aNewPanels)); |
831 | 0 | } |
832 | | |
833 | | void SidebarController::SwitchToDeck ( |
834 | | const DeckDescriptor& rDeckDescriptor, |
835 | | const Context& rContext) |
836 | 0 | { |
837 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
838 | 0 | { |
839 | 0 | if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell()) |
840 | 0 | { |
841 | 0 | std::vector<std::pair<std::string, std::string>> aStateChanges; |
842 | 0 | if (msCurrentDeckId != rDeckDescriptor.msId) |
843 | 0 | { |
844 | 0 | const std::string hide = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext()); |
845 | 0 | if (!hide.empty()) |
846 | 0 | { |
847 | 0 | aStateChanges.push_back({hide, std::string("false")}); |
848 | 0 | } |
849 | 0 | } |
850 | |
|
851 | 0 | const std::string show = UnoNameFromDeckId(rDeckDescriptor.msId, GetCurrentContext()); |
852 | 0 | if (!show.empty()) |
853 | 0 | { |
854 | 0 | aStateChanges.push_back({show, std::string("true")}); |
855 | 0 | } |
856 | |
|
857 | 0 | for (const auto& rStateChange : aStateChanges) |
858 | 0 | { |
859 | 0 | boost::property_tree::ptree aTree; |
860 | 0 | aTree.put("locale", comphelper::LibreOfficeKit::getLocale().getBcp47()); |
861 | 0 | aTree.put("commandName", rStateChange.first); |
862 | 0 | aTree.put("state", rStateChange.second); |
863 | 0 | std::stringstream aStream; |
864 | 0 | boost::property_tree::write_json(aStream, aTree); |
865 | 0 | pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, |
866 | 0 | OString(aStream.str())); |
867 | 0 | } |
868 | 0 | } |
869 | 0 | } |
870 | |
|
871 | 0 | maFocusManager.Clear(); |
872 | |
|
873 | 0 | const bool bForceNewDeck ((mnRequestedForceFlags&SwitchFlag_ForceNewDeck)!=0); |
874 | 0 | const bool bForceNewPanels ((mnRequestedForceFlags&SwitchFlag_ForceNewPanels)!=0); |
875 | 0 | mnRequestedForceFlags = SwitchFlag_NoForce; |
876 | |
|
877 | 0 | if ( msCurrentDeckId != rDeckDescriptor.msId |
878 | 0 | || bForceNewDeck) |
879 | 0 | { |
880 | 0 | if (mpCurrentDeck) |
881 | 0 | mpCurrentDeck->Hide(); |
882 | |
|
883 | 0 | msCurrentDeckId = rDeckDescriptor.msId; |
884 | 0 | } |
885 | | |
886 | | // Determine the panels to display in the deck. |
887 | 0 | ResourceManager::PanelContextDescriptorContainer aPanelContextDescriptors; |
888 | |
|
889 | 0 | css::uno::Reference<css::frame::XController> xController = mxCurrentController.is() ? mxCurrentController : mxFrame->getController(); |
890 | |
|
891 | 0 | mpResourceManager->GetMatchingPanels( |
892 | 0 | aPanelContextDescriptors, |
893 | 0 | rContext, |
894 | 0 | rDeckDescriptor.msId, |
895 | 0 | xController); |
896 | |
|
897 | 0 | if (aPanelContextDescriptors.empty()) |
898 | 0 | { |
899 | | // There are no panels to be displayed in the current context. |
900 | 0 | if (vcl::EnumContext::GetContextEnum(rContext.msContext) != vcl::EnumContext::Context::Empty) |
901 | 0 | { |
902 | | // Switch to the "empty" context and try again. |
903 | 0 | SwitchToDeck( |
904 | 0 | rDeckDescriptor, |
905 | 0 | Context( |
906 | 0 | rContext.msApplication, |
907 | 0 | vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Empty))); |
908 | 0 | return; |
909 | 0 | } |
910 | 0 | else |
911 | 0 | { |
912 | | // This is already the "empty" context. Looks like we have |
913 | | // to live with an empty deck. |
914 | 0 | } |
915 | 0 | } |
916 | | |
917 | | // Provide a configuration and Deck object. |
918 | | |
919 | 0 | CreateDeck(rDeckDescriptor.msId, rContext, bForceNewDeck); |
920 | |
|
921 | 0 | if (bForceNewPanels && !bForceNewDeck) // already forced if bForceNewDeck |
922 | 0 | CreatePanels(rDeckDescriptor.msId, rContext); |
923 | |
|
924 | 0 | if (mpCurrentDeck && mpCurrentDeck != rDeckDescriptor.mpDeck) |
925 | 0 | mpCurrentDeck->Hide(); |
926 | 0 | mpCurrentDeck.reset(rDeckDescriptor.mpDeck); |
927 | |
|
928 | 0 | if ( ! mpCurrentDeck) |
929 | 0 | return; |
930 | | |
931 | | #if OSL_DEBUG_LEVEL >= 2 |
932 | | // Show the context name in the deck title bar. |
933 | | DeckTitleBar* pDebugTitleBar = mpCurrentDeck->GetTitleBar(); |
934 | | if (pDebugTitleBar) |
935 | | pDebugTitleBar->SetTitle(rDeckDescriptor.msTitle + " (" + maCurrentContext.msContext + ")"); |
936 | | #endif |
937 | | |
938 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
939 | 0 | sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth(); |
940 | 0 | WindowAlign eAlign = pSplitWindow ? pSplitWindow->GetAlign() : WindowAlign::Right; |
941 | 0 | tools::Long nDeckX; |
942 | 0 | if (eAlign == WindowAlign::Left) // attach the Sidebar towards the left-side of screen |
943 | 0 | { |
944 | 0 | nDeckX = nTabBarDefaultWidth; |
945 | 0 | } |
946 | 0 | else // attach the Sidebar towards the right-side of screen |
947 | 0 | { |
948 | 0 | nDeckX = 0; |
949 | 0 | } |
950 | | |
951 | | // Activate the deck and the new set of panels. |
952 | 0 | mpCurrentDeck->setPosSizePixel( |
953 | 0 | nDeckX, |
954 | 0 | 0, |
955 | 0 | mpParentWindow->GetSizePixel().Width() - nTabBarDefaultWidth, |
956 | 0 | mpParentWindow->GetSizePixel().Height()); |
957 | |
|
958 | 0 | mpCurrentDeck->Show(); |
959 | |
|
960 | 0 | mpParentWindow->SetText(rDeckDescriptor.msTitle); |
961 | |
|
962 | 0 | NotifyResize(); |
963 | | |
964 | | // Tell the focus manager about the new panels and tab bar |
965 | | // buttons. |
966 | 0 | maFocusManager.SetDeck(mpCurrentDeck); |
967 | 0 | maFocusManager.SetPanels(mpCurrentDeck->GetPanels()); |
968 | |
|
969 | 0 | mpTabBar->UpdateFocusManager(maFocusManager); |
970 | 0 | UpdateTitleBarIcons(); |
971 | 0 | } |
972 | | |
973 | | void SidebarController::notifyDeckTitle(std::u16string_view targetDeckId) |
974 | 0 | { |
975 | 0 | if (msCurrentDeckId == targetDeckId) |
976 | 0 | { |
977 | 0 | maFocusManager.SetDeck(mpCurrentDeck); |
978 | 0 | mpTabBar->UpdateFocusManager(maFocusManager); |
979 | 0 | UpdateTitleBarIcons(); |
980 | 0 | } |
981 | 0 | } |
982 | | |
983 | | std::shared_ptr<Panel> SidebarController::CreatePanel ( |
984 | | std::u16string_view rsPanelId, |
985 | | weld::Widget* pParentWindow, |
986 | | const bool bIsInitiallyExpanded, |
987 | | const Context& rContext, |
988 | | const VclPtr<Deck>& pDeck) |
989 | 0 | { |
990 | 0 | std::shared_ptr<PanelDescriptor> xPanelDescriptor = mpResourceManager->GetPanelDescriptor(rsPanelId); |
991 | |
|
992 | 0 | if (!xPanelDescriptor) |
993 | 0 | return nullptr; |
994 | | |
995 | | // Create the panel which is the parent window of the UIElement. |
996 | 0 | auto xPanel = std::make_shared<Panel>( |
997 | 0 | *xPanelDescriptor, |
998 | 0 | pParentWindow, |
999 | 0 | bIsInitiallyExpanded, |
1000 | 0 | pDeck, |
1001 | 0 | [this]() { return this->GetCurrentContext(); }, |
1002 | 0 | mxFrame); |
1003 | | |
1004 | | // Create the XUIElement. |
1005 | 0 | Reference<ui::XUIElement> xUIElement (CreateUIElement( |
1006 | 0 | xPanel->GetElementParentWindow(), |
1007 | 0 | xPanelDescriptor->msImplementationURL, |
1008 | 0 | xPanelDescriptor->mbWantsCanvas, |
1009 | 0 | rContext)); |
1010 | 0 | if (xUIElement.is()) |
1011 | 0 | { |
1012 | | // Initialize the panel and add it to the active deck. |
1013 | 0 | xPanel->SetUIElement(xUIElement); |
1014 | 0 | } |
1015 | 0 | else |
1016 | 0 | { |
1017 | 0 | xPanel.reset(); |
1018 | 0 | } |
1019 | |
|
1020 | 0 | return xPanel; |
1021 | 0 | } |
1022 | | |
1023 | | Reference<ui::XUIElement> SidebarController::CreateUIElement ( |
1024 | | const Reference<awt::XWindow>& rxWindow, |
1025 | | const OUString& rsImplementationURL, |
1026 | | const bool bWantsCanvas, |
1027 | | const Context& rContext) |
1028 | 0 | { |
1029 | 0 | try |
1030 | 0 | { |
1031 | 0 | const Reference<XComponentContext>& xComponentContext (::comphelper::getProcessComponentContext() ); |
1032 | 0 | const Reference<ui::XUIElementFactory> xUIElementFactory = |
1033 | 0 | ui::theUIElementFactoryManager::get( xComponentContext ); |
1034 | | |
1035 | | // Create the XUIElement. |
1036 | 0 | ::comphelper::NamedValueCollection aCreationArguments; |
1037 | 0 | aCreationArguments.put(u"Frame"_ustr, Any(mxFrame)); |
1038 | 0 | aCreationArguments.put(u"ParentWindow"_ustr, Any(rxWindow)); |
1039 | 0 | SidebarDockingWindow* pSfxDockingWindow = mpParentWindow.get(); |
1040 | 0 | if (pSfxDockingWindow != nullptr) |
1041 | 0 | aCreationArguments.put(u"SfxBindings"_ustr, Any(reinterpret_cast<sal_uInt64>(&pSfxDockingWindow->GetBindings()))); |
1042 | 0 | aCreationArguments.put(u"Theme"_ustr, Theme::GetPropertySet()); |
1043 | 0 | aCreationArguments.put(u"Sidebar"_ustr, Any(Reference<ui::XSidebar>(static_cast<ui::XSidebar*>(this)))); |
1044 | 0 | if (bWantsCanvas) |
1045 | 0 | { |
1046 | 0 | Reference<rendering::XSpriteCanvas> xCanvas (VCLUnoHelper::GetWindow(rxWindow)->GetOutDev()->GetSpriteCanvas()); |
1047 | 0 | aCreationArguments.put(u"Canvas"_ustr, Any(xCanvas)); |
1048 | 0 | } |
1049 | |
|
1050 | 0 | if (mxCurrentController.is()) |
1051 | 0 | { |
1052 | 0 | OUString aModule = Tools::GetModuleName(mxCurrentController); |
1053 | 0 | if (!aModule.isEmpty()) |
1054 | 0 | aCreationArguments.put(u"Module"_ustr, Any(aModule)); |
1055 | | // See also sfx2::sidebar::GetFrame. Maybe we should always create |
1056 | | // uielements with the Controller's getFrame as the XFrame. For now |
1057 | | // set both XFrame and Controller and selectively get the XFrame |
1058 | | // from the Controller. |
1059 | 0 | aCreationArguments.put(u"Controller"_ustr, Any(mxCurrentController)); |
1060 | 0 | } |
1061 | |
|
1062 | 0 | aCreationArguments.put(u"ApplicationName"_ustr, Any(rContext.msApplication)); |
1063 | 0 | aCreationArguments.put(u"ContextName"_ustr, Any(rContext.msContext)); |
1064 | |
|
1065 | 0 | Reference<ui::XUIElement> xUIElement( |
1066 | 0 | xUIElementFactory->createUIElement( |
1067 | 0 | rsImplementationURL, |
1068 | 0 | aCreationArguments.getPropertyValues()), |
1069 | 0 | UNO_SET_THROW); |
1070 | |
|
1071 | 0 | return xUIElement; |
1072 | 0 | } |
1073 | 0 | catch(const Exception&) |
1074 | 0 | { |
1075 | 0 | TOOLS_WARN_EXCEPTION("sfx.sidebar", "Cannot create panel " << rsImplementationURL); |
1076 | 0 | return nullptr; |
1077 | 0 | } |
1078 | 0 | } |
1079 | | |
1080 | | IMPL_LINK(SidebarController, WindowEventHandler, VclWindowEvent&, rEvent, void) |
1081 | 0 | { |
1082 | 0 | if (rEvent.GetWindow() == mpParentWindow) |
1083 | 0 | { |
1084 | 0 | switch (rEvent.GetId()) |
1085 | 0 | { |
1086 | 0 | case VclEventId::WindowShow: |
1087 | 0 | case VclEventId::WindowResize: |
1088 | 0 | NotifyResize(); |
1089 | 0 | break; |
1090 | | |
1091 | 0 | case VclEventId::WindowDataChanged: |
1092 | | // Force an update of deck and tab bar to reflect |
1093 | | // changes in theme (high contrast mode). |
1094 | 0 | Theme::HandleDataChange(); |
1095 | 0 | UpdateTitleBarIcons(); |
1096 | 0 | mpParentWindow->Invalidate(); |
1097 | 0 | mnRequestedForceFlags |= SwitchFlag_ForceNewDeck | SwitchFlag_ForceNewPanels; |
1098 | 0 | maContextChangeUpdate.RequestCall(); |
1099 | 0 | break; |
1100 | | |
1101 | 0 | case VclEventId::WindowToggleFloating: |
1102 | | // make sure the appropriate "Dock" or "Undock" menu entry is shown |
1103 | 0 | mpTabBar->UpdateMenus(); |
1104 | 0 | break; |
1105 | | |
1106 | 0 | case VclEventId::ObjectDying: |
1107 | 0 | dispose(); |
1108 | 0 | break; |
1109 | | |
1110 | 0 | case VclEventId::WindowPaint: |
1111 | 0 | SAL_INFO("sfx.sidebar", "Paint"); |
1112 | 0 | break; |
1113 | | |
1114 | 0 | default: |
1115 | 0 | break; |
1116 | 0 | } |
1117 | 0 | } |
1118 | 0 | else if (rEvent.GetWindow()==mpSplitWindow && mpSplitWindow!=nullptr) |
1119 | 0 | { |
1120 | 0 | switch (rEvent.GetId()) |
1121 | 0 | { |
1122 | 0 | case VclEventId::WindowMouseButtonDown: |
1123 | 0 | mnWidthOnSplitterButtonDown = mpParentWindow->GetSizePixel().Width(); |
1124 | 0 | break; |
1125 | | |
1126 | 0 | case VclEventId::WindowMouseButtonUp: |
1127 | 0 | { |
1128 | 0 | ProcessNewWidth(mpParentWindow->GetSizePixel().Width()); |
1129 | 0 | break; |
1130 | 0 | } |
1131 | | |
1132 | 0 | case VclEventId::ObjectDying: |
1133 | 0 | dispose(); |
1134 | 0 | break; |
1135 | | |
1136 | 0 | default: break; |
1137 | 0 | } |
1138 | 0 | } |
1139 | 0 | } |
1140 | | |
1141 | | void SidebarController::ConnectMenuActivateHandlers(weld::Menu& rMainMenu, weld::Menu& rSubMenu) const |
1142 | 0 | { |
1143 | 0 | rMainMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnMenuItemSelected)); |
1144 | 0 | rSubMenu.connect_activate(LINK(const_cast<SidebarController*>(this), SidebarController, OnSubMenuItemSelected)); |
1145 | 0 | } |
1146 | | |
1147 | | IMPL_LINK(SidebarController, OnMenuItemSelected, const OUString&, rCurItemId, void) |
1148 | 0 | { |
1149 | 0 | if (rCurItemId == "unlocktaskpanel") |
1150 | 0 | { |
1151 | 0 | mpParentWindow->SetFloatingMode(true); |
1152 | 0 | if (mpParentWindow->IsFloatingMode()) |
1153 | 0 | mpParentWindow->ToTop(ToTopFlags::GrabFocusOnly); |
1154 | 0 | } |
1155 | 0 | else if (rCurItemId == "locktaskpanel") |
1156 | 0 | { |
1157 | 0 | mpParentWindow->SetFloatingMode(false); |
1158 | 0 | } |
1159 | 0 | else if (rCurItemId == "hidesidebar") |
1160 | 0 | { |
1161 | 0 | if (!comphelper::LibreOfficeKit::isActive()) |
1162 | 0 | { |
1163 | 0 | const util::URL aURL(Tools::GetURL(u".uno:Sidebar"_ustr)); |
1164 | 0 | Reference<frame::XDispatch> xDispatch(Tools::GetDispatch(mxFrame, aURL)); |
1165 | 0 | if (xDispatch.is()) |
1166 | 0 | xDispatch->dispatch(aURL, Sequence<beans::PropertyValue>()); |
1167 | 0 | } |
1168 | 0 | else |
1169 | 0 | { |
1170 | | // In LOK we don't really destroy the sidebar when "closing"; |
1171 | | // we simply hide it. This is because recreating it is problematic |
1172 | | // See notes in SidebarDockingWindow::NotifyResize(). |
1173 | 0 | RequestCloseDeck(); |
1174 | 0 | } |
1175 | 0 | } |
1176 | 0 | else |
1177 | 0 | { |
1178 | 0 | try |
1179 | 0 | { |
1180 | 0 | std::u16string_view sNumber; |
1181 | 0 | if (rCurItemId.startsWith("select", &sNumber)) |
1182 | 0 | { |
1183 | 0 | RequestOpenDeck(); |
1184 | 0 | SwitchToDeck(mpTabBar->GetDeckIdForIndex(o3tl::toInt32(sNumber))); |
1185 | 0 | } |
1186 | 0 | mpParentWindow->GrabFocusToDocument(); |
1187 | 0 | } |
1188 | 0 | catch (RuntimeException&) |
1189 | 0 | { |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | } |
1193 | | |
1194 | | IMPL_LINK(SidebarController, OnSubMenuItemSelected, const OUString&, rCurItemId, void) |
1195 | 0 | { |
1196 | 0 | try |
1197 | 0 | { |
1198 | 0 | std::u16string_view sNumber; |
1199 | 0 | if (rCurItemId.startsWith("customize", &sNumber)) |
1200 | 0 | { |
1201 | 0 | mpTabBar->ToggleHideFlag(o3tl::toInt32(sNumber)); |
1202 | | |
1203 | | // Find the set of decks that could be displayed for the new context. |
1204 | 0 | ResourceManager::DeckContextDescriptorContainer aDecks; |
1205 | 0 | mpResourceManager->GetMatchingDecks ( |
1206 | 0 | aDecks, |
1207 | 0 | GetCurrentContext(), |
1208 | 0 | IsDocumentReadOnly(), |
1209 | 0 | mxFrame->getController()); |
1210 | | // Notify the tab bar about the updated set of decks. |
1211 | 0 | maFocusManager.Clear(); |
1212 | 0 | mpTabBar->SetDecks(aDecks); |
1213 | 0 | mpTabBar->HighlightDeck(mpCurrentDeck->GetId()); |
1214 | 0 | mpTabBar->UpdateFocusManager(maFocusManager); |
1215 | 0 | } |
1216 | 0 | mpParentWindow->GrabFocusToDocument(); |
1217 | 0 | } |
1218 | 0 | catch (RuntimeException&) |
1219 | 0 | { |
1220 | 0 | } |
1221 | 0 | } |
1222 | | |
1223 | | |
1224 | | void SidebarController::RequestCloseDeck() |
1225 | 0 | { |
1226 | 0 | mbIsDeckRequestedOpen = false; |
1227 | 0 | UpdateDeckOpenState(); |
1228 | |
|
1229 | 0 | mpTabBar->RemoveDeckHighlight(); |
1230 | 0 | } |
1231 | | |
1232 | | void SidebarController::RequestOpenDeck() |
1233 | 0 | { |
1234 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
1235 | 0 | if ( pSplitWindow && !pSplitWindow->IsFadeIn() ) |
1236 | | // tdf#83546 Collapsed sidebar should expand first |
1237 | 0 | pSplitWindow->FadeIn(); |
1238 | |
|
1239 | 0 | mbIsDeckRequestedOpen = true; |
1240 | 0 | UpdateDeckOpenState(); |
1241 | 0 | } |
1242 | | |
1243 | | bool SidebarController::IsDeckOpen(const sal_Int32 nIndex) |
1244 | 0 | { |
1245 | 0 | if (nIndex >= 0) |
1246 | 0 | { |
1247 | 0 | OUString asDeckId(mpTabBar->GetDeckIdForIndex(nIndex)); |
1248 | 0 | return IsDeckVisible(asDeckId); |
1249 | 0 | } |
1250 | 0 | return mbIsDeckOpen.has_value() && *mbIsDeckOpen; |
1251 | 0 | } |
1252 | | |
1253 | | bool SidebarController::IsDeckVisible(std::u16string_view rsDeckId) |
1254 | 0 | { |
1255 | 0 | return mbIsDeckOpen.has_value() && *mbIsDeckOpen && msCurrentDeckId == rsDeckId; |
1256 | 0 | } |
1257 | | |
1258 | | void SidebarController::UpdateDeckOpenState() |
1259 | 0 | { |
1260 | 0 | if ( ! mbIsDeckRequestedOpen.has_value() ) |
1261 | | // No state requested. |
1262 | 0 | return; |
1263 | | |
1264 | 0 | const sal_Int32 nTabBarDefaultWidth = TabBar::GetDefaultWidth(); |
1265 | | |
1266 | | // Update (change) the open state when it either has not yet been initialized |
1267 | | // or when its value differs from the requested state. |
1268 | 0 | if ( mbIsDeckOpen.has_value() && *mbIsDeckOpen == *mbIsDeckRequestedOpen ) |
1269 | 0 | return; |
1270 | | |
1271 | 0 | if (*mbIsDeckRequestedOpen) |
1272 | 0 | { |
1273 | 0 | if (!mpParentWindow->IsFloatingMode()) |
1274 | 0 | { |
1275 | 0 | if (mnSavedSidebarWidth <= nTabBarDefaultWidth) |
1276 | 0 | SetChildWindowWidth(SidebarChildWindow::GetDefaultWidth(mpParentWindow)); |
1277 | 0 | else |
1278 | 0 | SetChildWindowWidth(mnSavedSidebarWidth); |
1279 | 0 | } |
1280 | 0 | else |
1281 | 0 | { |
1282 | | // Show the Deck by resizing back to the original size (before hiding). |
1283 | 0 | Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel()); |
1284 | 0 | Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel()); |
1285 | |
|
1286 | 0 | aNewPos.setX(aNewPos.X() - mnSavedSidebarWidth + nTabBarDefaultWidth); |
1287 | 0 | aNewSize.setWidth(mnSavedSidebarWidth); |
1288 | |
|
1289 | 0 | mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize); |
1290 | |
|
1291 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
1292 | 0 | { |
1293 | | // Sidebar wide enough to render the menu; enable it. |
1294 | 0 | mpTabBar->EnableMenuButton(true); |
1295 | |
|
1296 | 0 | if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell()) |
1297 | 0 | { |
1298 | 0 | const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext()); |
1299 | 0 | if (!uno.empty()) |
1300 | 0 | pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, |
1301 | 0 | OString(uno + "=true")); |
1302 | 0 | } |
1303 | 0 | } |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | else |
1307 | 0 | { |
1308 | 0 | if ( ! mpParentWindow->IsFloatingMode()) |
1309 | 0 | mnSavedSidebarWidth = SetChildWindowWidth(nTabBarDefaultWidth); |
1310 | 0 | else |
1311 | 0 | { |
1312 | | // Hide the Deck by resizing to the width of the TabBar. |
1313 | 0 | Size aNewSize(mpParentWindow->GetFloatingWindow()->GetSizePixel()); |
1314 | 0 | Point aNewPos(mpParentWindow->GetFloatingWindow()->GetPosPixel()); |
1315 | 0 | mnSavedSidebarWidth = aNewSize.Width(); // Save the current width to restore. |
1316 | |
|
1317 | 0 | aNewPos.setX(aNewPos.X() + mnSavedSidebarWidth - nTabBarDefaultWidth); |
1318 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
1319 | 0 | { |
1320 | | // Hide by collapsing, otherwise with 0x0 the client might expect |
1321 | | // to get valid dimensions on rendering and not collapse the sidebar. |
1322 | 0 | aNewSize.setWidth(1); |
1323 | 0 | } |
1324 | 0 | else |
1325 | 0 | aNewSize.setWidth(nTabBarDefaultWidth); |
1326 | |
|
1327 | 0 | mpParentWindow->GetFloatingWindow()->SetPosSizePixel(aNewPos, aNewSize); |
1328 | |
|
1329 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
1330 | 0 | { |
1331 | | // Sidebar too narrow to render the menu; disable it. |
1332 | 0 | mpTabBar->EnableMenuButton(false); |
1333 | |
|
1334 | 0 | if (const SfxViewShell* pViewShell = mpViewFrame->GetViewShell()) |
1335 | 0 | { |
1336 | 0 | const std::string uno = UnoNameFromDeckId(msCurrentDeckId, GetCurrentContext()); |
1337 | 0 | if (!uno.empty()) |
1338 | 0 | pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, |
1339 | 0 | OString(uno + "=false")); |
1340 | 0 | } |
1341 | 0 | } |
1342 | 0 | } |
1343 | |
|
1344 | 0 | if (mnWidthOnSplitterButtonDown > nTabBarDefaultWidth) |
1345 | 0 | mnSavedSidebarWidth = mnWidthOnSplitterButtonDown; |
1346 | 0 | mpParentWindow->SetStyle(mpParentWindow->GetStyle() & ~WB_SIZEABLE); |
1347 | 0 | } |
1348 | |
|
1349 | 0 | NotifyResize(); |
1350 | 0 | } |
1351 | | |
1352 | | bool SidebarController::CanModifyChildWindowWidth() |
1353 | 0 | { |
1354 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
1355 | 0 | if (pSplitWindow == nullptr) |
1356 | 0 | return false; |
1357 | | |
1358 | 0 | sal_uInt16 nRow (0xffff); |
1359 | 0 | sal_uInt16 nColumn (0xffff); |
1360 | 0 | if (pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow)) |
1361 | 0 | { |
1362 | 0 | sal_uInt16 nRowCount (pSplitWindow->GetWindowCount(nColumn)); |
1363 | 0 | return nRowCount==1; |
1364 | 0 | } |
1365 | 0 | else |
1366 | 0 | return false; |
1367 | 0 | } |
1368 | | |
1369 | | sal_Int32 SidebarController::SetChildWindowWidth (const sal_Int32 nNewWidth) |
1370 | 0 | { |
1371 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
1372 | 0 | if (pSplitWindow == nullptr) |
1373 | 0 | return 0; |
1374 | | |
1375 | 0 | sal_uInt16 nRow (0xffff); |
1376 | 0 | sal_uInt16 nColumn (0xffff); |
1377 | 0 | pSplitWindow->GetWindowPos(mpParentWindow, nColumn, nRow); |
1378 | 0 | const tools::Long nColumnWidth (pSplitWindow->GetLineSize(nColumn)); |
1379 | |
|
1380 | 0 | vcl::Window* pWindow = mpParentWindow; |
1381 | 0 | const Size aWindowSize (pWindow->GetSizePixel()); |
1382 | |
|
1383 | 0 | pSplitWindow->MoveWindow( |
1384 | 0 | mpParentWindow, |
1385 | 0 | Size(nNewWidth, aWindowSize.Height()), |
1386 | 0 | nColumn, |
1387 | 0 | nRow, |
1388 | 0 | false); |
1389 | 0 | static_cast<SplitWindow*>(pSplitWindow)->Split(); |
1390 | |
|
1391 | 0 | return static_cast<sal_Int32>(nColumnWidth); |
1392 | 0 | } |
1393 | | |
1394 | | void SidebarController::RestrictWidth (sal_Int32 nWidth) |
1395 | 0 | { |
1396 | 0 | SfxSplitWindow* pSplitWindow = GetSplitWindow(); |
1397 | 0 | if (pSplitWindow != nullptr) |
1398 | 0 | { |
1399 | 0 | const sal_uInt16 nId (pSplitWindow->GetItemId(mpParentWindow.get())); |
1400 | 0 | const sal_uInt16 nSetId (pSplitWindow->GetSet(nId)); |
1401 | 0 | const sal_Int32 nRequestedWidth = TabBar::GetDefaultWidth() + nWidth; |
1402 | |
|
1403 | 0 | pSplitWindow->SetItemSizeRange( |
1404 | 0 | nSetId, |
1405 | 0 | Range(nRequestedWidth, std::max(nRequestedWidth, getMaximumWidth()))); |
1406 | 0 | } |
1407 | 0 | } |
1408 | | |
1409 | | SfxSplitWindow* SidebarController::GetSplitWindow() |
1410 | 0 | { |
1411 | 0 | if (mpParentWindow != nullptr) |
1412 | 0 | { |
1413 | 0 | SfxSplitWindow* pSplitWindow = dynamic_cast<SfxSplitWindow*>(mpParentWindow->GetParent()); |
1414 | 0 | if (pSplitWindow != mpSplitWindow) |
1415 | 0 | { |
1416 | 0 | if (mpSplitWindow != nullptr) |
1417 | 0 | mpSplitWindow->RemoveEventListener(LINK(this, SidebarController, WindowEventHandler)); |
1418 | |
|
1419 | 0 | mpSplitWindow = pSplitWindow; |
1420 | |
|
1421 | 0 | if (mpSplitWindow != nullptr) |
1422 | 0 | mpSplitWindow->AddEventListener(LINK(this, SidebarController, WindowEventHandler)); |
1423 | 0 | } |
1424 | 0 | return mpSplitWindow; |
1425 | 0 | } |
1426 | 0 | else |
1427 | 0 | return nullptr; |
1428 | 0 | } |
1429 | | |
1430 | | void SidebarController::UpdateCloseIndicator (const bool bCloseAfterDrag) |
1431 | 0 | { |
1432 | 0 | if (mpParentWindow == nullptr) |
1433 | 0 | return; |
1434 | | |
1435 | 0 | if (bCloseAfterDrag) |
1436 | 0 | { |
1437 | | // Make sure that the indicator exists. |
1438 | 0 | if (!mpCloseIndicator) |
1439 | 0 | mpCloseIndicator.reset(VclPtr<CloseIndicator>::Create(mpParentWindow)); |
1440 | | |
1441 | | // Place and show the indicator. |
1442 | 0 | const Size aWindowSize (mpParentWindow->GetSizePixel()); |
1443 | 0 | const Size aImageSize (mpCloseIndicator->GetSizePixel()); |
1444 | 0 | mpCloseIndicator->SetPosPixel( |
1445 | 0 | Point( |
1446 | 0 | aWindowSize.Width() - TabBar::GetDefaultWidth() - aImageSize.Width(), |
1447 | 0 | (aWindowSize.Height() - aImageSize.Height())/2)); |
1448 | 0 | mpCloseIndicator->Show(); |
1449 | 0 | } |
1450 | 0 | else |
1451 | 0 | { |
1452 | | // Hide but don't delete the indicator. |
1453 | 0 | if (mpCloseIndicator) |
1454 | 0 | mpCloseIndicator->Hide(); |
1455 | 0 | } |
1456 | 0 | } |
1457 | | |
1458 | | void SidebarController::UpdateTitleBarIcons() |
1459 | 0 | { |
1460 | 0 | if ( ! mpCurrentDeck) |
1461 | 0 | return; |
1462 | | |
1463 | 0 | const bool bIsHighContrastModeActive (Theme::IsHighContrastMode()); |
1464 | |
|
1465 | 0 | const ResourceManager& rResourceManager = *mpResourceManager; |
1466 | | |
1467 | | // Update the deck icon. |
1468 | 0 | std::shared_ptr<DeckDescriptor> xDeckDescriptor = rResourceManager.GetDeckDescriptor(mpCurrentDeck->GetId()); |
1469 | 0 | if (xDeckDescriptor && mpCurrentDeck->GetTitleBar()) |
1470 | 0 | { |
1471 | 0 | const OUString sIconURL( |
1472 | 0 | bIsHighContrastModeActive |
1473 | 0 | ? xDeckDescriptor->msHighContrastTitleBarIconURL |
1474 | 0 | : xDeckDescriptor->msTitleBarIconURL); |
1475 | 0 | mpCurrentDeck->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame)); |
1476 | 0 | } |
1477 | | |
1478 | | // Update the panel icons. |
1479 | 0 | const SharedPanelContainer& rPanels (mpCurrentDeck->GetPanels()); |
1480 | 0 | for (const auto& rxPanel : rPanels) |
1481 | 0 | { |
1482 | 0 | if ( ! rxPanel) |
1483 | 0 | continue; |
1484 | 0 | if (!rxPanel->GetTitleBar()) |
1485 | 0 | continue; |
1486 | 0 | std::shared_ptr<PanelDescriptor> xPanelDescriptor = rResourceManager.GetPanelDescriptor(rxPanel->GetId()); |
1487 | 0 | if (!xPanelDescriptor) |
1488 | 0 | continue; |
1489 | 0 | const OUString sIconURL ( |
1490 | 0 | bIsHighContrastModeActive |
1491 | 0 | ? xPanelDescriptor->msHighContrastTitleBarIconURL |
1492 | 0 | : xPanelDescriptor->msTitleBarIconURL); |
1493 | 0 | rxPanel->GetTitleBar()->SetIcon(Tools::GetImage(sIconURL, mxFrame)); |
1494 | 0 | } |
1495 | 0 | } |
1496 | | |
1497 | | void SidebarController::ShowPanel (const Panel& rPanel) |
1498 | 0 | { |
1499 | 0 | if (mpCurrentDeck) |
1500 | 0 | { |
1501 | 0 | if (!IsDeckOpen()) |
1502 | 0 | RequestOpenDeck(); |
1503 | 0 | mpCurrentDeck->ShowPanel(rPanel); |
1504 | 0 | } |
1505 | 0 | } |
1506 | | |
1507 | | ResourceManager::DeckContextDescriptorContainer SidebarController::GetMatchingDecks() |
1508 | 0 | { |
1509 | 0 | ResourceManager::DeckContextDescriptorContainer aDecks; |
1510 | 0 | mpResourceManager->GetMatchingDecks (aDecks, |
1511 | 0 | GetCurrentContext(), |
1512 | 0 | IsDocumentReadOnly(), |
1513 | 0 | mxFrame->getController()); |
1514 | 0 | return aDecks; |
1515 | 0 | } |
1516 | | |
1517 | | ResourceManager::PanelContextDescriptorContainer SidebarController::GetMatchingPanels(std::u16string_view rDeckId) |
1518 | 0 | { |
1519 | 0 | ResourceManager::PanelContextDescriptorContainer aPanels; |
1520 | |
|
1521 | 0 | mpResourceManager->GetMatchingPanels(aPanels, |
1522 | 0 | GetCurrentContext(), |
1523 | 0 | rDeckId, |
1524 | 0 | mxFrame->getController()); |
1525 | 0 | return aPanels; |
1526 | 0 | } |
1527 | | |
1528 | | void SidebarController::updateModel(const css::uno::Reference<css::frame::XModel>& xModel) |
1529 | 0 | { |
1530 | 0 | mpResourceManager->UpdateModel(xModel); |
1531 | 0 | } |
1532 | | |
1533 | | void SidebarController::FadeOut() |
1534 | 0 | { |
1535 | 0 | if (mpSplitWindow) |
1536 | 0 | mpSplitWindow->FadeOut(); |
1537 | 0 | } |
1538 | | |
1539 | | void SidebarController::FadeIn() |
1540 | 0 | { |
1541 | 0 | if (mpSplitWindow) |
1542 | 0 | mpSplitWindow->FadeIn(); |
1543 | 0 | } |
1544 | | |
1545 | | tools::Rectangle SidebarController::GetDeckDragArea() const |
1546 | 0 | { |
1547 | 0 | tools::Rectangle aRect; |
1548 | 0 | if (mpCurrentDeck) |
1549 | 0 | { |
1550 | 0 | if (DeckTitleBar* pTitleBar = mpCurrentDeck->GetTitleBar()) |
1551 | 0 | { |
1552 | 0 | aRect = pTitleBar->GetDragArea(); |
1553 | 0 | } |
1554 | 0 | } |
1555 | 0 | return aRect; |
1556 | 0 | } |
1557 | | |
1558 | | void SidebarController::frameAction(const css::frame::FrameActionEvent& rEvent) |
1559 | 0 | { |
1560 | 0 | if (rEvent.Frame == mxFrame) |
1561 | 0 | { |
1562 | 0 | if (rEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING) |
1563 | 0 | unregisterSidebarForFrame(mxFrame->getController()); |
1564 | 0 | else if (rEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED) |
1565 | 0 | registerSidebarForFrame(mxFrame->getController()); |
1566 | 0 | } |
1567 | 0 | } |
1568 | | |
1569 | | void SidebarController::saveDeckState() |
1570 | 0 | { |
1571 | | // Impress shutdown : context (frame) is disposed before sidebar disposing |
1572 | | // calc writer : context (frame) is disposed after sidebar disposing |
1573 | | // so need to test if GetCurrentContext is still valid regarding msApplication |
1574 | 0 | if (GetCurrentContext().msApplication != "none") |
1575 | 0 | { |
1576 | 0 | mpResourceManager->SaveDecksSettings(GetCurrentContext()); |
1577 | 0 | mpResourceManager->SaveLastActiveDeck(GetCurrentContext(), msCurrentDeckId); |
1578 | 0 | } |
1579 | 0 | } |
1580 | | |
1581 | | static bool isChartOrMathContext(const Context& context) |
1582 | 0 | { |
1583 | 0 | return context.msApplication == "com.sun.star.chart2.ChartDocument" |
1584 | 0 | || context.msApplication == "com.sun.star.formula.FormulaProperties"; |
1585 | 0 | } |
1586 | | |
1587 | | bool SidebarController::hasChartOrMathContextCurrently() const |
1588 | 0 | { |
1589 | 0 | if ((maRequestedContext != maCurrentContext) && isChartOrMathContext(maRequestedContext)) |
1590 | 0 | return true; // We are not yet changed, but in the process |
1591 | | |
1592 | 0 | return isChartOrMathContext(maCurrentContext); |
1593 | 0 | } |
1594 | | |
1595 | | sfx2::sidebar::SidebarController* SidebarController::GetSidebarControllerForView(const SfxViewShell* pViewShell) |
1596 | 0 | { |
1597 | 0 | if (!pViewShell) |
1598 | 0 | return nullptr; |
1599 | | |
1600 | 0 | Reference<css::frame::XController2> xController(pViewShell->GetController(), UNO_QUERY); |
1601 | 0 | if (!xController.is()) |
1602 | 0 | return nullptr; |
1603 | | |
1604 | | // Make sure there is a model behind the controller, otherwise getSidebar() can crash. |
1605 | 0 | if (!xController->getModel().is()) |
1606 | 0 | return nullptr; |
1607 | | |
1608 | 0 | Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar(); |
1609 | 0 | if (!xSidebarProvider.is()) |
1610 | 0 | return nullptr; |
1611 | | |
1612 | 0 | Reference<css::ui::XSidebar> xSidebar = xSidebarProvider->getSidebar(); |
1613 | 0 | if (!xSidebar.is()) |
1614 | 0 | return nullptr; |
1615 | | |
1616 | 0 | return dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get()); |
1617 | 0 | } |
1618 | | |
1619 | | } // end of namespace sfx2::sidebar |
1620 | | |
1621 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |