LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / sfx2 / source / dialog / infobar.cxx
blobeade717ea47417710757e8edc6dd01b867930505
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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/processorfromoutputdevice.hxx>
16 #include <memory>
17 #include <officecfg/Office/UI/Infobar.hxx>
18 #include <sfx2/bindings.hxx>
19 #include <sfx2/dispatch.hxx>
20 #include <sfx2/infobar.hxx>
21 #include <sfx2/objface.hxx>
22 #include <sfx2/sfxsids.hrc>
23 #include <sfx2/viewfrm.hxx>
24 #include <vcl/image.hxx>
25 #include <vcl/settings.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/virdev.hxx>
28 #include <vcl/weldutils.hxx>
29 #include <bitmaps.hlst>
31 using namespace drawinglayer::geometry;
32 using namespace drawinglayer::processor2d;
33 using namespace drawinglayer::primitive2d;
34 using namespace drawinglayer::attribute;
35 using namespace basegfx;
36 using namespace css::frame;
38 namespace
40 void GetInfoBarColors(InfobarType ibType, BColor& rBackgroundColor, BColor& rForegroundColor,
41 BColor& rMessageColor)
43 rMessageColor = basegfx::BColor(0.0, 0.0, 0.0);
45 switch (ibType)
47 case InfobarType::INFO: // blue; #004785/0,71,133; #BDE5F8/189,229,248
48 rBackgroundColor = basegfx::BColor(0.741, 0.898, 0.973);
49 rForegroundColor = basegfx::BColor(0.0, 0.278, 0.522);
50 break;
51 case InfobarType::SUCCESS: // green; #32550C/50,85,12; #DFF2BF/223,242,191
52 rBackgroundColor = basegfx::BColor(0.874, 0.949, 0.749);
53 rForegroundColor = basegfx::BColor(0.196, 0.333, 0.047);
54 break;
55 case InfobarType::WARNING: // orange; #704300/112,67,0; #FEEFB3/254,239,179
56 rBackgroundColor = basegfx::BColor(0.996, 0.937, 0.702);
57 rForegroundColor = basegfx::BColor(0.439, 0.263, 0.0);
58 break;
59 case InfobarType::DANGER: // red; #7A0006/122,0,6; #FFBABA/255,186,186
60 rBackgroundColor = basegfx::BColor(1.0, 0.729, 0.729);
61 rForegroundColor = basegfx::BColor(0.478, 0.0, 0.024);
62 break;
65 //remove this?
66 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
67 if (rSettings.GetHighContrastMode())
69 rBackgroundColor = rSettings.GetLightColor().getBColor();
70 rForegroundColor = rSettings.GetDialogTextColor().getBColor();
73 OUString GetInfoBarIconName(InfobarType ibType)
75 OUString aRet;
77 switch (ibType)
79 case InfobarType::INFO:
80 aRet = "vcl/res/infobox.png";
81 break;
82 case InfobarType::SUCCESS:
83 aRet = "vcl/res/successbox.png";
84 break;
85 case InfobarType::WARNING:
86 aRet = "vcl/res/warningbox.png";
87 break;
88 case InfobarType::DANGER:
89 aRet = "vcl/res/errorbox.png";
90 break;
93 return aRet;
96 } // anonymous namespace
98 void SfxInfoBarWindow::SetCloseButtonImage()
100 Size aSize = Image(StockImage::Yes, CLOSEDOC).GetSizePixel();
101 aSize = Size(aSize.Width() * 1.5, aSize.Height() * 1.5);
103 ScopedVclPtr<VirtualDevice> xDevice(m_xCloseBtn->create_virtual_device());
104 xDevice->SetOutputSizePixel(aSize);
106 Point aBtnPos(0, 0);
108 const ViewInformation2D aNewViewInfos;
109 const std::unique_ptr<BaseProcessor2D> pProcessor(
110 createBaseProcessor2DFromOutputDevice(*xDevice, aNewViewInfos));
112 const ::tools::Rectangle aRect(aBtnPos, xDevice->PixelToLogic(aSize));
114 drawinglayer::primitive2d::Primitive2DContainer aSeq(2);
116 // background
117 B2DPolygon aPolygon;
118 aPolygon.append(B2DPoint(aRect.Left(), aRect.Top()));
119 aPolygon.append(B2DPoint(aRect.Right(), aRect.Top()));
120 aPolygon.append(B2DPoint(aRect.Right(), aRect.Bottom()));
121 aPolygon.append(B2DPoint(aRect.Left(), aRect.Bottom()));
122 aPolygon.setClosed(true);
124 aSeq[0] = new PolyPolygonColorPrimitive2D(B2DPolyPolygon(aPolygon), m_aBackgroundColor);
126 LineAttribute aLineAttribute(m_aForegroundColor, 2.0);
128 // Cross
129 B2DPolyPolygon aCross;
131 B2DPolygon aLine1;
132 aLine1.append(B2DPoint(aRect.Left(), aRect.Top()));
133 aLine1.append(B2DPoint(aRect.Right(), aRect.Bottom()));
134 aCross.append(aLine1);
136 B2DPolygon aLine2;
137 aLine2.append(B2DPoint(aRect.Right(), aRect.Top()));
138 aLine2.append(B2DPoint(aRect.Left(), aRect.Bottom()));
139 aCross.append(aLine2);
141 aSeq[1] = new PolyPolygonStrokePrimitive2D(aCross, aLineAttribute, StrokeAttribute());
143 pProcessor->process(aSeq);
145 m_xCloseBtn->set_item_image("close", xDevice);
148 class ExtraButton
150 private:
151 std::unique_ptr<weld::Builder> m_xBuilder;
152 std::unique_ptr<weld::Container> m_xContainer;
153 std::unique_ptr<weld::Button> m_xButton;
154 /** StatusListener. Updates the button as the slot state changes */
155 rtl::Reference<weld::WidgetStatusListener> m_xStatusListener;
156 OUString m_aCommand;
158 DECL_LINK(CommandHdl, weld::Button&, void);
160 public:
161 ExtraButton(weld::Container* pContainer, const OUString* pCommand)
162 : m_xBuilder(Application::CreateBuilder(pContainer, "sfx/ui/extrabutton.ui"))
163 , m_xContainer(m_xBuilder->weld_container("ExtraButton"))
164 , m_xButton(m_xBuilder->weld_button("button"))
166 if (pCommand)
168 m_aCommand = *pCommand;
169 m_xButton->connect_clicked(LINK(this, ExtraButton, CommandHdl));
170 m_xStatusListener.set(new weld::WidgetStatusListener(m_xButton.get(), m_aCommand));
171 m_xStatusListener->startListening();
175 ~ExtraButton()
177 if (m_xStatusListener.is())
178 m_xStatusListener->dispose();
181 weld::Button& get_widget() { return *m_xButton; }
184 IMPL_LINK_NOARG(ExtraButton, CommandHdl, weld::Button&, void)
186 comphelper::dispatchCommand(m_aCommand, css::uno::Sequence<css::beans::PropertyValue>());
189 SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window* pParent, const OUString& sId,
190 const OUString& sPrimaryMessage,
191 const OUString& sSecondaryMessage, InfobarType ibType,
192 bool bShowCloseButton)
193 : InterimItemWindow(pParent, "sfx/ui/infobar.ui", "InfoBar")
194 , m_sId(sId)
195 , m_eType(ibType)
196 , m_bLayingOut(false)
197 , m_xImage(m_xBuilder->weld_image("image"))
198 , m_xPrimaryMessage(m_xBuilder->weld_label("primary"))
199 , m_xSecondaryMessage(m_xBuilder->weld_text_view("secondary"))
200 , m_xButtonBox(m_xBuilder->weld_container("buttonbox"))
201 , m_xCloseBtn(m_xBuilder->weld_toolbar("closebar"))
203 SetStyle(GetStyle() | WB_DIALOGCONTROL);
205 InitControlBase(m_xCloseBtn.get());
207 m_xImage->set_from_icon_name(GetInfoBarIconName(ibType));
208 m_xSecondaryMessage->set_margin_top(m_xImage->get_preferred_size().Height() / 4);
210 if (!sPrimaryMessage.isEmpty())
212 m_xPrimaryMessage->set_label(sPrimaryMessage);
213 m_xPrimaryMessage->show();
216 m_xSecondaryMessage->set_text(sSecondaryMessage);
217 m_aOrigMessageSize = m_xSecondaryMessage->get_preferred_size();
218 m_aMessageSize = m_aOrigMessageSize;
219 m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl));
221 if (bShowCloseButton)
223 m_xCloseBtn->connect_clicked(LINK(this, SfxInfoBarWindow, CloseHandler));
224 m_xCloseBtn->show();
227 EnableChildTransparentMode();
229 SetForeAndBackgroundColors(m_eType);
231 auto nWidth = pParent->GetSizePixel().getWidth();
232 auto nHeight = get_preferred_size().Height();
233 SetSizePixel(Size(nWidth, nHeight + 2));
235 Resize();
238 IMPL_LINK(SfxInfoBarWindow, SizeAllocHdl, const Size&, rSize, void)
240 if (m_aMessageSize != rSize)
242 m_aMessageSize = rSize;
243 static_cast<SfxInfoBarContainerWindow*>(GetParent())->TriggerUpdateLayout();
247 Size SfxInfoBarWindow::DoLayout()
249 Size aGivenSize(GetSizePixel());
251 // disconnect SizeAllocHdl because we don't care about the size change
252 // during layout
253 m_xSecondaryMessage->connect_size_allocate(Link<const Size&, void>());
255 // blow away size cache in case m_aMessageSize.Width() is already the width request
256 // and we would get the cached preferred size instead of the recalc we want to force
257 m_xSecondaryMessage->set_size_request(-1, -1);
258 // make the width we were detected as set to by SizeAllocHdl as our desired width
259 m_xSecondaryMessage->set_size_request(m_aMessageSize.Width(), -1);
260 // get our preferred size with that message width
261 Size aSizeForWidth(aGivenSize.Width(), m_xContainer->get_preferred_size().Height());
262 // restore the message preferred size so we can freely resize, and get a new
263 // m_aMessageSize and repeat the process if we do
264 m_xSecondaryMessage->set_size_request(m_aOrigMessageSize.Width(), -1);
266 // connect SizeAllocHdl so changes outside of this layout will trigger a new layout
267 m_xSecondaryMessage->connect_size_allocate(LINK(this, SfxInfoBarWindow, SizeAllocHdl));
269 return aSizeForWidth;
272 void SfxInfoBarWindow::Layout()
274 if (m_bLayingOut)
275 return;
276 m_bLayingOut = true;
278 InterimItemWindow::Layout();
280 m_bLayingOut = false;
283 weld::Button& SfxInfoBarWindow::addButton(const OUString* pCommand)
285 m_aActionBtns.emplace_back(std::make_unique<ExtraButton>(m_xButtonBox.get(), pCommand));
287 return m_aActionBtns.back()->get_widget();
290 SfxInfoBarWindow::~SfxInfoBarWindow() { disposeOnce(); }
292 void SfxInfoBarWindow::SetForeAndBackgroundColors(InfobarType eType)
294 basegfx::BColor aMessageColor;
295 GetInfoBarColors(eType, m_aBackgroundColor, m_aForegroundColor, aMessageColor);
297 m_xPrimaryMessage->set_font_color(Color(aMessageColor));
298 m_xSecondaryMessage->set_font_color(Color(aMessageColor));
300 Color aBackgroundColor(m_aBackgroundColor);
301 m_xPrimaryMessage->set_background(aBackgroundColor);
302 m_xSecondaryMessage->set_background(aBackgroundColor);
303 m_xContainer->set_background(aBackgroundColor);
304 if (m_xCloseBtn->get_visible())
306 m_xCloseBtn->set_background(aBackgroundColor);
307 SetCloseButtonImage();
311 void SfxInfoBarWindow::dispose()
313 for (auto& rxBtn : m_aActionBtns)
314 rxBtn.reset();
316 m_xImage.reset();
317 m_xPrimaryMessage.reset();
318 m_xSecondaryMessage.reset();
319 m_xButtonBox.reset();
320 m_xCloseBtn.reset();
321 m_aActionBtns.clear();
322 InterimItemWindow::dispose();
325 void SfxInfoBarWindow::Update(const OUString& sPrimaryMessage, const OUString& sSecondaryMessage,
326 InfobarType eType)
328 if (m_eType != eType)
330 m_eType = eType;
331 SetForeAndBackgroundColors(m_eType);
332 m_xImage->set_from_icon_name(GetInfoBarIconName(eType));
335 m_xPrimaryMessage->set_label(sPrimaryMessage);
336 m_xSecondaryMessage->set_text(sSecondaryMessage);
337 Resize();
338 Invalidate();
341 IMPL_LINK_NOARG(SfxInfoBarWindow, CloseHandler, const OString&, void)
343 static_cast<SfxInfoBarContainerWindow*>(GetParent())->removeInfoBar(this);
346 SfxInfoBarContainerWindow::SfxInfoBarContainerWindow(SfxInfoBarContainerChild* pChildWin)
347 : Window(pChildWin->GetParent(), WB_DIALOGCONTROL)
348 , m_pChildWin(pChildWin)
349 , m_aLayoutIdle("SfxInfoBarContainerWindow m_aLayoutIdle")
350 , m_bResizing(false)
352 m_aLayoutIdle.SetPriority(TaskPriority::HIGHEST);
353 m_aLayoutIdle.SetInvokeHandler(LINK(this, SfxInfoBarContainerWindow, DoUpdateLayout));
356 IMPL_LINK_NOARG(SfxInfoBarContainerWindow, DoUpdateLayout, Timer*, void) { m_pChildWin->Update(); }
358 SfxInfoBarContainerWindow::~SfxInfoBarContainerWindow() { disposeOnce(); }
360 void SfxInfoBarContainerWindow::dispose()
362 for (auto& infoBar : m_pInfoBars)
363 infoBar.disposeAndClear();
364 m_pInfoBars.clear();
365 Window::dispose();
368 VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId,
369 const OUString& sPrimaryMessage,
370 const OUString& sSecondaryMessage,
371 InfobarType ibType,
372 bool bShowCloseButton)
374 if (!isInfobarEnabled(sId))
375 return nullptr;
377 auto pInfoBar = VclPtr<SfxInfoBarWindow>::Create(this, sId, sPrimaryMessage, sSecondaryMessage,
378 ibType, bShowCloseButton);
380 basegfx::BColor aBackgroundColor;
381 basegfx::BColor aForegroundColor;
382 basegfx::BColor aMessageColor;
383 GetInfoBarColors(ibType, aBackgroundColor, aForegroundColor, aMessageColor);
384 pInfoBar->m_aBackgroundColor = aBackgroundColor;
385 pInfoBar->m_aForegroundColor = aForegroundColor;
386 m_pInfoBars.push_back(pInfoBar);
388 Resize();
389 return pInfoBar;
392 VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::getInfoBar(std::u16string_view sId)
394 for (auto const& infoBar : m_pInfoBars)
396 if (infoBar->getId() == sId)
397 return infoBar;
399 return nullptr;
402 bool SfxInfoBarContainerWindow::hasInfoBarWithID(std::u16string_view sId)
404 return (getInfoBar(sId) != nullptr);
407 void SfxInfoBarContainerWindow::removeInfoBar(VclPtr<SfxInfoBarWindow> const& pInfoBar)
409 // Remove
410 auto it = std::find(m_pInfoBars.begin(), m_pInfoBars.end(), pInfoBar);
411 if (it != m_pInfoBars.end())
413 it->disposeAndClear();
414 m_pInfoBars.erase(it);
417 m_pChildWin->Update();
420 bool SfxInfoBarContainerWindow::isInfobarEnabled(std::u16string_view sId)
422 if (sId == u"readonly")
423 return officecfg::Office::UI::Infobar::Enabled::Readonly::get();
424 if (sId == u"signature")
425 return officecfg::Office::UI::Infobar::Enabled::Signature::get();
426 if (sId == u"donate")
427 return officecfg::Office::UI::Infobar::Enabled::Donate::get();
428 if (sId == u"getinvolved")
429 return officecfg::Office::UI::Infobar::Enabled::GetInvolved::get();
430 if (sId == u"hyphenationmissing")
431 return officecfg::Office::UI::Infobar::Enabled::HyphenationMissing::get();
432 if (sId == u"whatsnew")
433 return officecfg::Office::UI::Infobar::Enabled::WhatsNew::get();
434 if (sId == u"hiddentrackchanges")
435 return officecfg::Office::UI::Infobar::Enabled::HiddenTrackChanges::get();
437 return true;
440 // This triggers the SfxFrame to re-layout its childwindows
441 void SfxInfoBarContainerWindow::TriggerUpdateLayout() { m_aLayoutIdle.Start(); }
443 void SfxInfoBarContainerWindow::Resize()
445 if (m_bResizing)
446 return;
447 m_bResizing = true;
448 const Size& rOrigSize = GetSizePixel();
449 auto nOrigWidth = rOrigSize.getWidth();
450 auto nOrigHeight = rOrigSize.getHeight();
452 tools::Long nHeight = 0;
454 for (auto& rxInfoBar : m_pInfoBars)
456 Size aOrigSize = rxInfoBar->GetSizePixel();
457 Size aSize(nOrigWidth, aOrigSize.Height());
459 Point aPos(0, nHeight);
460 // stage 1: provisionally size the infobar,
461 rxInfoBar->SetPosSizePixel(aPos, aSize);
463 // stage 2: perhaps allow height to stretch to fit
464 // the stage 1 width
465 aSize = rxInfoBar->DoLayout();
466 rxInfoBar->SetPosSizePixel(aPos, aSize);
467 rxInfoBar->Show();
469 // Stretch to fit the infobar(s)
470 nHeight += aSize.getHeight();
473 if (nOrigHeight != nHeight)
475 SetSizePixel(Size(nOrigWidth, nHeight));
476 TriggerUpdateLayout();
479 m_bResizing = false;
482 SFX_IMPL_POS_CHILDWINDOW_WITHID(SfxInfoBarContainerChild, SID_INFOBAR, SFX_OBJECTBAR_OBJECT);
484 SfxInfoBarContainerChild::SfxInfoBarContainerChild(vcl::Window* _pParent, sal_uInt16 nId,
485 SfxBindings* pBindings, SfxChildWinInfo*)
486 : SfxChildWindow(_pParent, nId)
487 , m_pBindings(pBindings)
489 SetWindow(VclPtr<SfxInfoBarContainerWindow>::Create(this));
490 GetWindow()->SetPosSizePixel(Point(0, 0), Size(_pParent->GetSizePixel().getWidth(), 0));
491 GetWindow()->Show();
493 SetAlignment(SfxChildAlignment::LOWESTTOP);
496 SfxInfoBarContainerChild::~SfxInfoBarContainerChild() {}
498 SfxChildWinInfo SfxInfoBarContainerChild::GetInfo() const
500 SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
501 return aInfo;
504 void SfxInfoBarContainerChild::Update()
506 // Layout to current width, this may change the height
507 if (vcl::Window* pChild = GetWindow())
509 Size aSize(pChild->GetSizePixel());
510 pChild->Resize();
511 if (aSize == pChild->GetSizePixel())
512 return;
515 // Refresh the frame to take the infobars container height change into account
516 const sal_uInt16 nId = GetChildWindowId();
517 SfxViewFrame* pVFrame = m_pBindings->GetDispatcher()->GetFrame();
518 pVFrame->ShowChildWindow(nId);
520 // Give the focus to the document view
521 pVFrame->GetWindow().GrabFocusToDocument();
524 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */