/src/libreoffice/sfx2/source/dialog/infobar.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 | | |
10 | | #include <basegfx/polygon/b2dpolygon.hxx> |
11 | | #include <comphelper/dispatchcommand.hxx> |
12 | | #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx> |
13 | | #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx> |
14 | | #include <drawinglayer/processor2d/baseprocessor2d.hxx> |
15 | | #include <drawinglayer/processor2d/processor2dtools.hxx> |
16 | | #include <memory> |
17 | | #include <officecfg/Office/UI/Infobar.hxx> |
18 | | #include <officecfg/Office/Common.hxx> |
19 | | #include <sfx2/bindings.hxx> |
20 | | #include <sfx2/chalign.hxx> |
21 | | #include <sfx2/dispatch.hxx> |
22 | | #include <sfx2/infobar.hxx> |
23 | | #include <sfx2/objface.hxx> |
24 | | #include <sfx2/sfxsids.hrc> |
25 | | #include <sfx2/viewfrm.hxx> |
26 | | #include <utility> |
27 | | #include <vcl/event.hxx> |
28 | | #include <vcl/image.hxx> |
29 | | #include <vcl/settings.hxx> |
30 | | #include <vcl/svapp.hxx> |
31 | | #include <vcl/virdev.hxx> |
32 | | #include <vcl/weld/Builder.hxx> |
33 | | #include <vcl/weld/Button.hxx> |
34 | | #include <vcl/weld/Container.hxx> |
35 | | #include <vcl/weld/Image.hxx> |
36 | | #include <vcl/weld/Label.hxx> |
37 | | #include <vcl/weld/TextView.hxx> |
38 | | #include <vcl/weld/Toolbar.hxx> |
39 | | #include <vcl/weld/weldutils.hxx> |
40 | | #include <bitmaps.hlst> |
41 | | |
42 | | using namespace drawinglayer::geometry; |
43 | | using namespace drawinglayer::processor2d; |
44 | | using namespace drawinglayer::primitive2d; |
45 | | using namespace drawinglayer::attribute; |
46 | | using namespace basegfx; |
47 | | using namespace css::frame; |
48 | | |
49 | | namespace |
50 | | { |
51 | | void GetInfoBarColors(InfobarType ibType, BColor& rBackgroundColor, BColor& rForegroundColor, |
52 | | BColor& rMessageColor) |
53 | 0 | { |
54 | 0 | const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); |
55 | 0 | const bool bIsDark = rSettings.GetWindowColor().IsDark(); |
56 | 0 | switch (ibType) |
57 | 0 | { |
58 | 0 | case InfobarType::INFO: // blue; #004785/0,71,133; #BDE5F8/189,229,248 |
59 | 0 | rBackgroundColor = bIsDark ? basegfx::BColor(0.000, 0.278, 0.522) |
60 | 0 | : basegfx::BColor(0.741, 0.898, 0.973); |
61 | 0 | rForegroundColor = bIsDark ? basegfx::BColor(0.741, 0.898, 0.973) |
62 | 0 | : basegfx::BColor(0.000, 0.278, 0.522); |
63 | 0 | rMessageColor = rForegroundColor; |
64 | 0 | break; |
65 | 0 | case InfobarType::SUCCESS: // green; #32550C/50,85,12; #DFF2BF/223,242,191 |
66 | 0 | rBackgroundColor = bIsDark ? basegfx::BColor(0.196, 0.333, 0.047) |
67 | 0 | : basegfx::BColor(0.874, 0.949, 0.749); |
68 | 0 | rForegroundColor = bIsDark ? basegfx::BColor(0.874, 0.949, 0.749) |
69 | 0 | : basegfx::BColor(0.196, 0.333, 0.047); |
70 | 0 | rMessageColor = rForegroundColor; |
71 | 0 | break; |
72 | 0 | case InfobarType::WARNING: // orange; #704300/112,67,0; #FEEFB3/254,239,179 |
73 | 0 | rBackgroundColor = rSettings.GetWarningColor().getBColor(); |
74 | 0 | rForegroundColor = rSettings.GetWarningTextColor().getBColor(); |
75 | 0 | rMessageColor = rSettings.GetWarningTextColor().getBColor(); |
76 | 0 | break; |
77 | 0 | case InfobarType::DANGER: // red; #7A0006/122,0,6; #FFBABA/255,186,186 |
78 | 0 | rBackgroundColor = rSettings.GetErrorColor().getBColor(); |
79 | 0 | rForegroundColor = rSettings.GetErrorTextColor().getBColor(); |
80 | 0 | rMessageColor = rSettings.GetErrorTextColor().getBColor(); |
81 | 0 | break; |
82 | 0 | } |
83 | | |
84 | 0 | if (rSettings.GetHighContrastMode()) |
85 | 0 | { |
86 | 0 | rBackgroundColor = rSettings.GetLightColor().getBColor(); |
87 | 0 | rForegroundColor = rSettings.GetDialogTextColor().getBColor(); |
88 | 0 | } |
89 | 0 | } |
90 | | OUString GetInfoBarIconName(InfobarType ibType) |
91 | 0 | { |
92 | 0 | OUString aRet; |
93 | |
|
94 | 0 | switch (ibType) |
95 | 0 | { |
96 | 0 | case InfobarType::INFO: |
97 | 0 | aRet = "vcl/res/infobox.png"; |
98 | 0 | break; |
99 | 0 | case InfobarType::SUCCESS: |
100 | 0 | aRet = "vcl/res/successbox.png"; |
101 | 0 | break; |
102 | 0 | case InfobarType::WARNING: |
103 | 0 | aRet = "vcl/res/warningbox.png"; |
104 | 0 | break; |
105 | 0 | case InfobarType::DANGER: |
106 | 0 | aRet = "vcl/res/errorbox.png"; |
107 | 0 | break; |
108 | 0 | } |
109 | | |
110 | 0 | return aRet; |
111 | 0 | } |
112 | | |
113 | | } // anonymous namespace |
114 | | |
115 | | void SfxInfoBarWindow::SetCloseButtonImage() |
116 | 0 | { |
117 | 0 | Size aSize = Image(StockImage::Yes, CLOSEDOC).GetSizePixel(); |
118 | 0 | aSize = Size(aSize.Width() * 1.5, aSize.Height() * 1.5); |
119 | |
|
120 | 0 | ScopedVclPtr<VirtualDevice> xDevice(m_xCloseBtn->create_virtual_device()); |
121 | 0 | xDevice->SetOutputSizePixel(Size(24, 24)); |
122 | 0 | xDevice->SetBackground(Color(m_aBackgroundColor)); |
123 | 0 | xDevice->Erase(); |
124 | |
|
125 | 0 | const int nPos = (24 - aSize.getWidth()) / 2; |
126 | 0 | Point aBtnPos(nPos, nPos); |
127 | |
|
128 | 0 | const ViewInformation2D aNewViewInfos; |
129 | 0 | const std::unique_ptr<BaseProcessor2D> pProcessor( |
130 | 0 | createProcessor2DFromOutputDevice(*xDevice, aNewViewInfos)); |
131 | |
|
132 | 0 | const ::tools::Rectangle aRect(aBtnPos, xDevice->PixelToLogic(aSize)); |
133 | |
|
134 | 0 | drawinglayer::primitive2d::Primitive2DContainer aSeq(2); |
135 | | |
136 | | // Draw background. The right and bottom need to be extended by 1 or |
137 | | // there will be a white line on both edges when Skia is enabled. |
138 | 0 | B2DPolygon aPolygon; |
139 | 0 | aPolygon.append(B2DPoint(aRect.Left(), aRect.Top())); |
140 | 0 | aPolygon.append(B2DPoint(aRect.Right() + 1, aRect.Top())); |
141 | 0 | aPolygon.append(B2DPoint(aRect.Right() + 1, aRect.Bottom() + 1)); |
142 | 0 | aPolygon.append(B2DPoint(aRect.Left(), aRect.Bottom() + 1)); |
143 | 0 | aPolygon.setClosed(true); |
144 | |
|
145 | 0 | aSeq[0] = new PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), m_aBackgroundColor); |
146 | |
|
147 | 0 | LineAttribute aLineAttribute(m_aForegroundColor, 2.0); |
148 | | |
149 | | // Cross |
150 | 0 | B2DPolyPolygon aCross; |
151 | |
|
152 | 0 | B2DPolygon aLine1; |
153 | 0 | aLine1.append(B2DPoint(aRect.Left(), aRect.Top())); |
154 | 0 | aLine1.append(B2DPoint(aRect.Right(), aRect.Bottom())); |
155 | 0 | aCross.append(aLine1); |
156 | |
|
157 | 0 | B2DPolygon aLine2; |
158 | 0 | aLine2.append(B2DPoint(aRect.Right(), aRect.Top())); |
159 | 0 | aLine2.append(B2DPoint(aRect.Left(), aRect.Bottom())); |
160 | 0 | aCross.append(aLine2); |
161 | |
|
162 | 0 | aSeq[1] |
163 | 0 | = new PolyPolygonStrokePrimitive2D(std::move(aCross), aLineAttribute, StrokeAttribute()); |
164 | |
|
165 | 0 | pProcessor->process(aSeq); |
166 | |
|
167 | 0 | m_xCloseBtn->set_item_image(u"close"_ustr, xDevice); |
168 | 0 | } |
169 | | |
170 | | class ExtraButton |
171 | | { |
172 | | private: |
173 | | std::unique_ptr<weld::Builder> m_xBuilder; |
174 | | std::unique_ptr<weld::Container> m_xContainer; |
175 | | std::unique_ptr<weld::Button> m_xButton; |
176 | | /** StatusListener. Updates the button as the slot state changes */ |
177 | | rtl::Reference<weld::WidgetStatusListener> m_xStatusListener; |
178 | | OUString m_aCommand; |
179 | | |
180 | | DECL_LINK(CommandHdl, weld::Button&, void); |
181 | | |
182 | | public: |
183 | | ExtraButton(weld::Container* pContainer, const OUString* pCommand) |
184 | 0 | : m_xBuilder(Application::CreateBuilder(pContainer, u"sfx/ui/extrabutton.ui"_ustr)) |
185 | 0 | , m_xContainer(m_xBuilder->weld_container(u"ExtraButton"_ustr)) |
186 | 0 | , m_xButton(m_xBuilder->weld_button(u"button"_ustr)) |
187 | 0 | { |
188 | 0 | if (pCommand) |
189 | 0 | { |
190 | 0 | m_aCommand = *pCommand; |
191 | 0 | m_xButton->connect_clicked(LINK(this, ExtraButton, CommandHdl)); |
192 | 0 | m_xStatusListener.set(new weld::WidgetStatusListener(m_xButton.get(), m_aCommand)); |
193 | 0 | m_xStatusListener->startListening(); |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | ~ExtraButton() |
198 | 0 | { |
199 | 0 | if (m_xStatusListener.is()) |
200 | 0 | m_xStatusListener->dispose(); |
201 | 0 | } |
202 | | |
203 | 0 | weld::Button& get_widget() { return *m_xButton; } |
204 | | }; |
205 | | |
206 | | IMPL_LINK_NOARG(ExtraButton, CommandHdl, weld::Button&, void) |
207 | 0 | { |
208 | 0 | comphelper::dispatchCommand(m_aCommand, css::uno::Sequence<css::beans::PropertyValue>()); |
209 | 0 | } |
210 | | |
211 | | SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window* pParent, OUString sId, |
212 | | const OUString& sPrimaryMessage, |
213 | | const OUString& sSecondaryMessage, InfobarType ibType, |
214 | | bool bShowCloseButton) |
215 | 0 | : InterimItemWindow(pParent, u"sfx/ui/infobar.ui"_ustr, u"InfoBar"_ustr) |
216 | 0 | , m_sId(std::move(sId)) |
217 | 0 | , m_eType(ibType) |
218 | 0 | , m_bLayingOut(false) |
219 | 0 | , m_xImage(m_xBuilder->weld_image(u"image"_ustr)) |
220 | 0 | , m_xPrimaryMessage(m_xBuilder->weld_label(u"primary"_ustr)) |
221 | 0 | , m_xSecondaryMessage(m_xBuilder->weld_text_view(u"secondary"_ustr)) |
222 | 0 | , m_xButtonBox(m_xBuilder->weld_container(u"buttonbox"_ustr)) |
223 | 0 | , m_xCloseBtn(m_xBuilder->weld_toolbar(u"closebar"_ustr)) |
224 | 0 | { |
225 | 0 | SetStyle(GetStyle() | WB_DIALOGCONTROL); |
226 | |
|
227 | 0 | InitControlBase(m_xCloseBtn.get()); |
228 | |
|
229 | 0 | m_xImage->set_from_icon_name(GetInfoBarIconName(ibType)); |
230 | 0 | m_xSecondaryMessage->set_margin_top(m_xImage->get_preferred_size().Height() / 4); |
231 | |
|
232 | 0 | if (!sPrimaryMessage.isEmpty()) |
233 | 0 | { |
234 | 0 | m_xPrimaryMessage->set_label(sPrimaryMessage); |
235 | 0 | m_xPrimaryMessage->show(); |
236 | 0 | } |
237 | |
|
238 | 0 | m_xSecondaryMessage->set_text(sSecondaryMessage); |
239 | 0 | m_aOrigMessageSize = m_xSecondaryMessage->get_preferred_size(); |
240 | 0 | m_aMessageSize = m_aOrigMessageSize; |
241 | 0 | m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl)); |
242 | |
|
243 | 0 | if (bShowCloseButton) |
244 | 0 | { |
245 | 0 | m_xCloseBtn->connect_clicked(LINK(this, SfxInfoBarWindow, CloseHandler)); |
246 | 0 | m_xCloseBtn->show(); |
247 | 0 | } |
248 | |
|
249 | 0 | EnableChildTransparentMode(); |
250 | |
|
251 | 0 | SetForeAndBackgroundColors(m_eType); |
252 | |
|
253 | 0 | auto nWidth = pParent->GetSizePixel().getWidth(); |
254 | 0 | auto nHeight = get_preferred_size().Height(); |
255 | 0 | SetSizePixel(Size(nWidth, nHeight + 2)); |
256 | |
|
257 | 0 | Resize(); |
258 | 0 | } Unexecuted instantiation: SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window*, rtl::OUString, rtl::OUString const&, rtl::OUString const&, InfobarType, bool) Unexecuted instantiation: SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window*, rtl::OUString, rtl::OUString const&, rtl::OUString const&, InfobarType, bool) |
259 | | |
260 | | IMPL_LINK(SfxInfoBarWindow, SizeAllocHdl, const Size&, rSize, void) |
261 | 0 | { |
262 | 0 | if (m_aMessageSize != rSize) |
263 | 0 | { |
264 | 0 | m_aMessageSize = rSize; |
265 | 0 | static_cast<SfxInfoBarContainerWindow*>(GetParent())->TriggerUpdateLayout(); |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | Size SfxInfoBarWindow::DoLayout() |
270 | 0 | { |
271 | 0 | Size aGivenSize(GetSizePixel()); |
272 | | |
273 | | // disconnect SizeAllocHdl because we don't care about the size change |
274 | | // during layout |
275 | 0 | m_xSecondaryMessage->connect_size_allocate(Link<const Size&, void>()); |
276 | | |
277 | | // blow away size cache in case m_aMessageSize.Width() is already the width request |
278 | | // and we would get the cached preferred size instead of the recalc we want to force |
279 | 0 | m_xSecondaryMessage->set_size_request(-1, -1); |
280 | | // make the width we were detected as set to by SizeAllocHdl as our desired width |
281 | 0 | m_xSecondaryMessage->set_size_request(m_aMessageSize.Width(), -1); |
282 | | // get our preferred size with that message width |
283 | 0 | Size aSizeForWidth(aGivenSize.Width(), m_xContainer->get_preferred_size().Height()); |
284 | | // restore the message preferred size so we can freely resize, and get a new |
285 | | // m_aMessageSize and repeat the process if we do |
286 | 0 | m_xSecondaryMessage->set_size_request(m_aOrigMessageSize.Width(), -1); |
287 | | |
288 | | // connect SizeAllocHdl so changes outside of this layout will trigger a new layout |
289 | 0 | m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl)); |
290 | |
|
291 | 0 | return aSizeForWidth; |
292 | 0 | } |
293 | | |
294 | | void SfxInfoBarWindow::Layout() |
295 | 0 | { |
296 | 0 | if (m_bLayingOut) |
297 | 0 | return; |
298 | 0 | m_bLayingOut = true; |
299 | |
|
300 | 0 | InterimItemWindow::Layout(); |
301 | |
|
302 | 0 | m_bLayingOut = false; |
303 | 0 | } |
304 | | |
305 | | bool SfxInfoBarWindow::EventNotify(NotifyEvent& rEvent) |
306 | 0 | { |
307 | 0 | const NotifyEventType nType = rEvent.GetType(); |
308 | 0 | if (NotifyEventType::KEYINPUT == nType) |
309 | 0 | { |
310 | 0 | const vcl::KeyCode& rKeyCode = rEvent.GetKeyEvent()->GetKeyCode(); |
311 | 0 | switch (rKeyCode.GetCode()) |
312 | 0 | { |
313 | 0 | case KEY_TAB: |
314 | 0 | case KEY_SPACE: |
315 | 0 | case KEY_RETURN: |
316 | | // Allow Tab, Space, and Enter to pass through to parent for proper focus handling |
317 | 0 | break; |
318 | 0 | default: |
319 | | // Consume all other keys to prevent document window interaction |
320 | 0 | return true; |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | 0 | return InterimItemWindow::EventNotify(rEvent); |
325 | 0 | } |
326 | | |
327 | | weld::Button& SfxInfoBarWindow::addButton(const OUString* pCommand) |
328 | 0 | { |
329 | 0 | m_aActionBtns.emplace_back(std::make_unique<ExtraButton>(m_xButtonBox.get(), pCommand)); |
330 | |
|
331 | 0 | return m_aActionBtns.back()->get_widget(); |
332 | 0 | } |
333 | | |
334 | 0 | SfxInfoBarWindow::~SfxInfoBarWindow() { disposeOnce(); } |
335 | | |
336 | | void SfxInfoBarWindow::SetForeAndBackgroundColors(InfobarType eType) |
337 | 0 | { |
338 | 0 | basegfx::BColor aMessageColor; |
339 | 0 | GetInfoBarColors(eType, m_aBackgroundColor, m_aForegroundColor, aMessageColor); |
340 | |
|
341 | 0 | m_xPrimaryMessage->set_font_color(Color(aMessageColor)); |
342 | 0 | m_xSecondaryMessage->set_font_color(Color(aMessageColor)); |
343 | |
|
344 | 0 | Color aBackgroundColor(m_aBackgroundColor); |
345 | 0 | m_xPrimaryMessage->set_background(aBackgroundColor); |
346 | 0 | m_xSecondaryMessage->set_background(aBackgroundColor); |
347 | 0 | m_xContainer->set_background(aBackgroundColor); |
348 | 0 | if (m_xCloseBtn->get_visible()) |
349 | 0 | { |
350 | 0 | m_xCloseBtn->set_background(aBackgroundColor); |
351 | 0 | SetCloseButtonImage(); |
352 | 0 | } |
353 | 0 | } |
354 | | |
355 | | void SfxInfoBarWindow::dispose() |
356 | 0 | { |
357 | 0 | for (auto& rxBtn : m_aActionBtns) |
358 | 0 | rxBtn.reset(); |
359 | |
|
360 | 0 | m_xImage.reset(); |
361 | 0 | m_xPrimaryMessage.reset(); |
362 | 0 | m_xSecondaryMessage.reset(); |
363 | 0 | m_xButtonBox.reset(); |
364 | 0 | m_xCloseBtn.reset(); |
365 | 0 | m_aActionBtns.clear(); |
366 | 0 | InterimItemWindow::dispose(); |
367 | 0 | } |
368 | | |
369 | | void SfxInfoBarWindow::Update(const OUString& sPrimaryMessage, const OUString& sSecondaryMessage, |
370 | | InfobarType eType) |
371 | 0 | { |
372 | 0 | if (m_eType != eType) |
373 | 0 | { |
374 | 0 | m_eType = eType; |
375 | 0 | SetForeAndBackgroundColors(m_eType); |
376 | 0 | m_xImage->set_from_icon_name(GetInfoBarIconName(eType)); |
377 | 0 | } |
378 | |
|
379 | 0 | m_xPrimaryMessage->set_label(sPrimaryMessage); |
380 | 0 | m_xSecondaryMessage->set_text(sSecondaryMessage); |
381 | 0 | Resize(); |
382 | 0 | Invalidate(); |
383 | 0 | } |
384 | | |
385 | | IMPL_LINK_NOARG(SfxInfoBarWindow, CloseHandler, const OUString&, void) |
386 | 0 | { |
387 | 0 | static_cast<SfxInfoBarContainerWindow*>(GetParent())->removeInfoBar(this); |
388 | 0 | } |
389 | | |
390 | | SfxInfoBarContainerWindow::SfxInfoBarContainerWindow(SfxInfoBarContainerChild* pChildWin) |
391 | 0 | : Window(pChildWin->GetParent(), WB_DIALOGCONTROL) |
392 | 0 | , m_pChildWin(pChildWin) |
393 | 0 | , m_aLayoutIdle("SfxInfoBarContainerWindow m_aLayoutIdle") |
394 | 0 | , m_bResizing(false) |
395 | 0 | { |
396 | 0 | m_aLayoutIdle.SetPriority(TaskPriority::HIGHEST); |
397 | 0 | m_aLayoutIdle.SetInvokeHandler(LINK(this, SfxInfoBarContainerWindow, DoUpdateLayout)); |
398 | 0 | } Unexecuted instantiation: SfxInfoBarContainerWindow::SfxInfoBarContainerWindow(SfxInfoBarContainerChild*) Unexecuted instantiation: SfxInfoBarContainerWindow::SfxInfoBarContainerWindow(SfxInfoBarContainerChild*) |
399 | | |
400 | 0 | IMPL_LINK_NOARG(SfxInfoBarContainerWindow, DoUpdateLayout, Timer*, void) { m_pChildWin->Update(); } |
401 | | |
402 | 0 | SfxInfoBarContainerWindow::~SfxInfoBarContainerWindow() { disposeOnce(); } |
403 | | |
404 | | void SfxInfoBarContainerWindow::dispose() |
405 | 0 | { |
406 | 0 | for (auto& infoBar : m_pInfoBars) |
407 | 0 | infoBar.disposeAndClear(); |
408 | 0 | m_pInfoBars.clear(); |
409 | 0 | Window::dispose(); |
410 | 0 | } |
411 | | |
412 | | VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId, |
413 | | const OUString& sPrimaryMessage, |
414 | | const OUString& sSecondaryMessage, |
415 | | InfobarType ibType, |
416 | | bool bShowCloseButton) |
417 | 0 | { |
418 | 0 | if (!isInfobarEnabled(sId)) |
419 | 0 | return nullptr; |
420 | | |
421 | 0 | auto pInfoBar = VclPtr<SfxInfoBarWindow>::Create(this, sId, sPrimaryMessage, sSecondaryMessage, |
422 | 0 | ibType, bShowCloseButton); |
423 | |
|
424 | 0 | basegfx::BColor aBackgroundColor; |
425 | 0 | basegfx::BColor aForegroundColor; |
426 | 0 | basegfx::BColor aMessageColor; |
427 | 0 | GetInfoBarColors(ibType, aBackgroundColor, aForegroundColor, aMessageColor); |
428 | 0 | pInfoBar->m_aBackgroundColor = aBackgroundColor; |
429 | 0 | pInfoBar->m_aForegroundColor = aForegroundColor; |
430 | 0 | m_pInfoBars.push_back(pInfoBar); |
431 | |
|
432 | 0 | Resize(); |
433 | 0 | return pInfoBar; |
434 | 0 | } |
435 | | |
436 | | VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::getInfoBar(std::u16string_view sId) |
437 | 0 | { |
438 | 0 | for (auto const& infoBar : m_pInfoBars) |
439 | 0 | { |
440 | 0 | if (infoBar->getId() == sId) |
441 | 0 | return infoBar; |
442 | 0 | } |
443 | 0 | return nullptr; |
444 | 0 | } |
445 | | |
446 | | bool SfxInfoBarContainerWindow::hasInfoBarWithID(std::u16string_view sId) |
447 | 0 | { |
448 | 0 | return (getInfoBar(sId) != nullptr); |
449 | 0 | } |
450 | | |
451 | | void SfxInfoBarContainerWindow::removeInfoBar(VclPtr<SfxInfoBarWindow> const& pInfoBar) |
452 | 0 | { |
453 | | // Remove |
454 | 0 | auto it = std::find(m_pInfoBars.begin(), m_pInfoBars.end(), pInfoBar); |
455 | 0 | if (it != m_pInfoBars.end()) |
456 | 0 | { |
457 | 0 | it->disposeAndClear(); |
458 | 0 | m_pInfoBars.erase(it); |
459 | 0 | } |
460 | |
|
461 | 0 | m_pChildWin->Update(); |
462 | 0 | } |
463 | | |
464 | | bool SfxInfoBarContainerWindow::isInfobarEnabled(std::u16string_view sId) |
465 | 0 | { |
466 | 0 | if (sId == u"readonly") |
467 | 0 | return officecfg::Office::UI::Infobar::Enabled::Readonly::get(); |
468 | 0 | if (sId == u"signature") |
469 | 0 | return officecfg::Office::UI::Infobar::Enabled::Signature::get(); |
470 | 0 | if (sId == u"donate") |
471 | 0 | return officecfg::Office::UI::Infobar::Enabled::Donate::get(); |
472 | 0 | if (sId == u"getinvolved") |
473 | 0 | return officecfg::Office::UI::Infobar::Enabled::GetInvolved::get(); |
474 | 0 | if (sId == u"hyphenationmissing") |
475 | 0 | return officecfg::Office::UI::Infobar::Enabled::HyphenationMissing::get(); |
476 | 0 | if (sId == u"whatsnew") |
477 | 0 | return officecfg::Office::UI::Infobar::Enabled::WhatsNew::get(); |
478 | 0 | if (sId == u"hiddentrackchanges") |
479 | 0 | return officecfg::Office::UI::Infobar::Enabled::HiddenTrackChanges::get(); |
480 | 0 | if (sId == u"macro") |
481 | 0 | return officecfg::Office::UI::Infobar::Enabled::MacrosDisabled::get(); |
482 | 0 | if (sId == u"securitywarn") |
483 | 0 | { |
484 | 0 | return officecfg::Office::Common::Security::Scripting::WarnSaveOrSendDoc::get() |
485 | 0 | || officecfg::Office::Common::Security::Scripting::WarnSignDoc::get() |
486 | 0 | || officecfg::Office::Common::Security::Scripting::WarnPrintDoc::get() |
487 | 0 | || officecfg::Office::Common::Security::Scripting::WarnCreatePDF::get(); |
488 | 0 | } |
489 | 0 | if (sId == u"autocorr_leadtrail") |
490 | 0 | return officecfg::Office::UI::Infobar::Enabled::AutoCorrLeadTrail::get(); |
491 | 0 | if (sId == u"VCL_gen") |
492 | 0 | return officecfg::Office::UI::Infobar::Enabled::WarnGenericVCL::get(); |
493 | | |
494 | 0 | return true; |
495 | 0 | } |
496 | | |
497 | | // This triggers the SfxFrame to re-layout its childwindows |
498 | 0 | void SfxInfoBarContainerWindow::TriggerUpdateLayout() { m_aLayoutIdle.Start(); } |
499 | | |
500 | | void SfxInfoBarContainerWindow::Resize() |
501 | 0 | { |
502 | 0 | if (m_bResizing) |
503 | 0 | return; |
504 | 0 | m_bResizing = true; |
505 | 0 | const Size aWindowOrigSize = GetSizePixel(); |
506 | 0 | auto nOrigWidth = aWindowOrigSize.getWidth(); |
507 | 0 | auto nOrigHeight = aWindowOrigSize.getHeight(); |
508 | |
|
509 | 0 | tools::Long nHeight = 0; |
510 | |
|
511 | 0 | for (auto& rxInfoBar : m_pInfoBars) |
512 | 0 | { |
513 | 0 | Size aOrigSize = rxInfoBar->GetSizePixel(); |
514 | 0 | Size aSize(nOrigWidth, aOrigSize.Height()); |
515 | |
|
516 | 0 | Point aPos(0, nHeight); |
517 | | // stage 1: provisionally size the infobar, |
518 | 0 | rxInfoBar->SetPosSizePixel(aPos, aSize); |
519 | | |
520 | | // stage 2: perhaps allow height to stretch to fit |
521 | | // the stage 1 width |
522 | 0 | aSize = rxInfoBar->DoLayout(); |
523 | 0 | rxInfoBar->SetPosSizePixel(aPos, aSize); |
524 | 0 | rxInfoBar->Show(); |
525 | | |
526 | | // Stretch to fit the infobar(s) |
527 | 0 | nHeight += aSize.getHeight(); |
528 | 0 | } |
529 | |
|
530 | 0 | if (nOrigHeight != nHeight) |
531 | 0 | { |
532 | 0 | SetSizePixel(Size(nOrigWidth, nHeight)); |
533 | 0 | TriggerUpdateLayout(); |
534 | 0 | } |
535 | |
|
536 | 0 | m_bResizing = false; |
537 | 0 | } |
538 | | |
539 | | SFX_IMPL_POS_CHILDWINDOW_WITHID(SfxInfoBarContainerChild, SID_INFOBAR, SFX_OBJECTBAR_OBJECT); |
540 | | |
541 | | SfxInfoBarContainerChild::SfxInfoBarContainerChild(vcl::Window* _pParent, sal_uInt16 nId, |
542 | | SfxBindings* pBindings, SfxChildWinInfo*) |
543 | 0 | : SfxChildWindow(_pParent, nId) |
544 | 0 | , m_pBindings(pBindings) |
545 | 0 | { |
546 | 0 | SetWindow(VclPtr<SfxInfoBarContainerWindow>::Create(this)); |
547 | 0 | GetWindow()->SetPosSizePixel(Point(0, 0), Size(_pParent->GetSizePixel().getWidth(), 0)); |
548 | 0 | GetWindow()->Show(); |
549 | |
|
550 | 0 | SetAlignment(SfxChildAlignment::LOWESTTOP); |
551 | 0 | } |
552 | | |
553 | 0 | SfxInfoBarContainerChild::~SfxInfoBarContainerChild() {} |
554 | | |
555 | | SfxChildWinInfo SfxInfoBarContainerChild::GetInfo() const |
556 | 0 | { |
557 | 0 | SfxChildWinInfo aInfo = SfxChildWindow::GetInfo(); |
558 | 0 | return aInfo; |
559 | 0 | } |
560 | | |
561 | | void SfxInfoBarContainerChild::Update() |
562 | 0 | { |
563 | | // Layout to current width, this may change the height |
564 | 0 | if (vcl::Window* pChild = GetWindow()) |
565 | 0 | { |
566 | 0 | Size aSize(pChild->GetSizePixel()); |
567 | 0 | pChild->Resize(); |
568 | 0 | if (aSize == pChild->GetSizePixel()) |
569 | 0 | return; |
570 | 0 | } |
571 | | |
572 | | // Refresh the frame to take the infobars container height change into account |
573 | 0 | const sal_uInt16 nId = GetChildWindowId(); |
574 | 0 | SfxViewFrame* pVFrame = m_pBindings->GetDispatcher()->GetFrame(); |
575 | 0 | pVFrame->ShowChildWindow(nId); |
576 | | |
577 | | // Give the focus to the document view |
578 | 0 | pVFrame->GetWindow().GrabFocusToDocument(); |
579 | 0 | } |
580 | | |
581 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |