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 
10 #include <sal/config.h>
11 
12 #include <string_view>
13 
14 #include <vcl/layout.hxx>
15 #include <vcl/notebookbar/notebookbar.hxx>
16 #include <vcl/syswin.hxx>
17 #include <vcl/taskpanelist.hxx>
18 #include <vcl/NotebookbarContextControl.hxx>
19 #include <cppuhelper/implbase.hxx>
20 #include <comphelper/processfactory.hxx>
21 #include <rtl/bootstrap.hxx>
22 #include <osl/file.hxx>
23 #include <config_folders.h>
24 #include <com/sun/star/frame/XFrame.hpp>
25 #include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
26 #include <comphelper/lok.hxx>
27 
getCustomizedUIRootDir()28 static OUString getCustomizedUIRootDir()
29 {
30     OUString sShareLayer("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE(
31         "bootstrap") ":UserInstallation}/user/config/soffice.cfg/");
32     rtl::Bootstrap::expandMacros(sShareLayer);
33     return sShareLayer;
34 }
35 
doesFileExist(std::u16string_view sUIDir,std::u16string_view sUIFile)36 static bool doesFileExist(std::u16string_view sUIDir, std::u16string_view sUIFile)
37 {
38     OUString sUri = OUString::Concat(sUIDir) + sUIFile;
39     osl::File file(sUri);
40     return( file.open(0) == osl::FileBase::E_None );
41 }
42 
43 /**
44  * split from the main class since it needs different ref-counting mana
45  */
46 class NotebookBarContextChangeEventListener : public ::cppu::WeakImplHelper<css::ui::XContextChangeEventListener>
47 {
48     VclPtr<NotebookBar> mpParent;
49 public:
NotebookBarContextChangeEventListener(NotebookBar * p)50     explicit NotebookBarContextChangeEventListener(NotebookBar *p) : mpParent(p) {}
51 
52     // XContextChangeEventListener
53     virtual void SAL_CALL notifyContextChangeEvent(const css::ui::ContextChangeEventObject& rEvent) override;
54 
55     virtual void SAL_CALL disposing(const ::css::lang::EventObject&) override;
56 };
57 
NotebookBar(Window * pParent,const OString & rID,const OUString & rUIXMLDescription,const css::uno::Reference<css::frame::XFrame> & rFrame,const NotebookBarAddonsItem & aNotebookBarAddonsItem)58 NotebookBar::NotebookBar(Window* pParent, const OString& rID, const OUString& rUIXMLDescription,
59                          const css::uno::Reference<css::frame::XFrame>& rFrame,
60                          const NotebookBarAddonsItem& aNotebookBarAddonsItem)
61     : Control(pParent)
62     , m_pEventListener(new NotebookBarContextChangeEventListener(this))
63     , m_pViewShell(nullptr)
64     , m_bIsWelded(false)
65     , m_sUIXMLDescription(rUIXMLDescription)
66 {
67     mxFrame = rFrame;
68 
69     SetStyle(GetStyle() | WB_DIALOGCONTROL);
70     OUString sUIDir = AllSettings::GetUIRootDir();
71     bool doesCustomizedUIExist = doesFileExist(getCustomizedUIRootDir(), rUIXMLDescription);
72     if ( doesCustomizedUIExist )
73         sUIDir = getCustomizedUIRootDir();
74 
75     bool bIsWelded = comphelper::LibreOfficeKit::isActive();
76     if (bIsWelded)
77     {
78         m_bIsWelded = true;
79         m_xVclContentArea = VclPtr<VclVBox>::Create(this);
80         m_xVclContentArea->Show();
81         // now access it using GetMainContainer and set dispose callback with SetDisposeCallback
82     }
83     else
84     {
85         m_pUIBuilder.reset(
86             new VclBuilder(this, sUIDir, rUIXMLDescription, rID, rFrame, true, &aNotebookBarAddonsItem));
87 
88         // In the Notebookbar's .ui file must exist control handling context
89         // - implementing NotebookbarContextControl interface with id "ContextContainer"
90         // or "ContextContainerX" where X is a number >= 1
91         NotebookbarContextControl* pContextContainer = nullptr;
92         int i = 0;
93         do
94         {
95             OUString aName = "ContextContainer";
96             if (i)
97                 aName += OUString::number(i);
98 
99             pContextContainer = dynamic_cast<NotebookbarContextControl*>(m_pUIBuilder->get<Window>(OUStringToOString(aName, RTL_TEXTENCODING_UTF8)));
100             if (pContextContainer)
101                 m_pContextContainers.push_back(pContextContainer);
102             i++;
103         }
104         while( pContextContainer != nullptr );
105     }
106 
107     UpdateBackground();
108 }
109 
SetDisposeCallback(const Link<const SfxViewShell *,void> rDisposeCallback,const SfxViewShell * pViewShell)110 void NotebookBar::SetDisposeCallback(const Link<const SfxViewShell*, void> rDisposeCallback, const SfxViewShell* pViewShell)
111 {
112     m_rDisposeLink = rDisposeCallback;
113     m_pViewShell = pViewShell;
114 }
115 
~NotebookBar()116 NotebookBar::~NotebookBar()
117 {
118     disposeOnce();
119 }
120 
dispose()121 void NotebookBar::dispose()
122 {
123     m_pContextContainers.clear();
124     if (m_pSystemWindow && m_pSystemWindow->ImplIsInTaskPaneList(this))
125         m_pSystemWindow->GetTaskPaneList()->RemoveWindow(this);
126     m_pSystemWindow.clear();
127 
128     if (m_rDisposeLink.IsSet())
129         m_rDisposeLink.Call(m_pViewShell);
130 
131     if (m_bIsWelded)
132         m_xVclContentArea.disposeAndClear();
133     else
134         disposeBuilder();
135 
136     assert(m_alisteningControllers.empty());
137     m_pEventListener.clear();
138 
139     Control::dispose();
140 }
141 
PreNotify(NotifyEvent & rNEvt)142 bool NotebookBar::PreNotify(NotifyEvent& rNEvt)
143 {
144     // capture KeyEvents for taskpane cycling
145     if (rNEvt.GetType() == MouseNotifyEvent::KEYINPUT)
146     {
147         if (m_pSystemWindow)
148             return m_pSystemWindow->PreNotify(rNEvt);
149     }
150     return Window::PreNotify( rNEvt );
151 }
152 
GetOptimalSize() const153 Size NotebookBar::GetOptimalSize() const
154 {
155     if (isLayoutEnabled(this))
156         return VclContainer::getLayoutRequisition(*GetWindow(GetWindowType::FirstChild));
157 
158     return Control::GetOptimalSize();
159 }
160 
setPosSizePixel(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,PosSizeFlags nFlags)161 void NotebookBar::setPosSizePixel(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags)
162 {
163     bool bCanHandleSmallerWidth = false;
164     bool bCanHandleSmallerHeight = false;
165 
166     bool bIsLayoutEnabled = isLayoutEnabled(this);
167     Window *pChild = GetWindow(GetWindowType::FirstChild);
168 
169     if (bIsLayoutEnabled && pChild->GetType() == WindowType::SCROLLWINDOW)
170     {
171         WinBits nStyle = pChild->GetStyle();
172         if (nStyle & (WB_AUTOHSCROLL | WB_HSCROLL))
173             bCanHandleSmallerWidth = true;
174         if (nStyle & (WB_AUTOVSCROLL | WB_VSCROLL))
175             bCanHandleSmallerHeight = true;
176     }
177 
178     Size aSize(GetOptimalSize());
179     if (!bCanHandleSmallerWidth)
180         nWidth = std::max(nWidth, aSize.Width());
181     if (!bCanHandleSmallerHeight)
182         nHeight = std::max(nHeight, aSize.Height());
183 
184     Control::setPosSizePixel(nX, nY, nWidth, nHeight, nFlags);
185 
186     if (bIsLayoutEnabled && (nFlags & PosSizeFlags::Size))
187         VclContainer::setLayoutAllocation(*pChild, Point(0, 0), Size(nWidth, nHeight));
188 }
189 
Resize()190 void NotebookBar::Resize()
191 {
192     if(m_pUIBuilder && m_pUIBuilder->get_widget_root())
193     {
194         vcl::Window* pWindow = m_pUIBuilder->get_widget_root()->GetChild(0);
195         if (pWindow)
196         {
197             Size aSize = pWindow->GetSizePixel();
198             aSize.setWidth( GetSizePixel().Width() );
199             pWindow->SetSizePixel(aSize);
200         }
201     }
202     if(m_bIsWelded)
203     {
204         vcl::Window* pChild = GetWindow(GetWindowType::FirstChild);
205         assert(pChild);
206         VclContainer::setLayoutAllocation(*pChild, Point(0, 0), GetSizePixel());
207         Control::Resize();
208     }
209     Control::Resize();
210 }
211 
SetSystemWindow(SystemWindow * pSystemWindow)212 void NotebookBar::SetSystemWindow(SystemWindow* pSystemWindow)
213 {
214     m_pSystemWindow = pSystemWindow;
215     if (!m_pSystemWindow->ImplIsInTaskPaneList(this))
216         m_pSystemWindow->GetTaskPaneList()->AddWindow(this);
217 }
218 
notifyContextChangeEvent(const css::ui::ContextChangeEventObject & rEvent)219 void SAL_CALL NotebookBarContextChangeEventListener::notifyContextChangeEvent(const css::ui::ContextChangeEventObject& rEvent)
220 {
221     if (mpParent)
222     {
223         for (NotebookbarContextControl* pControl : mpParent->m_pContextContainers)
224             pControl->SetContext(vcl::EnumContext::GetContextEnum(rEvent.ContextName));
225     }
226 }
227 
ControlListenerForCurrentController(bool bListen)228 void NotebookBar::ControlListenerForCurrentController(bool bListen)
229 {
230     if (comphelper::LibreOfficeKit::isActive())
231         return;
232 
233     auto xController = mxFrame->getController();
234     if(bListen)
235     {
236         // add listeners
237         if (m_alisteningControllers.count(xController) == 0)
238         {
239             auto xMultiplexer(css::ui::ContextChangeEventMultiplexer::get(
240                 ::comphelper::getProcessComponentContext()));
241             xMultiplexer->addContextChangeEventListener(m_pEventListener, xController);
242             m_alisteningControllers.insert(xController);
243         }
244     }
245     else
246     {
247         // remove listeners
248         if (m_alisteningControllers.count(xController))
249         {
250             auto xMultiplexer(css::ui::ContextChangeEventMultiplexer::get(
251                 ::comphelper::getProcessComponentContext()));
252             xMultiplexer->removeContextChangeEventListener(m_pEventListener, xController);
253             m_alisteningControllers.erase(xController);
254         }
255     }
256 }
257 
StopListeningAllControllers()258 void NotebookBar::StopListeningAllControllers()
259 {
260     if (comphelper::LibreOfficeKit::isActive())
261         return;
262 
263     auto xMultiplexer(
264         css::ui::ContextChangeEventMultiplexer::get(comphelper::getProcessComponentContext()));
265     xMultiplexer->removeAllContextChangeEventListeners(m_pEventListener);
266     m_alisteningControllers.clear();
267 }
268 
disposing(const::css::lang::EventObject &)269 void SAL_CALL NotebookBarContextChangeEventListener::disposing(const ::css::lang::EventObject&)
270 {
271     mpParent.clear();
272 }
273 
DataChanged(const DataChangedEvent & rDCEvt)274 void NotebookBar::DataChanged(const DataChangedEvent& rDCEvt)
275 {
276     UpdateBackground();
277     Control::DataChanged(rDCEvt);
278 }
279 
StateChanged(const StateChangedType nStateChange)280 void NotebookBar::StateChanged(const  StateChangedType nStateChange )
281 {
282     UpdateBackground();
283     Control::StateChanged(nStateChange);
284     Invalidate();
285 }
286 
UpdateBackground()287 void NotebookBar::UpdateBackground()
288 {
289     const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
290     const BitmapEx& aPersona = rStyleSettings.GetPersonaHeader();
291     Wallpaper aWallpaper(aPersona);
292     aWallpaper.SetStyle(WallpaperStyle::TopRight);
293     if (!aPersona.IsEmpty())
294         {
295             SetBackground(aWallpaper);
296             UpdatePersonaSettings();
297             GetOutDev()->SetSettings( PersonaSettings );
298         }
299     else
300         {
301             SetBackground(rStyleSettings.GetDialogColor());
302             UpdateDefaultSettings();
303             GetOutDev()->SetSettings( DefaultSettings );
304         }
305 
306     Invalidate(tools::Rectangle(Point(0,0), GetSizePixel()));
307 }
308 
UpdateDefaultSettings()309 void NotebookBar::UpdateDefaultSettings()
310 {
311     AllSettings aAllSettings( GetSettings() );
312     StyleSettings aStyleSet( aAllSettings.GetStyleSettings() );
313 
314     ::Color aTextColor = aStyleSet.GetFieldTextColor();
315     aStyleSet.SetDialogTextColor( aTextColor );
316     aStyleSet.SetButtonTextColor( aTextColor );
317     aStyleSet.SetRadioCheckTextColor( aTextColor );
318     aStyleSet.SetGroupTextColor( aTextColor );
319     aStyleSet.SetLabelTextColor( aTextColor );
320     aStyleSet.SetWindowTextColor( aTextColor );
321     aStyleSet.SetTabTextColor(aTextColor);
322     aStyleSet.SetToolTextColor(aTextColor);
323 
324     aAllSettings.SetStyleSettings(aStyleSet);
325     DefaultSettings = aAllSettings;
326 }
327 
UpdatePersonaSettings()328 void NotebookBar::UpdatePersonaSettings()
329 {
330     AllSettings aAllSettings( GetSettings() );
331     StyleSettings aStyleSet( aAllSettings.GetStyleSettings() );
332 
333     ::Color aTextColor = aStyleSet.GetPersonaMenuBarTextColor().value_or(COL_BLACK );
334     aStyleSet.SetDialogTextColor( aTextColor );
335     aStyleSet.SetButtonTextColor( aTextColor );
336     aStyleSet.SetRadioCheckTextColor( aTextColor );
337     aStyleSet.SetGroupTextColor( aTextColor );
338     aStyleSet.SetLabelTextColor( aTextColor );
339     aStyleSet.SetWindowTextColor( aTextColor );
340     aStyleSet.SetTabTextColor(aTextColor);
341     aStyleSet.SetToolTextColor(aTextColor);
342 
343     aAllSettings.SetStyleSettings(aStyleSet);
344     PersonaSettings = aAllSettings;
345 }
346 
347 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
348