cid#1607171 Data race condition
[LibreOffice.git] / sd / source / ui / view / ViewTabBar.cxx
blobe6e91016bf6b4663c379ba81b07e667e22d5e5d1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <ViewTabBar.hxx>
22 #include <ViewShellBase.hxx>
23 #include <framework/FrameworkHelper.hxx>
24 #include <framework/Pane.hxx>
25 #include <DrawController.hxx>
27 #include <Client.hxx>
28 #include <utility>
29 #include <vcl/settings.hxx>
30 #include <vcl/svapp.hxx>
32 #include <sfx2/viewfrm.hxx>
33 #include <com/sun/star/drawing/framework/ResourceId.hpp>
34 #include <com/sun/star/drawing/framework/XControllerManager.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/drawing/framework/XView.hpp>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <comphelper/servicehelper.hxx>
40 #include <comphelper/diagnose_ex.hxx>
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::drawing::framework;
45 using ::sd::framework::FrameworkHelper;
47 namespace sd {
49 namespace {
50 bool IsEqual (const TabBarButton& rButton1, const TabBarButton& rButton2)
52 return ((rButton1.ResourceId.is()
53 && rButton2.ResourceId.is()
54 && rButton1.ResourceId->compareTo(rButton2.ResourceId) == 0)
55 || rButton1.ButtonLabel == rButton2.ButtonLabel);
58 } // end of anonymous namespace
60 ViewTabBar::ViewTabBar (
61 const Reference<XResourceId>& rxViewTabBarId,
62 const rtl::Reference<::sd::DrawController>& rxController)
63 : mpTabControl(VclPtr<TabBarControl>::Create(GetAnchorWindow(rxViewTabBarId,rxController), this)),
64 mxController(rxController),
65 mxViewTabBarId(rxViewTabBarId),
66 mpViewShellBase(nullptr),
67 mnNoteBookWidthPadding(0)
69 // Do this manually instead of via uno::Reference, so we don't delete ourselves.
70 osl_atomic_increment(&m_refCount);
72 // Tunnel through the controller and use the ViewShellBase to obtain the
73 // view frame.
74 if (mxController)
75 mpViewShellBase = mxController->GetViewShellBase();
77 // Register as listener at XConfigurationController.
78 if (mxController.is())
80 mxConfigurationController = mxController->getConfigurationController();
81 if (mxConfigurationController.is())
83 mxConfigurationController->addConfigurationChangeListener(
84 this,
85 FrameworkHelper::msResourceActivationEvent,
86 Any());
90 mpTabControl->Show();
92 if (mpViewShellBase != nullptr
93 && rxViewTabBarId->isBoundToURL(
94 FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
96 mpViewShellBase->SetViewTabBar(this);
99 osl_atomic_decrement(&m_refCount);
102 ViewTabBar::~ViewTabBar()
106 void ViewTabBar::disposing(std::unique_lock<std::mutex>&)
108 if (mpViewShellBase != nullptr
109 && mxViewTabBarId->isBoundToURL(
110 FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
112 mpViewShellBase->SetViewTabBar(nullptr);
115 if (mxConfigurationController.is())
117 // Unregister listener from XConfigurationController.
120 mxConfigurationController->removeConfigurationChangeListener(this);
122 catch (const lang::DisposedException&)
124 // Receiving a disposed exception is the normal case. Is there
125 // a way to avoid it?
127 mxConfigurationController = nullptr;
131 const SolarMutexGuard aSolarGuard;
132 mpTabControl.disposeAndClear();
135 mxController = nullptr;
138 vcl::Window* ViewTabBar::GetAnchorWindow(
139 const Reference<XResourceId>& rxViewTabBarId,
140 const rtl::Reference<::sd::DrawController>& rxController)
142 vcl::Window* pWindow = nullptr;
143 ViewShellBase* pBase = nullptr;
145 // Tunnel through the controller and use the ViewShellBase to obtain the
146 // view frame.
147 if (rxController)
148 pBase = rxController->GetViewShellBase();
150 // The ViewTabBar supports at the moment only the center pane.
151 if (rxViewTabBarId.is()
152 && rxViewTabBarId->isBoundToURL(
153 FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT))
155 if (pBase != nullptr)
156 pWindow = &pBase->GetViewFrame().GetWindow();
159 // The rest is (at the moment) just for the emergency case.
160 if (pWindow == nullptr)
162 Reference<XPane> xPane;
165 Reference<XConfigurationController> xCC (
166 rxController->getConfigurationController());
167 if (xCC.is())
168 xPane.set(xCC->getResource(rxViewTabBarId->getAnchor()), UNO_QUERY);
170 catch (const RuntimeException&)
174 // Tunnel through the XWindow to the VCL side.
177 if (auto pPane = dynamic_cast<framework::Pane*>(xPane.get()))
178 pWindow = pPane->GetWindow()->GetParent();
180 catch (const RuntimeException&)
185 return pWindow;
188 //----- XConfigurationChangeListener ------------------------------------------
190 void SAL_CALL ViewTabBar::notifyConfigurationChange (
191 const ConfigurationChangeEvent& rEvent)
193 if (rEvent.Type == FrameworkHelper::msResourceActivationEvent
194 && rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)
195 && rEvent.ResourceId->isBoundTo(mxViewTabBarId->getAnchor(), AnchorBindingMode_DIRECT))
197 UpdateActiveButton();
201 //----- XEventListener --------------------------------------------------------
203 void SAL_CALL ViewTabBar::disposing(
204 const lang::EventObject& rEvent)
206 if (rEvent.Source == mxConfigurationController)
208 mxConfigurationController = nullptr;
209 mxController = nullptr;
213 //----- XTabBar ---------------------------------------------------------------
215 void SAL_CALL ViewTabBar::addTabBarButtonAfter (
216 const TabBarButton& rButton,
217 const TabBarButton& rAnchor)
219 const SolarMutexGuard aSolarGuard;
220 AddTabBarButton(rButton, rAnchor);
223 void SAL_CALL ViewTabBar::appendTabBarButton (const TabBarButton& rButton)
225 const SolarMutexGuard aSolarGuard;
226 AddTabBarButton(rButton);
229 void SAL_CALL ViewTabBar::removeTabBarButton (const TabBarButton& rButton)
231 const SolarMutexGuard aSolarGuard;
232 RemoveTabBarButton(rButton);
235 sal_Bool SAL_CALL ViewTabBar::hasTabBarButton (const TabBarButton& rButton)
237 const SolarMutexGuard aSolarGuard;
238 return HasTabBarButton(rButton);
241 Sequence<TabBarButton> SAL_CALL ViewTabBar::getTabBarButtons()
243 const SolarMutexGuard aSolarGuard;
244 return GetTabBarButtons();
247 //----- XResource -------------------------------------------------------------
249 Reference<XResourceId> SAL_CALL ViewTabBar::getResourceId()
251 return mxViewTabBarId;
254 sal_Bool SAL_CALL ViewTabBar::isAnchorOnly()
256 return false;
259 bool ViewTabBar::ActivatePage(size_t nIndex)
263 Reference<XConfigurationController> xConfigurationController (
264 mxController->getConfigurationController());
265 if ( ! xConfigurationController.is())
266 throw RuntimeException();
267 Reference<XView> xView;
270 xView.set(xConfigurationController->getResource(
271 ResourceId::create(
272 ::comphelper::getProcessComponentContext(),
273 FrameworkHelper::msCenterPaneURL)),
274 UNO_QUERY);
276 catch (const DeploymentException&)
280 Client* pIPClient = nullptr;
281 if (mpViewShellBase != nullptr)
282 pIPClient = dynamic_cast<Client*>(mpViewShellBase->GetIPClient());
283 if (pIPClient==nullptr || ! pIPClient->IsObjectInPlaceActive())
285 if (nIndex < maTabBarButtons.size())
287 xConfigurationController->requestResourceActivation(
288 maTabBarButtons[nIndex].ResourceId,
289 ResourceActivationMode_REPLACE);
292 return true;
295 catch (const RuntimeException&)
297 DBG_UNHANDLED_EXCEPTION("sd.view");
300 return false;
303 int ViewTabBar::GetHeight() const
305 int nHeight (0);
307 if (!maTabBarButtons.empty())
309 if (mpTabControl->IsReallyVisible())
311 weld::Notebook& rNotebook = mpTabControl->GetNotebook();
312 int nAllocatedWidth = mpTabControl->GetAllocatedWidth();
313 int nPageWidth = nAllocatedWidth - mnNoteBookWidthPadding;
315 // set each page width-request to the size it takes to fit the notebook allocation
316 for (int nIndex = 1, nPageCount = rNotebook.get_n_pages(); nIndex <= nPageCount; ++nIndex)
318 OUString sIdent(OUString::number(nIndex));
319 weld::Container* pContainer = rNotebook.get_page(sIdent);
320 pContainer->set_size_request(nPageWidth, -1);
323 // get the height-for-width for this allocation
324 nHeight = mpTabControl->get_preferred_size().Height();
327 if (nHeight <= 0)
329 // Using a default when the real height can not be determined.
330 // To get correct height this method should be called when the
331 // control is visible.
332 nHeight = 21;
336 return nHeight;
339 void ViewTabBar::AddTabBarButton (
340 const css::drawing::framework::TabBarButton& rButton,
341 const css::drawing::framework::TabBarButton& rAnchor)
343 TabBarButtonList::size_type nIndex;
345 if ( ! rAnchor.ResourceId.is()
346 || (rAnchor.ResourceId->getResourceURL().isEmpty()
347 && rAnchor.ButtonLabel.isEmpty()))
349 nIndex = 0;
351 else
353 for (nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
355 if (IsEqual(maTabBarButtons[nIndex], rAnchor))
357 ++nIndex;
358 break;
363 AddTabBarButton(rButton,nIndex);
366 void ViewTabBar::AddTabBarButton (
367 const css::drawing::framework::TabBarButton& rButton)
369 AddTabBarButton(rButton, maTabBarButtons.size());
372 void ViewTabBar::AddTabBarButton (
373 const css::drawing::framework::TabBarButton& rButton,
374 sal_Int32 nPosition)
376 if (nPosition >= 0 &&
377 nPosition <= mpTabControl->GetNotebook().get_n_pages())
379 // Insert the button into our local array.
380 maTabBarButtons.insert(maTabBarButtons.begin() + nPosition, rButton);
381 UpdateTabBarButtons();
382 UpdateActiveButton();
386 void ViewTabBar::RemoveTabBarButton (
387 const css::drawing::framework::TabBarButton& rButton)
389 for (TabBarButtonList::size_type nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
391 if (IsEqual(maTabBarButtons[nIndex], rButton))
393 maTabBarButtons.erase(maTabBarButtons.begin()+nIndex);
394 UpdateTabBarButtons();
395 UpdateActiveButton();
396 break;
401 bool ViewTabBar::HasTabBarButton (
402 const css::drawing::framework::TabBarButton& rButton)
404 bool bResult (false);
406 for (const css::drawing::framework::TabBarButton & r : maTabBarButtons)
408 if (IsEqual(r, rButton))
410 bResult = true;
411 break;
415 return bResult;
418 css::uno::Sequence<css::drawing::framework::TabBarButton>
419 ViewTabBar::GetTabBarButtons()
421 return comphelper::containerToSequence(maTabBarButtons);
424 void ViewTabBar::UpdateActiveButton()
426 Reference<XView> xView;
427 if (mpViewShellBase != nullptr)
428 xView = FrameworkHelper::Instance(*mpViewShellBase)->GetView(
429 mxViewTabBarId->getAnchor());
430 if (!xView.is())
431 return;
433 Reference<XResourceId> xViewId (xView->getResourceId());
434 for (size_t nIndex=0; nIndex<maTabBarButtons.size(); ++nIndex)
436 if (maTabBarButtons[nIndex].ResourceId->compareTo(xViewId) == 0)
438 mpTabControl->GetNotebook().set_current_page(nIndex);
439 break;
444 void ViewTabBar::UpdateTabBarButtons()
446 int nMaxPageWidthReq(0);
448 weld::Notebook& rNotebook = mpTabControl->GetNotebook();
449 int nPageCount(rNotebook.get_n_pages());
450 int nIndex = 1;
451 for (const auto& rTab : maTabBarButtons)
453 OUString sIdent(OUString::number(nIndex));
454 // Create a new tab when there are not enough.
455 if (nPageCount < nIndex)
456 rNotebook.append_page(sIdent, rTab.ButtonLabel);
457 else
459 // Update the tab.
460 rNotebook.set_tab_label_text(sIdent, rTab.ButtonLabel);
463 // Set a fairly arbitrary initial width request for the pages so we can
464 // measure what extra width the notebook itself uses
465 weld::Container* pContainer = rNotebook.get_page(sIdent);
466 int nTextWidth = pContainer->get_pixel_size(rTab.ButtonLabel).Width();
467 pContainer->set_size_request(nTextWidth, -1);
468 nMaxPageWidthReq = std::max(nMaxPageWidthReq, nTextWidth);
470 ++nIndex;
473 // Delete tabs that are no longer used.
474 for (; nIndex<=nPageCount; ++nIndex)
475 rNotebook.remove_page(OUString::number(nIndex));
477 int nWidthReq = rNotebook.get_preferred_size().Width();
478 // The excess width over the page request that the notebook uses we will
479 // use this later to help measure the best height-for-width given the
480 // eventual allocated width of the notebook
481 mnNoteBookWidthPadding = nWidthReq - nMaxPageWidthReq;
484 //===== TabBarControl =========================================================
486 TabBarControl::TabBarControl (
487 vcl::Window* pParentWindow,
488 ::rtl::Reference<ViewTabBar> pViewTabBar)
489 : InterimItemWindow(pParentWindow, u"modules/simpress/ui/tabviewbar.ui"_ustr, u"TabViewBar"_ustr)
490 , mxTabControl(m_xBuilder->weld_notebook(u"tabcontrol"_ustr))
491 , mpViewTabBar(std::move(pViewTabBar))
492 , mnAllocatedWidth(0)
494 // Because the actual window background is transparent--to avoid
495 // flickering due to multiple background paintings by this and by child
496 // windows--we have to paint the background for this control explicitly:
497 // the actual control is not painted over its whole bounding box.
498 SetPaintTransparent(false);
499 SetBackground(Application::GetSettings().GetStyleSettings().GetDialogColor());
501 InitControlBase(mxTabControl.get());
503 mxTabControl->connect_enter_page(LINK(this, TabBarControl, ActivatePageHdl));
504 mxTabControl->connect_size_allocate(LINK(this, TabBarControl, NotebookSizeAllocHdl));
507 void TabBarControl::dispose()
509 mxTabControl.reset();
510 InterimItemWindow::dispose();
513 TabBarControl::~TabBarControl()
515 disposeOnce();
518 IMPL_LINK(TabBarControl, NotebookSizeAllocHdl, const Size&, rSize, void)
520 mnAllocatedWidth = rSize.Width();
523 IMPL_LINK(TabBarControl, ActivatePageHdl, const OUString&, rPage, void)
525 if (!mpViewTabBar->ActivatePage(mxTabControl->get_page_index(rPage)))
527 // When we run into this else branch then we have an active OLE
528 // object. We ignore the request to switch views. Additionally
529 // we put the active tab back to the one for the current view.
530 mpViewTabBar->UpdateActiveButton();
534 } // end of namespace sd
536 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */