1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
;
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
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(
85 FrameworkHelper::msResourceActivationEvent
,
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
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());
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
&)
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()
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(
272 ::comphelper::getProcessComponentContext(),
273 FrameworkHelper::msCenterPaneURL
)),
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
);
295 catch (const RuntimeException
&)
297 DBG_UNHANDLED_EXCEPTION("sd.view");
303 int ViewTabBar::GetHeight() const
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();
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.
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()))
353 for (nIndex
=0; nIndex
<maTabBarButtons
.size(); ++nIndex
)
355 if (IsEqual(maTabBarButtons
[nIndex
], rAnchor
))
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
,
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();
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
))
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());
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
);
444 void ViewTabBar::UpdateTabBarButtons()
446 int nMaxPageWidthReq(0);
448 weld::Notebook
& rNotebook
= mpTabControl
->GetNotebook();
449 int nPageCount(rNotebook
.get_n_pages());
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
);
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
);
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()
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: */