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