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